summaryrefslogtreecommitdiff
path: root/src/main/java/com/seaofnodes/simple/print
diff options
context:
space:
mode:
authorCliff Click <cliffc@acm.org>2026-08-10 11:11:18 -0700
committerCliff Click <cliffc@acm.org>2026-08-10 15:21:17 -0700
commit28209703c72fb7e9955961dbdb84e24a0f871acd (patch)
tree8e4df2c1a5e6811696b454bbc4d7cd1cc30e42e7 /src/main/java/com/seaofnodes/simple/print
parente57bdd315a7918a0d130e1c95648ca4a1b4f08ed (diff)
downloadSimple-linear-chapter25.tar.gz
Simple-linear-chapter25.zip
Chapter 25: Moduleslinear-chapter25
Diffstat (limited to 'src/main/java/com/seaofnodes/simple/print')
-rw-r--r--src/main/java/com/seaofnodes/simple/print/ASMPrinter.java192
-rw-r--r--src/main/java/com/seaofnodes/simple/print/GraphVisualizer.java15
-rw-r--r--src/main/java/com/seaofnodes/simple/print/IRPrinter.java377
-rw-r--r--src/main/java/com/seaofnodes/simple/print/JSViewer.java11
4 files changed, 216 insertions, 379 deletions
diff --git a/src/main/java/com/seaofnodes/simple/print/ASMPrinter.java b/src/main/java/com/seaofnodes/simple/print/ASMPrinter.java
index a9f86ce8..206983a0 100644
--- a/src/main/java/com/seaofnodes/simple/print/ASMPrinter.java
+++ b/src/main/java/com/seaofnodes/simple/print/ASMPrinter.java
@@ -1,130 +1,126 @@
package com.seaofnodes.simple.print;
+import com.seaofnodes.simple.codegen.CompUnit;
import com.seaofnodes.simple.codegen.CodeGen;
import com.seaofnodes.simple.codegen.Encoding.Relo;
import com.seaofnodes.simple.codegen.Encoding;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.*;
-import com.seaofnodes.simple.util.Ary;
-import com.seaofnodes.simple.util.SB;
-import com.seaofnodes.simple.util.Utils;
+import com.seaofnodes.simple.util.*;
+import java.util.HashMap;
import java.util.HashSet;
public abstract class ASMPrinter {
- public static SB print(SB sb, CodeGen code) {
- if( code._cfg==null )
- return sb.p("Need _cfg set, run after GCM");
+ public static SB print(SB sb, CodeGen code ) {
+ Encoding enc = code._encoding;
+ if( enc == null ) return sb.p("No encoding for ").p(code._srcName);
// instruction address
int iadr = 0;
- for( int i=0; i<code._cfg._len; i++ )
- if( code._cfg.at(i) instanceof FunNode fun )
- iadr = print(iadr,sb,code,fun,i);
+ // Print all functions in order
+ for( int i=0; i<enc._cfg._len; i++ )
+ if( enc._cfg.at(i) instanceof FunNode fun )
+ iadr = print(iadr,sb,code,enc,fun,i);
- // constant pool
+ // constant pools
iadr = (iadr+15)&-16; // pad to 16
- Encoding enc = code._encoding;
- if( enc!=null && !enc._bigCons.isEmpty() && iadr < enc._bits.size() ) {
- // radix sort the big constants by alignment
- Ary<Relo>[] raligns = new Ary[5];
- for( Node op : enc._bigCons.keySet() ) {
- Relo relo = enc._bigCons.get(op);
- int align = relo._t.alignment();
- Ary<Relo> relos = raligns[align]==null ? (raligns[align]=new Ary<>( Relo.class)) : raligns[align];
- relos.add(relo);
- }
+ if( enc._cpool.size()>0 )
+ iadr = printConstantPool(iadr, sb, enc._cpool,enc._bigCons,true ,"Constant Pool");
- HashSet<Type> targets = new HashSet<>();
- sb.p("--- Constant Pool ------").nl();
-
- // By alignment
- for( int align = 4; align >= 0; align-- ) {
- Ary<Relo> relos = raligns[align];
- if( relos == null ) continue;
- for( Relo relo : relos ) {
- if( targets.contains(relo._t) ) continue;
- targets.add(relo._t);
- sb.hex2(iadr).p(" ");
- switch( relo._t ) {
- case TypeTuple tt -> {
- for( Type tx : tt._types ) {
- pN(enc,sb,iadr,align).p(" ");
- iadr += (1<<align);
- }
- }
-
- case TypeStruct ts -> {
- int sz = ts.size();
- int log = 1<<align;
- sz = (sz + (log -1)) & -log; // Round up final padding
- for( int i=0; i<sz; i++ )
- sb.hex1(enc.read1(iadr++));
- }
+ iadr = (iadr+15)&-16; // pad to 16
+ if( enc._sdata.size()>0 )
+ iadr = printConstantPool(iadr, sb,enc._sdata,enc._bigCons,false, "Static Data" );
+ return sb;
+ }
+
+
+ static int printConstantPool( int iadr, SB sb, BAOS bits, HashMap<Node,Relo> bigCons, boolean ro, String msg ) {
+ sb.p("--- ").p(msg).p(" ------").nl();
+ // radix sort the big constants by alignment
+ Ary<Relo>[] raligns = new Ary[5];
+ for( Node op : bigCons.keySet() ) {
+ Relo relo = bigCons.get(op);
+ // non-constant structs in the r/w data, everything else in r/o data
+ if( (relo._t instanceof TypeStruct ts && !ts.isConstant()) == ro )
+ continue;
+ int align = relo._t.alignment();
+ Ary<Relo> relos = raligns[align]==null ? (raligns[align]=new Ary<>( Relo.class)) : raligns[align];
+ relos.add(relo);
+ }
+
+ HashSet<Type> targets = new HashSet<>();
+
+ // By alignment
+ int dadr=0;
+ for( int align = 4; align >= 0; align-- ) {
+ Ary<Relo> relos = raligns[align];
+ if( relos == null ) continue;
+ for( Relo relo : relos ) {
+ if( targets.contains(relo._t) ) continue;
+ targets.add(relo._t);
+ sb.hex2(iadr+dadr).p(" ");
+ if( relo._t instanceof TypeStruct ts ) {
+ int sz = ts.size();
+ int log = 1<<align;
+ sz = (sz + (log -1)) & -log; // Round up final padding
+ for( int i=0; i<sz; i++ )
+ sb.hex1(bits.read1(dadr++));
+ } else {
// Simple primitive (e.g. larger int, float)
- default -> {
- pN(enc,sb,iadr,align).fix(9-(1<<align),"");
- iadr += (1<<align);
- }
- }
- relo._t.print(sb).nl();
+ pN(bits,sb,dadr,align).fix(9-(1<<align),"");
+ dadr += (1<<align);
}
+ relo._t.print(sb.p('\t')).nl();
}
}
-
- return sb;
+ return iadr+dadr;
}
- private static SB pN( Encoding enc, SB sb, int iadr, int log ) {
+
+ private static SB pN( BAOS bits, SB sb, int dadr, int log ) {
switch( log ) {
- case 0: sb.hex1(enc.read1(iadr)); break;
- case 1: sb.hex2(enc.read2(iadr)); break;
- case 2: sb.hex4(enc.read4(iadr)); break;
- case 3: sb.hex8(enc.read8(iadr)); break;
+ case 0: sb.hex1(bits.read1(dadr)); break;
+ case 1: sb.hex2(bits.read2(dadr)); break;
+ case 2: sb.hex4(bits.read4(dadr)); break;
+ case 3: sb.hex8(bits.read8(dadr)); break;
}
return sb;
}
- private static int print(int iadr, SB sb, CodeGen code, FunNode fun, int cfgidx) {
- FunNode old=null;
- if( code._encoding!=null ) {
- old = code._encoding._fun;
- code._encoding._fun = fun; // Useful printing after RA
- }
+ private static int print(int iadr, SB sb, CodeGen code, Encoding enc, FunNode fun, int cfgidx) {
// Function header
sb.nl().p("---");
if( fun._name != null ) sb.p(fun._name).p(" ");
fun.sig().print(sb);
sb.p("---------------------------").nl();
- if( code._encoding!=null && code._encoding._padFunHeads )
- iadr = (iadr + 15)&-16; // All function entries padded to 16 align
+ iadr = (iadr + 15)&-16; // All function entries padded to 16 align
if( fun._frameAdjust != 0 )
- iadr = doInst(iadr,sb,code,fun,cfgidx,fun,true,true);
- while( !(code._cfg.at(cfgidx) instanceof ReturnNode) )
- iadr = doBlock(iadr,sb,code,fun,cfgidx++);
+ iadr = doInst(iadr,sb,code,enc, cfgidx,fun,true,enc!=null);
+ while( !(enc._cfg.at(cfgidx) instanceof ReturnNode) )
+ iadr = doBlock(iadr,sb,code,enc,fun,cfgidx++);
// Function separator
sb.p("---");
fun.sig().print(sb);
sb.p("---------------------------").nl();
- if( code._encoding != null )
- code._encoding._fun = old;
return iadr;
}
static private final int opWidth = 5;
static private final int argWidth = 30;
- static int doBlock(int iadr, SB sb, CodeGen code, FunNode fun, int cfgidx) {
+ static int doBlock(int iadr, SB sb, CodeGen code, Encoding enc, FunNode fun, int cfgidx) {
final int encWidth = code._mach==null ? 2 : code._mach.defaultOpSize()*2;
- CFGNode bb = code._cfg.at(cfgidx);
+ CFGNode bb = enc._cfg.at(cfgidx);
if( bb != fun && !(bb instanceof IfNode) && !(bb instanceof CallEndNode) && !(bb instanceof CallNode) && !(bb instanceof CProjNode && bb.in(0) instanceof CallEndNode ))
sb.p(label(bb)).p(":").nl();
if( bb instanceof CallNode ) return iadr;
- final boolean postAlloc = code._phase.ordinal() > CodeGen.Phase.RegAlloc.ordinal();
final boolean postEncode= code._phase.ordinal() >=CodeGen.Phase.Encoding.ordinal();
+ final boolean postAlloc = code._phase.ordinal() > CodeGen.Phase.RegAlloc.ordinal() ||
+ (code._phase.ordinal() == CodeGen.Phase.RegAlloc.ordinal() && code._regAlloc.done());
boolean once=false;
for( Node n : bb.outs() ) {
@@ -133,10 +129,10 @@ public abstract class ASMPrinter {
// Post-RegAlloc phi prints all on one line
if( postAlloc ) {
if( !once ) { once=true; sb.fix(4," ").p(" ").fix(encWidth,"").p(" "); }
- sb.p(phi._label).p(':').p(code.reg(phi,fun)).p(',');
+ sb.p(phi._label).p(':').p(code.reg(phi)).p(',');
} else {
// Pre-RegAlloc phi prints one line per
- sb.fix(4," ").p(" ").fix(encWidth,"").p(" ").fix(opWidth,phi._label).p(" ").p(code.reg(phi,fun));
+ sb.fix(4," ").p(" ").fix(encWidth,"").p(" ").fix(opWidth,phi._label).p(" ").p(code.reg(phi));
if( phi.getClass() == PhiNode.class ) {
sb.p(" = phi( ");
for( int i=1; i<phi.nIns(); i++ )
@@ -151,16 +147,17 @@ public abstract class ASMPrinter {
// All the non-phis
for( int i=0; i<bb.nOuts(); i++ )
if( !(bb.out(i) instanceof PhiNode) )
- iadr = doInst(iadr, sb,code, fun, cfgidx, bb.out(i),postAlloc, postEncode );
+ iadr = doInst(iadr, sb,code, enc, cfgidx, bb.out(i),postAlloc, postEncode );
return iadr;
}
- static int doInst( int iadr, SB sb, CodeGen code, FunNode fun, int cfgidx, Node n, boolean postAlloc, boolean postEncode ) {
+ static int doInst( int iadr, SB sb, CodeGen code, Encoding enc, int cfgidx, Node n, boolean postAlloc, boolean postEncode ) {
if( n==null || n instanceof CProjNode ) return iadr;
if( postAlloc && n instanceof CalleeSaveNode ) return iadr;
if( postEncode && n instanceof ProjNode ) return iadr;
if( n instanceof MemMergeNode ) return iadr;
+ if( n instanceof EscapeNode ) return iadr;
if( n.getClass() == ConstantNode.class ) return iadr; // Default placeholders
final int dopz = code._mach==null ? 2 : code._mach.defaultOpSize();
final int encWidth = dopz*2;
@@ -170,8 +167,8 @@ public abstract class ASMPrinter {
// need to assume a jump. There's no real hardware op here, yet.
if( n instanceof RegionNode cfg && !(n instanceof FunNode) ) {
if( postEncode ) return iadr; // All jumps inserted already
- while( cfgidx < code._cfg._len-1 ) {
- CFGNode next = code._cfg.at(++cfgidx);
+ while( cfgidx < enc._cfg._len-1 ) {
+ CFGNode next = enc._cfg.at(++cfgidx);
if( next == n ) return iadr; // Fall-through, no branch
if( next.nOuts()>1 )
break; // Has code in the block, need to jump around
@@ -185,7 +182,9 @@ public abstract class ASMPrinter {
// get indent slightly and just print their index & node#
if( n instanceof ProjNode proj ) {
if( proj._type instanceof TypeMem ) return iadr; // Nothing for the hidden ones
- sb.fix(4," ").p(" ").fix(encWidth,"").p(" ").fix(opWidth,proj._label==null ? "---" : proj._label).p(" ").p(code.reg(n,fun)).nl();
+ sb.fix(4," ").p(" ").fix(encWidth,"").p(" ").fix(opWidth,code.reg(n)).p(" // ");
+ if( proj._label != null ) sb.p(proj._label);
+ sb.nl();
return iadr;
}
@@ -195,15 +194,15 @@ public abstract class ASMPrinter {
// Encoding
int fatEncoding = 0;
- if( code._encoding != null && code._encoding._opLen!=null ) {
- int size = code._encoding._opLen[n._nid];
+ if( enc != null ) {
+ int size = enc.opLen(n);
if( code._asmLittle )
for( int i=0; i<Math.min(size,dopz); i++ )
- sb.hex1(code._encoding._bits.buf()[iadr++]);
+ sb.hex1(enc._bits.buf()[iadr++]);
else {
iadr += Math.min(size,dopz);
for( int i=0; i<Math.min(size,dopz); i++ )
- sb.hex1(code._encoding._bits.buf()[iadr-i-1]);
+ sb.hex1(enc._bits.buf()[iadr-i-1]);
}
for( int i=size*2; i<encWidth; i++ )
sb.p(" ");
@@ -244,16 +243,16 @@ public abstract class ASMPrinter {
sb.nl();
// Printing more op bits than fit
- if( isMultiOp != null && code._encoding != null && code._encoding._opLen!=null ) {
+ if( isMultiOp != null ) {
// Multiple ops, template style, no RA, no scheduling. Print out
// one-line-per-newline, with encoding bits up front.
- int size = code._encoding._opLen[n._nid];
+ int size = enc != null ? enc.opLen(n) : 0;
int off = Math.min(size,dopz);
while( isMultiOp!=null ) {
sb.hex2(iadr).p(" ");
int len = Math.min(size-off,dopz);
for( int i=0; i<len; i++ )
- sb.hex1(code._encoding._bits.buf()[iadr++]);
+ sb.hex1(enc._bits.buf()[iadr++]);
off += len;
for( int i=len; i<dopz; i++ ) sb.p(" ");
sb.p(" ");
@@ -273,7 +272,7 @@ public abstract class ASMPrinter {
// themselves. X86 special for super long encodings
sb.hex2(iadr).p(" ");
for( int i=0; i<fatEncoding; i++ )
- sb.hex1(code._encoding._bits.buf()[iadr++]);
+ sb.hex1(enc._bits.buf()[iadr++]);
sb.nl();
}
@@ -282,7 +281,7 @@ public abstract class ASMPrinter {
if( !(n instanceof CFGNode) && n instanceof MultiNode ) {
for( Node proj : n._outputs ) {
if( proj instanceof ProjNode ) // it could also be an ante-dep
- doInst(iadr,sb,code,fun, cfgidx, proj,postAlloc,postEncode);
+ doInst(iadr,sb,code,enc, cfgidx, proj,postAlloc,postEncode);
}
}
@@ -294,13 +293,10 @@ public abstract class ASMPrinter {
}
private static String isMultiOp(SB sb, int old, int len) {
- for( int i=old; i<len; i++ )
- if( sb.at(i)=='\n' ) {
- String s = sb.subString(i+1,len);
- sb.setLen(i);
- return s;
- }
- return null;
+ if( old==len || sb.at(old)!='\n' ) return null;
+ String rest = sb.subString(old+1,len);
+ sb.setLen(old);
+ return rest;
}
}
diff --git a/src/main/java/com/seaofnodes/simple/print/GraphVisualizer.java b/src/main/java/com/seaofnodes/simple/print/GraphVisualizer.java
index 20c8439a..ad2df8b6 100644
--- a/src/main/java/com/seaofnodes/simple/print/GraphVisualizer.java
+++ b/src/main/java/com/seaofnodes/simple/print/GraphVisualizer.java
@@ -1,6 +1,7 @@
package com.seaofnodes.simple.print;
import com.seaofnodes.simple.Parser;
+import com.seaofnodes.simple.codegen.CompUnit;
import com.seaofnodes.simple.node.*;
import java.util.*;
import static com.seaofnodes.simple.util.Utils.TODO;
@@ -21,16 +22,16 @@ public class GraphVisualizer {
public GraphVisualizer(boolean separateControlCluster) { this._separateControlCluster = separateControlCluster; }
public GraphVisualizer() { this(false); }
- public String generateDotOutput(Parser parse) { return generateDotOutput(parse._code._stop,parse._scope,parse._xScopes); }
- public String generateDotOutput(StopNode stop, Node scope, Stack<ScopeNode> xScopes) {
+ public String generateDotOutput(Parser parse) { return generateDotOutput(parse._ref,parse._scope,parse._xScopes); }
+ public String generateDotOutput(CompUnit cu, Node scope, Stack<ScopeNode> xScopes) {
// Since the graph has cycles, we need to create a flat list of all the
// nodes in the graph.
- Collection<Node> all = findAll(xScopes, stop, scope);
+ Collection<Node> all = findAll(xScopes, cu._stop, scope);
StringBuilder sb = new StringBuilder();
sb.append("digraph chapter18 {\n");
sb.append("/*\n");
- sb.append(stop._src);
+ sb.append(cu._src);
sb.append("\n*/\n");
// To keep the Scopes below the graph and pointing up into the graph we
@@ -80,7 +81,7 @@ public class GraphVisualizer {
// Just the Nodes first, in a cluster no edges
sb.append(doCtrl ? "\tsubgraph cluster_Controls {\n" : "\tsubgraph cluster_Nodes {\n"); // Magic "cluster_" in the subgraph name
for (Node n : all) {
- if (n instanceof ProjNode || n instanceof CProjNode || n instanceof MemMergeNode || n==Parser.XCTRL)
+ if( n instanceof ProjNode || n instanceof CProjNode || n instanceof MemMergeNode || n instanceof XCtrlNode )
continue; // Do not emit, rolled into MultiNode or Scope cluster already
if( _separateControlCluster && doCtrl && !(n instanceof CFGNode) ) continue;
if( _separateControlCluster && !doCtrl && (n instanceof CFGNode) ) continue;
@@ -162,7 +163,7 @@ public class GraphVisualizer {
sb.append("\tsubgraph cluster_").append(scopeName).append(" {\n"); // Magic "cluster_" in the subgraph name
// Special for memory ScopeMinNode
if( level==0 ) {
- MemMergeNode n = scope.mem();
+ Node n = scope.mem();
sb.append("\t\t").append(n.uniqueName()).append(" [label=\"").append(n.glabel()).append("\"];\n");
}
sb.append("\t\t").append(scopeName).append(" [label=<\n");
@@ -195,7 +196,7 @@ public class GraphVisualizer {
// Do not display the Constant->Start edge;
// ProjNodes handled by Multi;
// ScopeNodes are done separately
- if( n instanceof ConstantNode || n instanceof ProjNode || n instanceof CProjNode || n instanceof ScopeNode || n==Parser.XCTRL )
+ if( n instanceof ConstantNode || n instanceof ProjNode || n instanceof CProjNode || n instanceof ScopeNode || n instanceof XCtrlNode )
continue;
for( int i=0; i<n.nIns(); i++ ) {
Node def = n.in(i);
diff --git a/src/main/java/com/seaofnodes/simple/print/IRPrinter.java b/src/main/java/com/seaofnodes/simple/print/IRPrinter.java
index d1265bef..7c0f84f3 100644
--- a/src/main/java/com/seaofnodes/simple/print/IRPrinter.java
+++ b/src/main/java/com/seaofnodes/simple/print/IRPrinter.java
@@ -1,14 +1,12 @@
package com.seaofnodes.simple.print;
import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.Serialize;
import com.seaofnodes.simple.node.*;
-import com.seaofnodes.simple.type.TypeFunPtr;
import com.seaofnodes.simple.util.Ary;
import com.seaofnodes.simple.util.SB;
-import com.seaofnodes.simple.util.Utils;
-import java.util.ArrayList;
-import java.util.BitSet;
-import java.util.HashMap;
+
+import java.util.*;
public abstract class IRPrinter {
@@ -37,295 +35,138 @@ public abstract class IRPrinter {
return sb.p("\n");
}
-
- public static String prettyPrint(Node node, int depth) {
- return CodeGen.CODE._phase.ordinal() > CodeGen.Phase.Schedule.ordinal()
- ? _prettyPrintScheduled( node, depth )
- : _prettyPrint( node, depth );
- }
-
- // Another bulk pretty-printer. Makes more effort at basic-block grouping.
- private static String _prettyPrint( Node node, int depth ) {
- // First, a Breadth First Search at a fixed depth.
- BFS bfs = new BFS(node,depth);
- // Convert just that set to a post-order
- ArrayList<Node> rpos = new ArrayList<>();
- BitSet visit = new BitSet();
- for( int i=bfs._lim; i< bfs._bfs.size(); i++ )
- postOrd( bfs._bfs.get(i), null, rpos, visit, bfs._bs);
- // Reverse the post-order walk
+ // Bulk whole program pretty print
+ public static String prettyPrint( CodeGen code ) {
+ // Loop-tree for nodeOrder
+ CFGNode.LoopTree old = code._start._ltree;
+ if( old==null )
+ code._start.buildLoopTree( code._linker, code._stop);
+ Ary<Node> nodes = Serialize.nodeOrder(code);
+ if( old == null )
+ code._start._ltree = null;
SB sb = new SB();
- boolean gap=false;
- for( int i=rpos.size()-1; i>=0; i-- ) {
- Node n = rpos.get(i);
- if( n instanceof CFGNode || n instanceof MultiNode ) {
- if( !gap ) sb.p("\n"); // Blank before multihead
- if( n instanceof FunNode fun )
- fun.sig().print(sb.p("--- ").p(fun._name==null ? "" : fun._name).p(" ")).p("----------------------\n");
- printLine( n, sb ); // Print head
- while( --i >= 0 ) {
- Node t = rpos.get(i);
- if( !(t.in(0) instanceof MultiNode) ) { i++; break; }
- printLine( t, sb );
- }
- if( n instanceof ReturnNode ret ) {
- FunNode fun = ret.fun();
- sb.p("--- ");
- if( fun != null )
- fun.sig().print(sb.p(fun._name==null ? "" : fun._name).p(" "));
- sb.p("----------------------\n");
- }
- if( !(n instanceof CallNode) ) {
- sb.p("\n"); // Blank after multitail
- gap = true;
- }
- } else {
- printLine( n, sb );
- gap = false;
+ Node prior=null;
+ for( Node n : nodes ) {
+ if( n instanceof FunNode fun ) {
+ sb.nl().p("--- ");
+ fun.sig().print(sb.p(fun._name==null ? "" : fun._name).p(" "));
+ sb.p("----------------------\n");
}
+ if( n instanceof MultiNode || n instanceof RegionNode || n instanceof CallNode ||
+ (isMultiChild(prior) && !isMultiChild(n)) )
+ sb.nl();
+ printLine(n,sb);
+ if( n instanceof ReturnNode ret )
+ sb.p("--- ").p(ret._fun._name==null ? "" : ret._fun._name).p(" ----------------------\n");
+ prior = n;
}
return sb.toString();
}
- private static void postOrd(Node n, Node prior, ArrayList<Node> rpos, BitSet visit, BitSet bfs) {
- if( !bfs.get(n._nid) )
- return; // Not in the BFS visit
- if( n instanceof FunNode && !(prior instanceof StartNode) )
- return; // Only visit Fun from Start
- if( visit.get(n._nid) ) return; // Already post-order walked
- visit.set(n._nid);
- // First walk the CFG, then everything
- if( n instanceof CFGNode ) {
- for( Node use : n._outputs )
- // Follow CFG, not across call/function borders, and not around backedges
- if( use instanceof CFGNode && !(n instanceof CallNode && use instanceof FunNode) &&
- use.nOuts() >= 1 && !(use._outputs.get(0) instanceof LoopNode) )
- postOrd(use, n, rpos,visit,bfs);
- for( Node use : n._outputs )
- // Follow CFG, not across call/function borders
- if( use instanceof CFGNode && !(n instanceof CallNode && use instanceof FunNode) )
- postOrd(use,n,rpos,visit,bfs);
- }
- // Follow all outputs
- for( Node use : n._outputs )
- if( use != null &&
- !(n instanceof CallNode && use instanceof FunNode) &&
- (n instanceof FunNode || !(use instanceof ParmNode)) )
- postOrd(use, n, rpos,visit,bfs);
- // Post-order
- rpos.add(n);
+ private static boolean isMultiHead(Node n) {
+ return n instanceof RegionNode || n instanceof MultiNode;
}
- // Breadth-first search, broken out in a class to keep in more independent.
- // Maintains a root-set of Nodes at the limit (or past by 1 if MultiHead).
- public static class BFS {
- // A breadth first search, plus MultiHeads for any MultiTails
- public final ArrayList<Node> _bfs;
- public final BitSet _bs; // Visited members by node id
- public final int _depth; // Depth limit
- public final int _lim; // From here to _bfs._len can be roots for a reverse search
- public BFS( Node base, int d ) {
- _depth = d;
- _bfs = new ArrayList<>();
- _bs = new BitSet();
-
- add(base); // Prime the pump
- int idx=0, lim=1; // Limit is where depth counter changes
- while( idx < _bfs.size() ) { // Ran out of nodes below depth
- Node n = _bfs.get(idx++);
- for( Node def : n._inputs )
- if( def!=null && !_bs.get(def._nid) )
- add(def);
- if( idx==lim ) { // Depth counter changes at limit
- if( --d < 0 )
- break; // Ran out of depth
- lim = _bfs.size(); // New depth limit
- }
- }
- // Toss things past the limit except multi-heads
- while( idx < _bfs.size() ) {
- Node n = _bfs.get(idx);
- if( n instanceof MultiNode ) idx++;
- else del(idx);
- }
- // Root set is any node with no inputs in the visited set
- lim = _bfs.size();
- for( int i=_bfs.size()-1; i>=0; i-- )
- if( !any_visited(_bfs.get(i)) )
- swap( i,--lim);
- _lim = lim;
- }
- void swap( int x, int y ) {
- if( x==y ) return;
- Node tx = _bfs.get(x);
- Node ty = _bfs.get(y);
- _bfs.set(x,ty);
- _bfs.set(y,tx);
- }
- void add(Node n) {
- _bfs.add(n);
- _bs.set(n._nid);
- }
- void del(int idx) {
- _bs.clear(_bfs.get(idx)._nid);
- Utils.del(_bfs, idx);
- }
- boolean any_visited( Node n ) {
- for( Node def : n._inputs )
- if( def!=null && _bs.get(def._nid) )
- return true;
- return false;
- }
+ private static boolean isMultiChild(Node n) {
+ return n!=null && n.nIns() > 0 && n.in(0) != null && isMultiHead(n.in(0));
}
- public static String _prettyPrint( CodeGen code ) {
- SB sb = new SB();
- // Print the Start "block"
- printLine(code._start,sb);
- for( Node n : code._start._outputs )
- printLine(n,sb);
- sb.nl();
- // Skip start, stop
- for( int i=1; i<code._cfg._len-1; i++ ) {
- CFGNode blk = code._cfg.at(i);
- if( blk.blockHead() && (!(blk instanceof CProjNode) || (blk.cfg0() instanceof IfNode )) ) {
- if( blk instanceof FunNode fun )
- sb.p("--- ").p(fun.label()).p(" ---------------------------").nl();
- // Print block header
- sb.p("%-13.13s".formatted(label(blk)+":"));
- sb.p( " ".repeat(4) ).p(" [[ ");
- if( blk instanceof RegionNode )
- for( int j=1; j<blk.nIns(); j++ )
- label(sb,blk.cfg(j));
- else
- label(sb,blk.cfg(0));
- sb.p(" ]] \n");
- }
- printLine(blk,sb);
- if( blk instanceof ReturnNode ret )
- sb.p("--- ").p(ret.fun().label()).p(" ---------------------------").nl();
+ // ----------------------------------------
+ // Another bulk pretty-printer. Makes more effort at basic-block grouping.
+ public static String prettyPrint(Node node, int depth) {
+ // Convert just that set to a post-order
+ Ary<Node> post = new Ary<>(Node.class);
+ Ary<Node> rets = new Ary<>(Node.class);
+ var visit = new IdentityHashMap<Node,Integer>();
+ assert depth < 5000;
+ postOrd( node, 0, depth, visit, post, rets);
+ // Function calls all print independent
+ while( rets.size()>0 )
+ postOrd( rets.pop(), 0, depth, visit, post, rets );
- // Block contents
- for( Node n : blk._outputs ) {
- if( n instanceof CFGNode cfg ) continue;
- printLine(n,sb);
- if( !(n instanceof CFGNode) && n instanceof MultiNode )
- for( Node use : n._outputs )
- if( use instanceof ProjNode )
- printLine(use,sb);
+
+ // Reverse the post-order walk
+ SB sb = new SB();
+ for( int i=0; i<post._len; i++ ) {
+ Node n = post.at(i);
+ // Split ahead of a Region/MultiNode
+ if( n instanceof RegionNode || n instanceof MultiNode ||
+ (i>0 && isMultiChild(post.at(i-1)) && !isMultiChild(n)) )
+ sb.nl();
+ if( n instanceof FunNode fun )
+ fun.sig().print(sb.p("--- ").p(fun._name==null ? "" : fun._name).p(" ")).p("----------------------\n");
+ printLine( n, sb ); // Print head
+ if( n instanceof ReturnNode ret ) {
+ FunNode fun = ret.fun();
+ sb.p("--- ").p(fun==null ? "" : fun._name).p("----------------------\n");
}
}
-
- printLine(code._stop,sb);
return sb.toString();
}
- // Bulk pretty printer, knowing scheduling information is available
- private static String _prettyPrintScheduled( Node node, int depth ) {
- // Backwards DFS walk to depth.
- HashMap<Integer,Integer> ds = new HashMap<>();
- Ary<Node> ns = new Ary<>(Node.class);
- _walk(ds,ns,node,depth);
- // Remove data projections, these are force-printed behind their multinode head
- for( int i=0; i<ns.size(); i++ ) {
- if( ns.get(i) instanceof ProjNode proj && !(proj.in(0) instanceof CFGNode) ) {
- ns.del(i--);
- ds.remove(proj._nid);
- }
- }
- // Print by block with least idepth
- SB sb = new SB();
- Ary<Node> bns = new Ary<>(Node.class);
- while( !ds.isEmpty() ) {
- CFGNode blk = null;
- for( Node n : ns ) {
- CFGNode cfg = n instanceof CFGNode cfg0 && cfg0.blockHead() ? cfg0 : n.cfg0();
- if( blk==null || cfg.idepth() < blk.idepth() )
- blk = cfg;
- }
- Integer d = ds.remove(blk._nid);
- ns.del(ns.find(blk));
- // Print block header
- sb.p("%-13.13s".formatted(label(blk)+":"));
- sb.p( " ".repeat(4) ).p(" [[ ");
- if( blk instanceof StartNode ) ;
- else if( blk instanceof RegionNode || blk instanceof StopNode )
- for( int i=(blk instanceof StopNode ? 3 : 1); i<blk.nIns(); i++ )
- label(sb,blk.cfg(i));
- else
- label(sb,blk.cfg(0));
- sb.p(" ]] \n");
- printLine(blk,sb);
+ // keep the largest PO depth?; probably want *shortest*
+ private static void postOrd( Node n, int d, int cutoff, IdentityHashMap<Node,Integer> visit, Ary<Node> post, Ary<Node> rets ) {
+ if( n==null ) return;
+ if( d >= cutoff ) return; // Too deep
+ Integer depth = visit.get(n);
+ if( depth!=null ) return; // Been there, done that
+
+ // Not a multi-child (Phi of a Region or Proj of a MultiNode)
+ if( !isMultiHead(n) && !isMultiChild(n) ) {
+ visit.put(n,d); // Visit node N and depth D
+ for( Node def : n._inputs ) // Children before visit
+ postOrd(def, d+1, cutoff, visit, post, rets);
+ post.add(n); // Post-order add
+ return;
+ }
- // Collect block contents that are in the depth limit
- bns.clear();
- int xd = Integer.MAX_VALUE;
- for( Node use : blk._outputs ) {
- Integer i = ds.get(use._nid);
- if( i!=null && !(use instanceof CFGNode cfg && cfg.blockHead()) ) {
- if( bns.find(use)==-1 )
- bns.add(use);
- xd = Math.min(xd,i);
+ // Multi-child; print the parent and all children together
+ Node multi = isMultiHead(n) ? n : n.in(0);
+ // Multi + children are visited all at once
+ visit.put(multi,d+1);
+ for( Node out : multi.outs() )
+ visit.put(out,d);
+
+ // Order to visit all the children
+ Node[] outs = multi.outs().asAry();
+ Arrays.sort(outs, Comparator.comparingInt( IRPrinter::sortOrder ) );
+
+ // Do not walk out of Functions, this makes functions stand-alone
+ // but otherwise unordered
+ if( !(multi instanceof FunNode fun && fun._folding) ) {
+ // Visit all children inputs all at once
+ for( Node out : outs ) {
+ // A FunNode is both a multi-head AND a multi-child from Start
+ if( !isMultiHead(out) && out != null ) {
+ for( Node def : out._inputs ) {
+ postOrd(def, d+1, cutoff, visit, post, rets);
+ }
}
}
- // Print Phis up front, if any
- for( int i=0; i<bns.size(); i++ )
- if( bns.get(i) instanceof PhiNode phi )
- printLine( phi, sb,bns,i--,ds,ns);
-
- // Print block contents in depth order, bumping depth until whole block printed
- for( ; !bns.isEmpty(); xd++ )
- for( int i=0; i<bns.size(); i++ ) {
- Node n = bns.get(i);
- if( ds.get(n._nid)==xd ) {
- printLine( n, sb, bns, i--, ds,ns );
- if( n instanceof MultiNode && !(n instanceof CFGNode) ) {
- for( Node use : n._outputs ) {
- if( use instanceof ProjNode )
- printLine(use,sb,bns,bns.indexOf(use),ds,ns);
- }
- }
- }
+ // Visit multi inputs
+ for( Node def : multi._inputs ) {
+ if( multi instanceof CallEndNode && def instanceof ReturnNode ret && !ret._fun._folding ) {
+ rets.add(ret); // But save the Return for a separate function print
+ } else {
+ postOrd(def, d+2, cutoff, visit, post, rets);
}
- sb.p("\n");
+ }
}
- return sb.toString();
- }
- private static void _walk( HashMap<Integer,Integer> ds, Ary<Node> ns, Node node, int d ) {
- Integer nd = ds.get(node._nid);
- if( nd!=null && d <= nd ) return; // Been there, done that
- Integer old = ds.put(node._nid,d) ;
- if( old == null )
- ns.add(node);
- if( d == 0 ) return; // Depth cutoff
- for( Node def : node._inputs )
- if( def != null &&
- !(node instanceof LoopNode loop && loop.back()==def) &&
- // Don't walk into or out of functions
- !(node instanceof CallEndNode && def instanceof ReturnNode) &&
- !(node instanceof FunNode && def instanceof CallNode) &&
- !(node instanceof ParmNode && !(def instanceof FunNode))
- )
- _walk(ds,ns,def,d-1);
+ // Post visit the multi and all children
+ post.add(multi);
+ for( Node out : outs )
+ if( !isMultiHead(out) )
+ post.add(out);
}
- static String label( CFGNode blk ) {
- if( blk instanceof StartNode ) return "START";
- return (blk instanceof LoopNode ? "LOOP" : "L")+blk._nid;
- }
- static void label( SB sb, CFGNode blk ) {
- if( !blk.blockHead() ) blk = blk.cfg(0);
- sb.p( "%-9.9s ".formatted( label( blk ) ) );
- }
- static void printLine( Node n, SB sb, Ary<Node> bns, int i, HashMap<Integer,Integer> ds, Ary<Node> ns ) {
- printLine( n, sb );
- if( i != -1 ) bns.del(i);
- ds.remove(n._nid);
- int idx = ns.find(n);
- if( idx!=-1 ) ns.del(idx);
+ static int sortOrder( Node n ) {
+ if( n instanceof ProjNode proj ) return proj._idx;
+ if( n instanceof PhiNode phi ) return phi._nid;
+ if( n == null ) return 0;
+ return n._nid+1000000;
}
}
diff --git a/src/main/java/com/seaofnodes/simple/print/JSViewer.java b/src/main/java/com/seaofnodes/simple/print/JSViewer.java
index 62ff0001..0f34af2e 100644
--- a/src/main/java/com/seaofnodes/simple/print/JSViewer.java
+++ b/src/main/java/com/seaofnodes/simple/print/JSViewer.java
@@ -202,11 +202,11 @@ public class JSViewer implements AutoCloseable {
int lexStart = level==0 ? 0 : scope._kinds.at(level-1)._lexSize;
// Special for memory ScopeMinNode
- MemMergeNode n = scope.nIns()>1 ? scope.mem() : null;
- if( level==0 && n!=null && n.nIns()>2 ) {
+ Node n = scope.nIns()>1 ? scope.mem() : null;
+ if( level==0 && n instanceof MemMergeNode merge && n.nIns()>2 ) {
sb.i().p("<TR>");
for( int m=2; m<n.nIns(); m++ )
- cell(sb,"#"+m,n.alias(m),"m"+m);
+ cell(sb,"#"+m,merge.alias(m),"m"+m);
sb.p("</TR>\n"); // End scope level
}
@@ -346,7 +346,7 @@ public class JSViewer implements AutoCloseable {
sb.i().p(scopeName).p(" -> ").p(n.uniqueName()).p(" [ style=dashed color=grey];\n");
}
// Force functions above return
- if( n instanceof ReturnNode ret && ret.inProgress() && !ret.fun().isDead() )
+ if( n instanceof ReturnNode ret && !ret.fun().isDead() )
sb.i().p(ret.ctrl().uniqueName()).p(" -> ").p(ret.fun().uniqueName()).p("[style=invis]\n");
}
}
@@ -376,8 +376,7 @@ public class JSViewer implements AutoCloseable {
}
// Memory
- if( scope.nIns()>1 ) {
- MemMergeNode n = scope.mem();
+ if( scope.nIns()>1 && scope.mem() instanceof MemMergeNode n ) {
for( int i=2; i<n.nIns(); i++ ) {
Node def = n.in(i);
while( def instanceof ScopeNode lazy )