From 8d90d2292bbdb23596849c3c3d1cda2f6cad783e Mon Sep 17 00:00:00 2001 From: Cliff Click Date: Mon, 10 Aug 2026 11:11:18 -0700 Subject: Chapter 27: Escape Analysis and Unknown Callers Co-authored-by: Hsiang-Ying Fu <66062782+fuhsnn@users.noreply.github.com> Co-authored-by: fuhsnn <66062782+fuhsnn@users.noreply.github.com> Co-authored-by: Symious <14933944+symious@users.noreply.github.com> Co-authored-by: Jun Kawasaki Co-authored-by: Claude Opus 5 --- .../com/seaofnodes/simple/node/CallEndNode.java | 24 +++++++++++++++- .../java/com/seaofnodes/simple/node/CallNode.java | 23 ++++++++++++++-- .../com/seaofnodes/simple/node/EscapeNode.java | 8 ++++-- .../com/seaofnodes/simple/node/MemMergeNode.java | 12 +++++--- .../com/seaofnodes/simple/node/MemPhiNode.java | 7 +++-- .../java/com/seaofnodes/simple/node/ParmNode.java | 12 ++++++-- .../com/seaofnodes/simple/node/ReturnNode.java | 2 +- .../java/com/seaofnodes/simple/node/StartNode.java | 32 ++++++++++++++++++---- .../java/com/seaofnodes/simple/node/StopNode.java | 26 ++++++++++++++++-- .../java/com/seaofnodes/simple/node/StoreNode.java | 5 ++-- 10 files changed, 125 insertions(+), 26 deletions(-) (limited to 'src/main/java/com/seaofnodes/simple/node') diff --git a/src/main/java/com/seaofnodes/simple/node/CallEndNode.java b/src/main/java/com/seaofnodes/simple/node/CallEndNode.java index 604bb9da..86c6c79d 100644 --- a/src/main/java/com/seaofnodes/simple/node/CallEndNode.java +++ b/src/main/java/com/seaofnodes/simple/node/CallEndNode.java @@ -69,7 +69,29 @@ public class CallEndNode extends CFGNode implements MultiNode { ret = ti.same_but_slightly_wider_than(TypeInteger.BOT).join(tfp.ret()); } } - return TypeTuple.make(call._type,TypeMem.BOT,ret); + TypeMem mem=TypeMem.TOP; + for( int i=1; i=0; fidx=XInt.next(tfp.fidxs(),fidx) ) { + if( CodeGen.CODE.externFunc(tfp.makeFrom(fidx))!=null ) external=true; + else locals++; + } + known=locals==nIns()-1; + } + if( external || (ftype instanceof TypeFunPtr tfp && XInt.isHigh(tfp.fidxs())) ) { + // Unknown code may retain arguments and all addresses already + // exposed to it. Heap values are arbitrary after this call; + // exposure itself still has a useful finite summary. + mem=(TypeMem)mem.meet(call.exposed(this)); + } else if( !known && (CodeGen.CODE._phase.ordinal()CodeGen.Phase.FinalizeIR.ordinal()) ) mem=TypeMem.BOT; + return TypeTuple.make(call._type,mem,ret); } @Override diff --git a/src/main/java/com/seaofnodes/simple/node/CallNode.java b/src/main/java/com/seaofnodes/simple/node/CallNode.java index 21f438da..44ad8e83 100644 --- a/src/main/java/com/seaofnodes/simple/node/CallNode.java +++ b/src/main/java/com/seaofnodes/simple/node/CallNode.java @@ -8,6 +8,7 @@ import com.seaofnodes.simple.codegen.CodeGen; import com.seaofnodes.simple.type.Type; import com.seaofnodes.simple.type.XInt; import com.seaofnodes.simple.type.TypeFunPtr; +import com.seaofnodes.simple.type.TypeMem; import com.seaofnodes.simple.util.Utils; /** @@ -21,7 +22,7 @@ public class CallNode extends CFGNode { // us whether this is a method or an ordinary function stored in a field. public String _method; - public CallNode(Parser.Lexer loc, Node... nodes) { super(nodes); _loc = loc; } + public CallNode(Parser.Lexer loc, Node... nodes) { super(nodes); _loc = loc; CodeGen.CODE._calls.add(this); } public CallNode(CallNode call) { super(call); _loc = call._loc; _method=call._method; } @Override public String label() { return "Call"; } @@ -58,7 +59,25 @@ public class CallNode extends CFGNode { public TypeFunPtr tfp() { return (TypeFunPtr)fptr()._type; } // Call is to an externally supplied code - public boolean external() { return false; } + public boolean external() { + if( !(fptr()._type instanceof TypeFunPtr tfp) ) return false; + if( XInt.isHigh(tfp.fidxs()) ) return true; + for( int fidx=XInt.next(tfp.fidxs(),-1); fidx>=0; fidx=XInt.next(tfp.fidxs(),fidx) ) + if( CodeGen.CODE.externFunc(tfp.makeFrom(fidx))!=null ) return true; + return false; + } + + // Unknown code can retain arguments without ever returning. Stop uses + // this summary directly as well as through the call's memory result. + TypeMem exposed(Node dep) { + dep.addDepForwards(this); + dep.addDepForwards(fptr()); + if( _type!=Type.CONTROL || !external() ) return TypeMem.START; + Type input=dep.addDepForwards(mem())._type; + TypeMem escaped=input instanceof TypeMem m ? m : input.isHigh() ? TypeMem.TOP : TypeMem.BOT; + for( int i=2; i=2 ) + mem=mem.escapes(addDepForwards(p)._type); + if( !(ret._type instanceof TypeTuple tt) || tt._types[0].isHigh() ) continue; + mem=mem.escapes(tt.ret()).escapes(tt._types[1]); + } + for( CallNode call : code._calls ) + if( !call.isDead() ) mem=mem.escapes(call.exposed(this)); + return mem; } @Override diff --git a/src/main/java/com/seaofnodes/simple/node/StoreNode.java b/src/main/java/com/seaofnodes/simple/node/StoreNode.java index caba7bdd..06ff51fc 100644 --- a/src/main/java/com/seaofnodes/simple/node/StoreNode.java +++ b/src/main/java/com/seaofnodes/simple/node/StoreNode.java @@ -48,10 +48,11 @@ public class StoreNode extends MemOpNode { // Initializing Stores are emitted at helper exits for a fresh object. // They replace this private field; Escape later joins it with public // memory. Keep an unwritten BOTTOM visible to the completion check. - if( _init ) return TypeMem.make(_alias,val); + if( _init ) return TypeMem.make(_alias,val,XInt.EMPTY,XInt.EMPTY).escapes(val); assert !_declaredType.isFRef(); Type t = val.join(_declaredType).meet(MemMergeNode.contents(mem(),_alias,this)); - return TypeMem.make(_alias,t); + TypeMem prior=mem()._type instanceof TypeMem m ? m : mem()._type.isHigh() ? TypeMem.TOP : TypeMem.BOT; + return prior.withContents(_alias,t).escapes(val); } @Override -- cgit v1.2.3