From d32f97238c903765dec4e9a9f3cbfa5fd12cded6 Mon Sep 17 00:00:00 2001 From: Cliff Click Date: Mon, 10 Aug 2026 11:11:18 -0700 Subject: Chapter 25: Modules, Separate Compilation, and SSA Construction 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/print/ASMPrinter.java | 40 ++++++++++++++------ .../com/seaofnodes/simple/print/IRPrinter.java | 17 ++++----- .../simple/print/SimpleGraphAdapter.java | 43 +++++++++++++++++----- .../simple/print/SimpleGraphObserver.java | 2 +- 4 files changed, 70 insertions(+), 32 deletions(-) (limited to 'src/main/java/com/seaofnodes/simple/print') diff --git a/src/main/java/com/seaofnodes/simple/print/ASMPrinter.java b/src/main/java/com/seaofnodes/simple/print/ASMPrinter.java index 48296ce7..e8218afd 100644 --- a/src/main/java/com/seaofnodes/simple/print/ASMPrinter.java +++ b/src/main/java/com/seaofnodes/simple/print/ASMPrinter.java @@ -9,6 +9,8 @@ import com.seaofnodes.simple.node.*; import com.seaofnodes.simple.type.*; import java.util.ArrayList; import com.seaofnodes.simple.codegen.Encoding; +import com.seaofnodes.simple.util.BAOS; +import java.util.HashMap; /** Machine-specific facts; traversal, columns and byte display live in print/. */ public final class ASMPrinter extends AssemblyAdapter { @@ -24,7 +26,7 @@ public final class ASMPrinter extends AssemblyAdapter { } @Override public ArrayList blocks() { var blocks=new ArrayList(); - var cfg=_code._cfg; + var cfg=_enc==null ? _code._cfg : _enc._cfg; if( cfg!=null ) for( Node n : cfg ) blocks.add(n); return blocks; } @@ -50,26 +52,24 @@ public final class ASMPrinter extends AssemblyAdapter { if( n._type instanceof TypeMem || n._type instanceof TypeRPC ) return true; if( postAlloc() && n instanceof CalleeSaveNode ) return true; if( n instanceof MemMergeNode ) return true; + if( n instanceof EscapeNode ) return true; return n.getClass()==ConstantNode.class; } - @Override public boolean postAlloc() { return _code._phase.ordinal()>CodeGen.Phase.RegAlloc.ordinal(); } + @Override public boolean postAlloc() { return _code._phase.ordinal()>CodeGen.Phase.RegAlloc.ordinal() || + (_code._phase==CodeGen.Phase.RegAlloc && _code._regAlloc!=null && _code._regAlloc.done()); } @Override public boolean encoded() { return _enc!=null && _code._phase.ordinal()>=CodeGen.Phase.Encoding.ordinal(); } @Override public int defaultSize() { return _code._mach==null ? 2 : _code._mach.defaultOpSize(); } @Override public boolean littleEndian() { return _code._asmLittle; } @Override public boolean prologue(Node n) { return ((FunNode)n)._frameAdjust!=0; } @Override public byte[] bytes() { return encoded() ? _enc._bits.buf() : null; } - @Override public int offset(Node n) { - return encoded() && _enc._opStart!=null && n._nid<_enc._opStart.length ? _enc._opStart[n._nid] : -1; - } - @Override public int size(Node n) { - return encoded() && _enc._opLen!=null && n._nid<_enc._opLen.length ? _enc._opLen[n._nid]&255 : 0; - } - private static ArrayList entries(Iterable relos) { + @Override public int offset(Node n) { return encoded() ? _enc.opStart(n) : -1; } + @Override public int size(Node n) { return encoded() ? _enc.opLen(n)&255 : 0; } + private static ArrayList entries(Iterable relos, boolean ro) { var data=new ArrayList(); for( Encoding.Relo r : relos ) { + if( r.readOnly()!=ro ) continue; int align=1< { var pools=new ArrayList(); if( !encoded() ) return pools; int base=(codeEnd+15)&-16; - if( !_enc._bigCons.isEmpty() && base<_enc._bits.size() ) - pools.add(new Pool("Constant Pool",_enc._bits.buf(),base,base,entries(_enc._bigCons.values()))); + if( _enc._cpool.size()>0 ) + pools.add(new Pool("Constant Pool",_enc._cpool.buf(),0,base,objects(true))); + base=(base+_enc._cpool.size()+15)&-16; + if( _enc._sdata.size()>0 ) + pools.add(new Pool("Static Data",_enc._sdata.buf(),0,base,objects(false))); return pools; } + private ArrayList objects(boolean ro) { + var data=new ArrayList(); + for( var obj : _enc._data.entries() ) + if( !obj.external && obj.readOnly==ro ) + data.add(new Data(obj,obj.symbol+": "+obj.type.str(),obj.alignment,obj.size)); + return data; + } + // Retain this diagnostic entry point for checking that pool printing never lays out types. + static int printConstantPool(int address, SB sb, BAOS bits, HashMap relos, + boolean ro, String name) { + sb.p(com.seaofnodes.print.ASMPrinter.pool(new Pool(name,bits.buf(),0,address,entries(relos.values(),ro)))); + return address+bits.size(); + } } diff --git a/src/main/java/com/seaofnodes/simple/print/IRPrinter.java b/src/main/java/com/seaofnodes/simple/print/IRPrinter.java index c912ab77..531a549a 100644 --- a/src/main/java/com/seaofnodes/simple/print/IRPrinter.java +++ b/src/main/java/com/seaofnodes/simple/print/IRPrinter.java @@ -14,6 +14,8 @@ public final class IRPrinter extends IRAdapter { @Override public String type(Node n) { return n._type==null ? "" : n._type.str(); } @Override public String inputMark(Node n, Node def) { return n instanceof MemMergeNode && def instanceof MemMergeNode ? "^" : " "; } @Override public Kind kind(Node n) { + if( n instanceof StartCUNode ) return Kind.UNIT; + if( n instanceof StopCUNode ) return Kind.UNIT; if( n instanceof StartNode ) return Kind.START; if( n instanceof StopNode ) return Kind.STOP; if( n instanceof FunNode ) return Kind.FUN; @@ -47,15 +49,12 @@ public final class IRPrinter extends IRAdapter { public static String prettyPrint(CodeGen code) { var units=new ArrayList>(); - var funs=new ArrayList(); - for( Node n : code._start._outputs ) if( n instanceof FunNode ) funs.add(n); - units.add(new com.seaofnodes.print.IRPrinter.Unit<>(null,null,null,funs)); + for( var cu : code._compunits.values() ) { + var funs=new ArrayList(); + for( FunNode fun : code._linker ) + if( fun!=null && fun._compunit==cu ) funs.add(fun); + units.add(new com.seaofnodes.print.IRPrinter.Unit<>(cu._cname,cu._start,cu._stop,funs)); + } return PRINT.program(code._start,code._stop,units); } - public static String _prettyPrint(CodeGen code) { - if( code._cfg==null ) return prettyPrint(code); - var blocks=new ArrayList(); - for( Node n : code._cfg ) blocks.add(n); - return PRINT.scheduled(blocks); - } } diff --git a/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java b/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java index 8dc6ea47..9ca0ec51 100644 --- a/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java +++ b/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java @@ -2,22 +2,30 @@ package com.seaofnodes.simple.print; import com.seaofnodes.graph.GraphAdapter; import com.seaofnodes.graph.GraphSnapshot.*; +import com.seaofnodes.simple.codegen.CodeGen; import com.seaofnodes.simple.node.*; import com.seaofnodes.simple.node.Node; import com.seaofnodes.simple.type.TypeMem; -/** Chapter 24's view of the IR; browser, transport and layout are shared. */ +/** Chapter 25's view of the IR, including partially constructed parse-time graphs. */ public class SimpleGraphAdapter extends GraphAdapter { + @Override protected boolean show(Node n) { + if( n != CodeGen.CODE.ZERO && n != CodeGen.CODE.XCTRL ) return true; + for( int i=0; i0 && n.in(i) instanceof ReturnNode ret ? ref(ret.fun()) : 0; } - @Override protected int projectionIndex(Node n) { return n instanceof ProjNode p ? p._idx : n instanceof CProjNode p ? p._idx : -1; } + @Override protected int projectionIndex(Node n) { return n instanceof Proj p ? p.idx() : -1; } @Override protected boolean folding(Node n) { if( n instanceof FunNode fun ) return fun.folding(); @@ -32,26 +40,41 @@ public class SimpleGraphAdapter extends GraphAdapter { @Override protected Kind kind(Node n) { if( n instanceof StopNode ) return Kind.STOP; if( n instanceof ScopeNode ) return Kind.SCOPE; + if( n instanceof StartCUNode ) return Kind.UNIT; if( n instanceof FunNode ) return Kind.FUN; - if( n instanceof StartNode ) return Kind.CTRL; + // Start inherits Loop for whole-program analysis, but is not a source loop. + if( n instanceof StartNode ) return Kind.START; if( n instanceof LoopNode ) return Kind.LOOP; if( n instanceof RegionNode ) return Kind.REGION; if( n instanceof PhiNode ) return Kind.PHI; - return n instanceof CFGNode ? Kind.CTRL : isMem(n) ? Kind.MEM : Kind.DATA; + if( n instanceof CFGNode ) return Kind.CTRL; + return isMem(n) ? Kind.MEM : Kind.DATA; } @Override protected Role role(Node n, int i) { - if( n instanceof ScopeNode || n instanceof ConstantNode || n instanceof CtrlNode || n instanceof XCtrlNode ) return Role.ASSOC; - if( n instanceof PhiNode ) return i == 0 ? Role.ASSOC : isMem(n) ? Role.MEM : Role.DATA; - if( n instanceof ProjNode || n instanceof CProjNode ) + if( n instanceof ScopeNode || n instanceof ConstantNode || n instanceof FunPtrNode || + n instanceof CtrlNode || n instanceof XCtrlNode ) + return Role.ASSOC; + if( n instanceof PhiNode ) + return i == 0 ? Role.ASSOC : isMem(n) ? Role.MEM : Role.DATA; + if( n instanceof Proj ) return n instanceof CFGNode ? Role.CTRL : isMem(n) ? Role.MEM : Role.DATA; + // StartCU inherits Region, but slot 0 really connects it to Start. + if( n instanceof StartCUNode && i == 0 ) return Role.CTRL; if( n instanceof RegionNode && i == 0 ) return Role.ASSOC; + // Preserve known slot roles even when an input is not attached/typed yet. if( i == 0 || n instanceof RegionNode || n instanceof StopNode ) return Role.CTRL; - if( n instanceof MemMergeNode || (i == 1 && (n instanceof MemOpNode || n instanceof ReturnNode || n instanceof CallNode || n instanceof NewNode)) ) return Role.MEM; + if( n instanceof MemMergeNode || + (i == 1 && (n instanceof MemOpNode || n instanceof ReturnNode || n instanceof CallNode)) || + (n instanceof EscapeNode && i >= 2) ) return Role.MEM; Node def = n.in(i); + if( def instanceof CFGNode ) return Role.CTRL; if( def != null && isMem(def) ) return Role.MEM; return Role.DATA; } - private boolean isMem(Node n) { return n.isMem() || n._type instanceof TypeMem; } + private boolean isMem(Node n) { + // These Phi subclasses are memory even before their first type computation. + return n instanceof MemPhiNode || n instanceof BulkMemPhiNode || n.isMem() || n._type instanceof TypeMem; + } } diff --git a/src/main/java/com/seaofnodes/simple/print/SimpleGraphObserver.java b/src/main/java/com/seaofnodes/simple/print/SimpleGraphObserver.java index 3df1e0ea..bf8403e1 100644 --- a/src/main/java/com/seaofnodes/simple/print/SimpleGraphObserver.java +++ b/src/main/java/com/seaofnodes/simple/print/SimpleGraphObserver.java @@ -7,7 +7,7 @@ import com.seaofnodes.simple.node.Node; import java.util.ArrayList; import java.io.IOException; -/** Chapter 24's compilation context; capture and event bookkeeping are shared. */ +/** Chapter 25's compilation context; capture and event bookkeeping are shared. */ public class SimpleGraphObserver extends GraphCapture { private CodeGen _code; public SimpleGraphObserver() { super(new SimpleGraphAdapter()); } -- cgit v1.2.3