Skip to content

escape analysis for automatic stack allocation of local objects#109

Merged
cs01 merged 3 commits intomainfrom
escape-analysis
Mar 7, 2026
Merged

escape analysis for automatic stack allocation of local objects#109
cs01 merged 3 commits intomainfrom
escape-analysis

Conversation

@cs01
Copy link
Owner

@cs01 cs01 commented Mar 7, 2026

feat: escape analysis for automatic stack allocation of local objects

Adds a conservative escape analysis semantic pass that automatically stack-allocates const object literals that don't escape their function scope. Zero GC pressure, zero syntax changes required — fully automatic.

What it does

Any const obj = { ... } declared inside a function that doesn't escape (not returned, not passed to external calls, not stored in outer scope or class fields) gets emitted as alloca instead of GC_malloc. Stack allocation is:

  • ~1 instruction (stack pointer adjustment)
  • Auto-freed on function return
  • Zero GC scan pressure

Conservative by design: uncertain → heap (safe). False "escape" wastes nothing. False "no-escape" would be a dangling pointer — never happens.

A value escapes if it is

  • Returned from the function
  • Assigned to a variable in outer scope
  • Assigned to a class field or object property
  • Passed as an argument to any function call
  • Stored inside another heap-allocated value
  • Captured by an arrow function closure

Implementation

src/semantic/escape-analysis.ts (new)

analyzeEscapes(ast) runs before codegen. For each function and class method body:

  1. collectCandidates — finds const x = { ... } declarations (object literal RHS only)
  2. scanBlockForEscapes — walks all statements/expressions marking which candidates escape
  3. Returns string[] of "name:line:col" keys for stack-eligible declarations

Full AST coverage: if/while/for/for-of/try-catch/switch all traversed. Arrow function bodies treated conservatively (any reference inside an arrow marks the var escaped).

All type assertions use real AST types from src/ast/types.ts — no invented partial interfaces that would produce wrong GEP indices in the native compiler.

src/codegen/types/objects/object.ts

New allocateStruct(structType, sizeBytes) helper used by all three object generators (generateInlineObject, generateInterfaceObject, generateInlineInterfaceObject). Checks ctx.isStackEligibleKey(ctx.getCurrentVarDeclKey()) — if eligible, emits alloca with auto-hoisting to the entry block; otherwise falls back to GC_malloc as before.

src/codegen/infrastructure/variable-allocator.ts

Sets ctx.setCurrentVarDeclKey("name:line:col") before generating each variable declaration's RHS, clears it after. This lets allocateStruct know which declaration is currently being generated.

src/codegen/llvm-generator.ts / generator-context.ts

  • analyzeEscapes(ast) called in generateParts(), result stored as stackEligibleVars: string[]
  • Four new methods on IGeneratorContext: setStackEligibleVars, isStackEligibleKey, setCurrentVarDeclKey, getCurrentVarDeclKey

Correctness

  • 423/423 unit tests pass
  • Full 3-stage self-hosting verified (npm run verify)
  • Escape analysis finds 0 stack-eligible vars in the compiler itself (all const obj = {} in the compiler source pass as args or are returned — correctly identified as escaped)
  • Fixed rebase artifact: isNumber global variable handling was dropped from llvm-generator.ts during rebase onto main; restored

Startup benchmark (hello world)

Binary Per-invocation
ChadScript ~1.87ms
C (clang -O2) ~1.34ms

Gap is Boehm GC init cost (one-time per process). For long-running servers the escape analysis impact is on per-request allocation, not startup.

cs01 added 3 commits March 6, 2026 16:01
- new src/semantic/escape-analysis.ts: conservative escape analysis that identifies const object literal variables that don't escape their function scope
- object.ts: allocateStruct() checks isStackEligibleKey to emit alloca instead of GC_malloc
- variable-allocator.ts: sets currentVarDeclKey before generating rhs of variable declarations
- llvm-generator.ts: calls analyzeEscapes(ast) in generateParts(), stores stack-eligible var keys
- generator-context.ts: adds isStackEligibleKey/getCurrentVarDeclKey/setCurrentVarDeclKey to interface
- fixed: all statement/expression type assertions use real ast types to avoid wrong gep indices in native compiler
@cs01 cs01 changed the title Escape analysis escape analysis for automatic stack allocation of local objects Mar 7, 2026
@cs01 cs01 merged commit 6de3904 into main Mar 7, 2026
12 checks passed
@cs01 cs01 deleted the escape-analysis branch March 7, 2026 07:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant