Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/8595-entry-outline-default.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
perf(codegen): finish structured module-entry outlining (#8595). Entry bodies with at least 1,000 top-level HIR statements or 4,000 estimated safepoints now split automatically into ordered functions capped at roughly 200 statements or 1,000 safepoints (`PERRY_OUTLINE_ENTRY=0` disables and `=1` forces the transform). Original declarations move unchanged, bindings that need cross-function storage are promoted to rooted module globals, and declaration/export/const/static-field/early-`process.env` scans reconstruct the logical source-order entry stream. Exports, Script `globalThis` reflection, and structured control flow are supported; top-level await and module-level TDZ preallocation remain fail-safe exclusions. This bounds per-function RS4GC fan-out, instruction selection, optimization, and register allocation without changing the requested optimization level.
2 changes: 1 addition & 1 deletion crates/perry-codegen/src/codegen/artifacts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1831,7 +1831,7 @@ pub(super) fn emit_module_artifacts(c: ModuleArtifactsCtx<'_>) -> Result<()> {
// name (`const bar = function namedBar(){}` ⇒ `"namedBar"`).
let mut named_inline_closure_ids: std::collections::HashSet<perry_hir::types::FuncId> =
std::collections::HashSet::new();
for stmt in &hir.init {
for stmt in super::entry_outline::logical_entry_stmts(hir) {
if let perry_hir::Stmt::Let { name, init, .. } = stmt {
if name.is_empty() || name.starts_with('_') {
continue;
Expand Down
8 changes: 5 additions & 3 deletions crates/perry-codegen/src/codegen/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ fn emit_plugin_abi_shim(llmod: &mut LlModule, hir: &HirModule, module_prefix: &s
/// (function(){ ... })()`), which is where the wrapped entry's top-level
/// statements live. Assignments nested in conditionals or inner functions are
/// deliberately skipped — those run conditionally/lazily, exactly as in Node.
fn collect_entry_env_literals(init: &[perry_hir::Stmt]) -> Vec<(String, String)> {
fn collect_entry_env_literals(hir: &HirModule) -> Vec<(String, String)> {
use perry_hir::{Expr, Stmt};

fn record(expr: &Expr, out: &mut Vec<(String, String)>) {
Expand Down Expand Up @@ -176,7 +176,9 @@ fn collect_entry_env_literals(init: &[perry_hir::Stmt]) -> Vec<(String, String)>
}

let mut out = Vec::new();
scan(init, &mut out, 0);
for stmt in super::entry_outline::logical_entry_stmts(hir) {
scan(std::slice::from_ref(stmt), &mut out, 0);
}
out
}

Expand Down Expand Up @@ -620,7 +622,7 @@ pub(super) fn compile_module_entry(
// `collect_entry_env_literals`. The "NODE_ENV"/"production" string
// handles are interned here and populated by the strings-init call
// above (the entry body also references them, so they share slots).
for (name, value) in collect_entry_env_literals(&hir.init) {
for (name, value) in collect_entry_env_literals(hir) {
let name_idx = strings.intern(&name);
let value_idx = strings.intern(&value);
let name_global = format!("@{}", strings.entry(name_idx).handle_global);
Expand Down
Loading
Loading