From eee5b806fabae0b480ff7d41159369c5a457d014 Mon Sep 17 00:00:00 2001 From: Cliff Click Date: Tue, 26 Nov 2024 11:01:47 -0800 Subject: Chapter 18: Functions --- .../java/com/seaofnodes/simple/node/AddNode.java | 2 +- .../java/com/seaofnodes/simple/node/AndNode.java | 20 +-- .../java/com/seaofnodes/simple/node/BoolNode.java | 17 ++- .../java/com/seaofnodes/simple/node/CFGNode.java | 34 ++++- .../java/com/seaofnodes/simple/node/CProjNode.java | 6 +- .../com/seaofnodes/simple/node/CallEndNode.java | 92 ++++++++++++ .../java/com/seaofnodes/simple/node/CallNode.java | 142 ++++++++++++++++++ .../java/com/seaofnodes/simple/node/CastNode.java | 22 ++- .../com/seaofnodes/simple/node/ConstantNode.java | 16 +- .../java/com/seaofnodes/simple/node/CtrlNode.java | 14 ++ .../java/com/seaofnodes/simple/node/DivNode.java | 1 - .../java/com/seaofnodes/simple/node/FRefNode.java | 35 +++++ .../java/com/seaofnodes/simple/node/FunNode.java | 133 +++++++++++++++++ .../java/com/seaofnodes/simple/node/IfNode.java | 18 ++- .../java/com/seaofnodes/simple/node/LoadNode.java | 35 +++-- .../com/seaofnodes/simple/node/LogicalNode.java | 29 ++++ .../java/com/seaofnodes/simple/node/LoopNode.java | 48 ++++-- .../com/seaofnodes/simple/node/MemMergeNode.java | 156 +++++++++++++++++++ .../java/com/seaofnodes/simple/node/MemOpNode.java | 18 ++- .../com/seaofnodes/simple/node/MinusFNode.java | 2 +- .../java/com/seaofnodes/simple/node/MinusNode.java | 10 +- .../java/com/seaofnodes/simple/node/MultiNode.java | 11 +- .../java/com/seaofnodes/simple/node/MultiUse.java | 5 + .../java/com/seaofnodes/simple/node/NewNode.java | 8 +- src/main/java/com/seaofnodes/simple/node/Node.java | 60 ++++---- .../java/com/seaofnodes/simple/node/NotNode.java | 24 +-- .../java/com/seaofnodes/simple/node/OrNode.java | 20 +-- .../java/com/seaofnodes/simple/node/OutNode.java | 7 - .../java/com/seaofnodes/simple/node/ParmNode.java | 47 ++++++ .../java/com/seaofnodes/simple/node/PhiNode.java | 66 ++++++-- .../java/com/seaofnodes/simple/node/ProjNode.java | 16 +- .../com/seaofnodes/simple/node/RegionNode.java | 28 ++-- .../com/seaofnodes/simple/node/ReturnNode.java | 104 +++++++++++-- .../java/com/seaofnodes/simple/node/SarNode.java | 23 +-- .../com/seaofnodes/simple/node/ScopeMinNode.java | 150 ------------------- .../java/com/seaofnodes/simple/node/ScopeNode.java | 166 ++++++++++++++------- .../java/com/seaofnodes/simple/node/ShlNode.java | 25 +--- .../java/com/seaofnodes/simple/node/ShrNode.java | 18 +-- .../java/com/seaofnodes/simple/node/StartNode.java | 16 +- .../java/com/seaofnodes/simple/node/StopNode.java | 40 ++--- .../java/com/seaofnodes/simple/node/StoreNode.java | 27 ++-- .../com/seaofnodes/simple/node/StructNode.java | 3 +- .../java/com/seaofnodes/simple/node/SubNode.java | 7 +- .../com/seaofnodes/simple/node/ToFloatNode.java | 10 +- .../java/com/seaofnodes/simple/node/XCtrlNode.java | 4 +- .../java/com/seaofnodes/simple/node/XorNode.java | 21 +-- 46 files changed, 1244 insertions(+), 512 deletions(-) create mode 100644 src/main/java/com/seaofnodes/simple/node/CallEndNode.java create mode 100644 src/main/java/com/seaofnodes/simple/node/CallNode.java create mode 100644 src/main/java/com/seaofnodes/simple/node/CtrlNode.java create mode 100644 src/main/java/com/seaofnodes/simple/node/FRefNode.java create mode 100644 src/main/java/com/seaofnodes/simple/node/FunNode.java create mode 100644 src/main/java/com/seaofnodes/simple/node/LogicalNode.java create mode 100644 src/main/java/com/seaofnodes/simple/node/MemMergeNode.java create mode 100644 src/main/java/com/seaofnodes/simple/node/MultiUse.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/OutNode.java create mode 100644 src/main/java/com/seaofnodes/simple/node/ParmNode.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/ScopeMinNode.java (limited to 'src/main/java/com/seaofnodes/simple/node') diff --git a/src/main/java/com/seaofnodes/simple/node/AddNode.java b/src/main/java/com/seaofnodes/simple/node/AddNode.java index 0834a381..e6704ea6 100644 --- a/src/main/java/com/seaofnodes/simple/node/AddNode.java +++ b/src/main/java/com/seaofnodes/simple/node/AddNode.java @@ -48,7 +48,7 @@ public class AddNode extends Node { // Add of 0. We do not check for (0+x) because this will already // canonicalize to (x+0) - if( t2 instanceof TypeInteger i && i.isConstant() && i.value()==0 ) + if( t2 == TypeInteger.ZERO ) return lhs; // Add of same to a multiply by 2 diff --git a/src/main/java/com/seaofnodes/simple/node/AndNode.java b/src/main/java/com/seaofnodes/simple/node/AndNode.java index 17a4e0ca..36e3a949 100644 --- a/src/main/java/com/seaofnodes/simple/node/AndNode.java +++ b/src/main/java/com/seaofnodes/simple/node/AndNode.java @@ -1,24 +1,19 @@ package com.seaofnodes.simple.node; +import com.seaofnodes.simple.Parser; import com.seaofnodes.simple.type.Type; import com.seaofnodes.simple.type.TypeInteger; import java.util.BitSet; -public class AndNode extends Node { - public AndNode(Node lhs, Node rhs) { super(null, lhs, rhs); } +public class AndNode extends LogicalNode { + public AndNode(Parser.Lexer loc, Node lhs, Node rhs) { super(loc, lhs, rhs); } @Override public String label() { return "And"; } + @Override public String op() { return "&"; } @Override public String glabel() { return "&"; } - @Override - StringBuilder _print1(StringBuilder sb, BitSet visited) { - in(1)._print0(sb.append("("), visited); - in(2)._print0(sb.append("&"), visited); - return sb.append(")"); - } - @Override public Type compute() { Type t1 = in(1)._type, t2 = in(2)._type; @@ -59,10 +54,5 @@ public class AndNode extends Node { return null; } - @Override Node copy(Node lhs, Node rhs) { return new AndNode(lhs,rhs); } - @Override String err() { - if( !(in(1)._type instanceof TypeInteger) ) return "Cannot '&' " + in(1)._type; - if( !(in(2)._type instanceof TypeInteger) ) return "Cannot '&' " + in(2)._type; - return null; - } + @Override Node copy(Node lhs, Node rhs) { return new AndNode(_loc,lhs,rhs); } } diff --git a/src/main/java/com/seaofnodes/simple/node/BoolNode.java b/src/main/java/com/seaofnodes/simple/node/BoolNode.java index 238be00e..ae968ca1 100644 --- a/src/main/java/com/seaofnodes/simple/node/BoolNode.java +++ b/src/main/java/com/seaofnodes/simple/node/BoolNode.java @@ -2,10 +2,7 @@ package com.seaofnodes.simple.node; import com.seaofnodes.simple.Parser; import com.seaofnodes.simple.Utils; -import com.seaofnodes.simple.type.Type; -import com.seaofnodes.simple.type.TypeInteger; -import com.seaofnodes.simple.type.TypeFloat; -import com.seaofnodes.simple.type.TypeMemPtr; +import com.seaofnodes.simple.type.*; import java.util.BitSet; import static com.seaofnodes.simple.type.TypeInteger.*; @@ -35,15 +32,19 @@ abstract public class BoolNode extends Node { public TypeInteger compute() { Type t1 = in(1)._type; Type t2 = in(2)._type; + // Exactly equals? if( t1.isHigh() || t2.isHigh() ) return BOOL.dual(); + if( in(1)==in(2) ) + // LT fails, both EQ and LE succeed + return this instanceof LT ? TypeInteger.FALSE : TypeInteger.TRUE; if( t1 instanceof TypeInteger i1 && t2 instanceof TypeInteger i2 ) return doOp(i1,i2); if( t1 instanceof TypeFloat f1 && t2 instanceof TypeFloat f2 && f1.isConstant() && f2.isConstant() ) - return TypeInteger.constant(doOp(f1.value(), f2.value()) ? 1 : 0); + return doOp(f1.value(), f2.value()) ? TypeInteger.TRUE : TypeInteger.FALSE; return BOOL; } @@ -55,7 +56,7 @@ abstract public class BoolNode extends Node { public Node idealize() { // Compare of same if( in(1)==in(2) ) - return new ConstantNode(doOp(ZERO,ZERO)); + return this instanceof LT ? Parser.ZERO : new ConstantNode(TypeInteger.TRUE); // Equals pushes constant to the right; 5==X becomes X==5. if( this instanceof EQ ) { @@ -66,7 +67,7 @@ abstract public class BoolNode extends Node { return in(1)._type instanceof TypeFloat ? new EQF(in(2),in(1)) : new EQ(in(2),in(1)); } // Equals X==0 becomes a !X - if( (in(2)._type == ZERO || in(2)._type == TypeMemPtr.NULLPTR) ) + if( (in(2)._type == ZERO || in(2)._type == Type.NIL) ) return new NotNode(in(1)); } @@ -93,6 +94,7 @@ abstract public class BoolNode extends Node { public static class LT extends BoolNode { public LT(Node lhs, Node rhs) { super(lhs,rhs); } String op() { return "<" ; } + public String glabel() { return "<"; } TypeInteger doOp(TypeInteger i1, TypeInteger i2) { if( i1._max < i2._min ) return TRUE; if( i1._min >= i2._max ) return FALSE; @@ -104,6 +106,7 @@ abstract public class BoolNode extends Node { public static class LE extends BoolNode { public LE(Node lhs, Node rhs) { super(lhs,rhs); } String op() { return "<="; } + public String glabel() { return "<="; } TypeInteger doOp(TypeInteger i1, TypeInteger i2) { if( i1._max <= i2._min ) return TRUE; if( i1._min > i2._max ) return FALSE; diff --git a/src/main/java/com/seaofnodes/simple/node/CFGNode.java b/src/main/java/com/seaofnodes/simple/node/CFGNode.java index 052ad8fc..b051b3ce 100644 --- a/src/main/java/com/seaofnodes/simple/node/CFGNode.java +++ b/src/main/java/com/seaofnodes/simple/node/CFGNode.java @@ -28,6 +28,17 @@ public abstract class CFGNode extends Node { // Block head is Start, Region, CProj, but not e.g. If, Return, Stop public boolean blockHead() { return false; } + // Get the one control following; error to call with more than one e.g. an + // IfNode or other multi-way branch. + public CFGNode uctrl() { + CFGNode c = null; + for( Node n : _outputs ) + if( n instanceof CFGNode cfg ) + { assert c==null; c = cfg; } + return c; + } + + // ------------------------------------------------------------------------ /** * Immediate dominator tree depth, used to approximate a real IDOM during @@ -82,21 +93,25 @@ public abstract class CFGNode extends Node { // Start is a LoopNode which contains all at depth 1. public void buildLoopTree(StopNode stop) { _ltree = stop._ltree = Parser.XCTRL._ltree = new LoopTree((StartNode)this); - _bltWalk(2,stop, new BitSet()); + _bltWalk(2,null,stop, new BitSet()); } - private int _bltWalk(int pre, StopNode stop, BitSet post) { + int _bltWalk( int pre, FunNode fun, StopNode stop, BitSet post ) { // Pre-walked? if( _pre!=0 ) return pre; _pre = pre++; // Pre-walk for( Node use : _outputs ) - if( use instanceof CFGNode usecfg ) - pre = usecfg._bltWalk(pre,stop,post); + if( use instanceof CFGNode usecfg && !skip(usecfg) ) + pre = usecfg._bltWalk(pre,use instanceof FunNode fuse ? fuse : fun,stop,post); // Post-order work: find innermost loop LoopTree inner = null, ltree; for( Node use : _outputs ) { if( !(use instanceof CFGNode usecfg) ) continue; + if( skip(usecfg) ) continue; + if( usecfg._type == Type.XCONTROL || // Do not walk dead control + usecfg._type == TypeTuple.IF_NEITHER ) // Nor dead IFs + continue; // Child visited but not post-visited? if( !post.get(usecfg._nid) ) { // Must be a backedge to a LoopNode then @@ -109,7 +124,7 @@ public abstract class CFGNode extends Node { if( ltree._par == null ) // This loop never had an If test choose to take its // exit, i.e. it is a no-exit infinite loop. - ltree._par = ltree._head.forceExit(stop)._ltree; + ltree._par = ltree._head.forceExit(fun,stop)._ltree; ltree = ltree._par; } } @@ -129,4 +144,13 @@ public abstract class CFGNode extends Node { post.set(_nid); return pre; } + + boolean skip(CFGNode usecfg) { + // Only walk control users that are alive. + // Do not walk from a Call to linked Fun's. + return usecfg instanceof XCtrlNode || + (this instanceof CallNode && usecfg instanceof FunNode) || + (this instanceof ReturnNode && usecfg instanceof CallEndNode); + } + } diff --git a/src/main/java/com/seaofnodes/simple/node/CProjNode.java b/src/main/java/com/seaofnodes/simple/node/CProjNode.java index d2f1bbe1..01119310 100644 --- a/src/main/java/com/seaofnodes/simple/node/CProjNode.java +++ b/src/main/java/com/seaofnodes/simple/node/CProjNode.java @@ -6,7 +6,7 @@ import com.seaofnodes.simple.type.TypeMem; import com.seaofnodes.simple.type.TypeTuple; import java.util.BitSet; -public class CProjNode extends CFGNode { +public class CProjNode extends CFGNode implements MultiUse { // Which slice of the incoming multipart value public final int _idx; @@ -21,6 +21,7 @@ public class CProjNode extends CFGNode { } @Override public String label() { return _label; } + @Override public int idx() { return _idx; } @Override StringBuilder _print1(StringBuilder sb, BitSet visited) { return sb.append(_label); } @@ -48,7 +49,8 @@ public class CProjNode extends CFGNode { if( ctrl() instanceof IfNode iff && iff.pred().addDep(this) instanceof NotNode not ) return new CProjNode(new IfNode(iff.ctrl(),not.in(1)).peephole(),1-_idx,_idx==0 ? "False" : "True"); - return null; + // Copy of some other input + return ((MultiNode)ctrl()).pcopy(_idx); } @Override diff --git a/src/main/java/com/seaofnodes/simple/node/CallEndNode.java b/src/main/java/com/seaofnodes/simple/node/CallEndNode.java new file mode 100644 index 00000000..07a2b5a3 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/CallEndNode.java @@ -0,0 +1,92 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.IterPeeps; +import com.seaofnodes.simple.Parser; +import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.type.*; +import java.util.BitSet; + +/** + * CallEnd + */ +public class CallEndNode extends CFGNode implements MultiNode { + + // When set true, this Call/CallEnd/Fun/Return is being trivially inlined + private boolean _folding; + + public CallEndNode(CallNode call) { super(call); } + + @Override + public String label() { return "CallEnd"; } + @Override public boolean isMultiHead() { return true; } + @Override public boolean blockHead() { return true; } + + CallNode call() { return (CallNode)in(0); } + + @Override + StringBuilder _print1(StringBuilder sb, BitSet visited) { + sb.append("cend( "); + sb.append( in(0) instanceof CallNode ? "Call, " : "----, "); + for( int i=1; i0 && out(0) instanceof CallEndNode cend ) { + assert _cend()==cend; + return cend; + } else { + assert _cend()==null; + return null; + } + } + private CallEndNode _cend() { + CallEndNode cend=null; + for( Node n : _outputs ) + if( n instanceof CallEndNode cend0 ) + { assert cend == null; cend = cend0; } + return cend; + } + + // Get the one control following; error to call with more than one e.g. an + // IfNode or other multi-way branch. + @Override public CFGNode uctrl() { return cend(); } + + + @Override + public Type compute() { + return ctrl()._type; + } + + @Override + public Node idealize() { + CallEndNode cend = cend(); + if( cend==null ) return null; // Still building + + // Link: call calls target function. Linking makes the target FunNode + // point to this Call, and all his Parms point to the call arguments; + // also the CallEnd points to the Return. + if( fptr()._type instanceof TypeFunPtr tfp && tfp.nargs() == nargs() ) { + // If fidxs is negative, then infinite unknown functions + long fidxs = tfp.fidxs(); + if( fidxs > 0 ) { + // Wipe out the return which matching in the linker table + // Walk the (63 max) bits and link + for( ; fidxs!=0; fidxs = TypeFunPtr.nextFIDX(fidxs) ) { + int fidx = Long.numberOfTrailingZeros(fidxs); + TypeFunPtr tfp0 = tfp.makeFrom(fidx); + FunNode fun = CodeGen.CODE.link(tfp0); + if( fun!=null && !fun._folding && !linked(fun) ) + link(fun); + } + } + } + + return null; + } + + // True if Fun is linked to this Call + boolean linked( FunNode fun ) { + for( Node n : fun._inputs ) + if( n == this ) + return true; + return false; + } + + + // Link so this calls fun + private void link( FunNode fun ) { + assert !linked(fun); + fun.addDef(this); + for( Node use : fun._outputs ) + if( use instanceof ParmNode parm ) + parm.addDef(parm._idx==0 ? cend() : arg(parm._idx)); + // Call end points to function return + IterPeeps.add(cend()).addDef(fun.ret()); + assert linked(fun); + } + + @Override + public Parser.ParseException err() { + if( !(fptr()._type instanceof TypeFunPtr tfp) ) + throw Utils.TODO(); + if( !tfp.notNull() ) + return Parser.error( "Might be null calling "+tfp, _loc); + if( nargs() != tfp.nargs() ) + return Parser.error( "Expecting "+tfp.nargs()+" arguments, but found "+nargs(), _loc); + + // Check for args + for( int i=0; i= nIns() ) addDef(null); + return setDef(alias,st); + } + + // Read or update from memory. + // A shared implementation allows us to create lazy phis both during + // lookups and updates; the lazy phi creation is part of chapter 8. + Node _mem( int alias, Node st ) { + // Memory projections are made lazily; if one does not exist + // then it must be START.proj(1) + Node old = alias(alias); + if( old instanceof ScopeNode loop ) { + MemMergeNode loopmem = loop.mem(); + Node memdef = loopmem.alias(alias); + // Lazy phi! + old = memdef instanceof PhiNode phi && loop.ctrl()==phi.region() + // Loop already has a real Phi, use it + ? memdef + // Set real Phi in the loop head + // The phi takes its one input (no backedge yet) from a recursive + // lookup, which might have insert a Phi in every loop nest. + : loopmem.alias(alias, new PhiNode(Parser.memName(alias), TypeMem.BOT,loop.ctrl(),loopmem._mem(alias,null),null).peephole() ); + alias(alias,old); + } + // Memory projections are made lazily; expand as needed + return st==null ? old : alias(alias,st); // Not lazy, so this is the answer + } + + + void _merge( MemMergeNode that, RegionNode r) { + int len = Math.max(nIns(),that.nIns()); + for( int i = 2; i < len; i++) + if( alias(i) != that.alias(i) ) { // No need for redundant Phis + // If we are in lazy phi mode we need to a lookup + // by alias as it will trigger a phi creation + Node lhs = this._mem(i,null); + Node rhs = that._mem(i,null); + alias(i, new PhiNode(Parser.memName(i), lhs._type.glb().meet(rhs._type.glb()), r, lhs, rhs).peephole()); + } + } + + // Fill in the backedge of any inserted Phis + void _endLoopMem( ScopeNode scope, MemMergeNode back, MemMergeNode exit ) { + Node exit_def = exit.alias(1); + for( int i=1; i outs(); // Find a projection by index default ProjNode proj( int idx ) { @@ -17,4 +22,8 @@ public interface MultiNode extends OutNode { return prj; return null; } + + // Return not-null if this projection index is a ideal copy. + // Called by ProjNode ideal and used to collapse Multis. + default Node pcopy(int idx) { return null; } } diff --git a/src/main/java/com/seaofnodes/simple/node/MultiUse.java b/src/main/java/com/seaofnodes/simple/node/MultiUse.java new file mode 100644 index 00000000..436d3432 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/MultiUse.java @@ -0,0 +1,5 @@ +package com.seaofnodes.simple.node; + +public interface MultiUse { + abstract int idx(); +} diff --git a/src/main/java/com/seaofnodes/simple/node/NewNode.java b/src/main/java/com/seaofnodes/simple/node/NewNode.java index ddb1e09a..34d695f4 100644 --- a/src/main/java/com/seaofnodes/simple/node/NewNode.java +++ b/src/main/java/com/seaofnodes/simple/node/NewNode.java @@ -17,15 +17,16 @@ public class NewNode extends Node implements MultiNode { public NewNode(TypeMemPtr ptr, Node... nodes) { super(nodes); + assert !ptr.nullable(); _ptr = ptr; _len = ptr._obj._fields.length; // Control in slot 0 assert nodes[0]._type==Type.CONTROL || nodes[0]._type == Type.XCONTROL; // Malloc-length in slot 1 - assert nodes[1]._type instanceof TypeInteger; + assert nodes[1]._type instanceof TypeInteger || nodes[1]._type==Type.NIL; for( int i=0; i<_len; i++ ) { // Memory slices for all fields. - assert nodes[2+ i]._type instanceof TypeMem; + assert nodes[2+ i]._type.isa(TypeMem.BOT); // Value slices for all fields. assert nodes[2 + _len + i]._type != null; } @@ -57,7 +58,8 @@ public class NewNode extends Node implements MultiNode { ts[0] = Type.CONTROL; ts[1] = _ptr; for( int i=0; i outs() { return _outputs; } + public final Ary outs() { return _outputs; } public int nIns() { return _inputs.size(); } @@ -164,9 +163,11 @@ public abstract class Node implements OutNode { new_def.addUse(this); // Set the new_def over the old (killed) edge _inputs.set(idx,new_def); - if( old_def != null && // If the old def exists, remove a def->use edge - old_def.delUse(this) ) // If we removed the last use, the old def is now dead - old_def.kill(); // Kill old def + if( old_def != null ) { // If the old def exists, remove a def->use edge + if( old_def.delUse(this) ) // If we removed the last use, the old def is now dead + old_def.kill(); // Kill old def + else IterPeeps.add(old_def);// Else old lost a use, so onto worklist + } moveDepsToWorklist(); // Return new_def for easy flow-coding return new_def; @@ -301,12 +302,12 @@ public abstract class Node implements OutNode { * Always returns some not-null Node (often this). */ public final Node peephole( ) { - if (_disablePeephole) { - _type = compute(); - return this; // Peephole optimizations turned off - } + if( _type==null ) // Brand-new node, never peeped before + JSViewer.show(); Node n = peepholeOpt(); - return n==null ? this : deadCodeElim(n.peephole()); // Cannot return null for no-progress + if( n!=null ) // Made progress? + JSViewer.show(); // Show again + return n==null ? this : deadCodeElim(n._nid >= _nid ? n.peephole() : n); // Cannot return null for no-progress } /** @@ -332,9 +333,18 @@ public abstract class Node implements OutNode { // Compute initial or improved Type Type old = setType(compute()); - // Replace constant computations from non-constants with a constant node - if( !(this instanceof ConstantNode) && !(this instanceof XCtrlNode) && _type.isHighOrConst() ) - return (_type==Type.XCONTROL ? new XCtrlNode() : new ConstantNode(_type)).peepholeOpt(); + // Peepholes can be turned off - except for cleaning up dead CFG paths. + // These need to clean up so the following code motion algorithms don't + // get confused by dead or infinite paths. + if( _disablePeephole && !(this instanceof RegionNode) && !(this instanceof PhiNode) ) + return old==_type ? null : this; // Peephole optimizations turned off + + // Replace constant computations from non-constants with a constant + // node. If peeps are disabled, still allow high Phis to collapse; + // they typically come from dead Regions, and we want the Region to + // collapse, which requires the Phis to die first. + if( _type.isHighOrConst() && !isConst() && (!_disablePeephole || _type.isHigh()) ) + return ConstantNode.make(_type).peepholeOpt(); // Global Value Numbering if( _hash==0 ) { @@ -357,8 +367,9 @@ public abstract class Node implements OutNode { if( n != null ) // Something changed return n; // Report progress - if( old==_type ) ITER_NOP_CNT++; - return old==_type ? null : this; // Report progress + if( old!=_type ) return this; // Report progress; + ITER_NOP_CNT++; + return null; // No progress } public static int ITER_CNT, ITER_NOP_CNT; @@ -409,6 +420,7 @@ public abstract class Node implements OutNode { return old; } + public N init() { _type = compute(); return (N)this; } /** * This function rewrites the current Node into a more "idealized" form. @@ -577,9 +589,7 @@ public abstract class Node implements OutNode { // Swap inputs without letting either input go dead during the swap. Node swap12() { unlock(); // Hash is order dependent - Node tmp = in(1); - _inputs.set(1,in(2)); - _inputs.set(2,tmp); + _inputs.swap(1,2); return this; } @@ -606,7 +616,7 @@ public abstract class Node implements OutNode { Node copy(Node lhs, Node rhs) { throw Utils.TODO("Binary ops need to implement copy"); } // Report any post-optimize errors - String err() { return null; } + public Parser.ParseException err() { return null; } /** * Used to allow repeating tests in the same JVM. This just resets the diff --git a/src/main/java/com/seaofnodes/simple/node/NotNode.java b/src/main/java/com/seaofnodes/simple/node/NotNode.java index 664d7c7b..b454de66 100644 --- a/src/main/java/com/seaofnodes/simple/node/NotNode.java +++ b/src/main/java/com/seaofnodes/simple/node/NotNode.java @@ -18,25 +18,13 @@ public class NotNode extends Node { } @Override - public Type compute() { + public TypeInteger compute() { Type t0 = in(1)._type; - if( t0.isHigh() ) return TypeInteger.BOOL.dual(); - switch( t0 ) { - case TypeInteger i0: return i0._max < 0 || i0._min > 0 ? TypeInteger.FALSE : (i0==TypeInteger.ZERO ? TypeInteger.TRUE : TypeInteger.BOOL); - case TypeFloat i0: return i0.isConstant() ? TypeInteger.constant(i0.value()==0 ? 1 : 0) : i0; - case TypeMemPtr p0: - // top->top, bot->bot, null->1, *void->0, not-null ptr->0, ptr/nil->bot - // If input in null then true - // If input is not null ptr then false - if( p0 == TypeMemPtr.NULLPTR ) return TypeInteger.TRUE; - if( !p0._nil ) return TypeInteger.FALSE; - return TypeInteger.BOOL; - case Type t: - if( t0.getClass() != Type.class ) - // Only doing NOT on ints and ptrs - throw Utils.TODO(); - return TypeInteger.BOOL; - } + if( t0.isHigh() ) return TypeInteger.BOOL.dual(); + if( t0 == Type.NIL || t0 == TypeInteger.ZERO ) return TypeInteger.TRUE; + if( t0 instanceof TypeNil tn && tn.notNull() ) return TypeInteger.FALSE; + if( t0 instanceof TypeInteger i && (i._min > 0 || i._max < 0) ) return TypeInteger.FALSE; + return TypeInteger.BOOL; } @Override diff --git a/src/main/java/com/seaofnodes/simple/node/OrNode.java b/src/main/java/com/seaofnodes/simple/node/OrNode.java index b572346f..9b03dc5d 100644 --- a/src/main/java/com/seaofnodes/simple/node/OrNode.java +++ b/src/main/java/com/seaofnodes/simple/node/OrNode.java @@ -1,24 +1,19 @@ package com.seaofnodes.simple.node; +import com.seaofnodes.simple.Parser; import com.seaofnodes.simple.type.Type; import com.seaofnodes.simple.type.TypeInteger; import java.util.BitSet; -public class OrNode extends Node { - public OrNode(Node lhs, Node rhs) { super(null, lhs, rhs); } +public class OrNode extends LogicalNode { + public OrNode(Parser.Lexer loc, Node lhs, Node rhs) { super(loc, lhs, rhs); } @Override public String label() { return "Or"; } + @Override public String op() { return "|"; } @Override public String glabel() { return "|"; } - @Override - StringBuilder _print1(StringBuilder sb, BitSet visited) { - in(1)._print0(sb.append("("), visited); - in(2)._print0(sb.append("|"), visited); - return sb.append(")"); - } - @Override public Type compute() { Type t1 = in(1)._type, t2 = in(2)._type; @@ -56,10 +51,5 @@ public class OrNode extends Node { return null; } - @Override Node copy(Node lhs, Node rhs) { return new OrNode(lhs,rhs); } - @Override String err() { - if( !(in(1)._type instanceof TypeInteger) ) return "Cannot '|' " + in(1)._type; - if( !(in(2)._type instanceof TypeInteger) ) return "Cannot '|' " + in(2)._type; - return null; - } + @Override Node copy(Node lhs, Node rhs) { return new OrNode(_loc,lhs,rhs); } } diff --git a/src/main/java/com/seaofnodes/simple/node/OutNode.java b/src/main/java/com/seaofnodes/simple/node/OutNode.java deleted file mode 100644 index dbac136b..00000000 --- a/src/main/java/com/seaofnodes/simple/node/OutNode.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.seaofnodes.simple.node; - -import com.seaofnodes.simple.Ary; - -public interface OutNode { - Ary outs(); -} diff --git a/src/main/java/com/seaofnodes/simple/node/ParmNode.java b/src/main/java/com/seaofnodes/simple/node/ParmNode.java new file mode 100644 index 00000000..784b9146 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/ParmNode.java @@ -0,0 +1,47 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.type.Type; +import java.util.BitSet; + +public class ParmNode extends PhiNode { + + // Argument indices are mapped one-to-one on CallNode inputs + public final int _idx; // Argument index + + public ParmNode(String label, int idx, Type declaredType, Node... inputs) { + super(label,declaredType,inputs); + _idx = idx; + } + + @Override public String label() { return MemOpNode.mlabel(_label); } + + @Override public String glabel() { return _label; } + + public FunNode fun() { return (FunNode)in(0); } + + @Override + StringBuilder _print1(StringBuilder sb, BitSet visited) { + if( "main".equals(fun().sig()._name) && _label.equals("arg") ) + return sb.append("arg"); + sb.append("Parm_").append(_label).append("("); + for( Node in : _inputs ) { + if (in == null) sb.append("____"); + else in._print0(sb, visited); + sb.append(","); + } + sb.setLength(sb.length()-1); + sb.append(")"); + return sb; + } + + + @Override + public Node idealize() { + if( inProgress() ) return null; + return super.idealize(); + } + + // Always in-progress until we run out of unknown callers + @Override public boolean inProgress() { return fun().inProgress(); } +} diff --git a/src/main/java/com/seaofnodes/simple/node/PhiNode.java b/src/main/java/com/seaofnodes/simple/node/PhiNode.java index cb266ca3..9aed8287 100644 --- a/src/main/java/com/seaofnodes/simple/node/PhiNode.java +++ b/src/main/java/com/seaofnodes/simple/node/PhiNode.java @@ -1,14 +1,12 @@ package com.seaofnodes.simple.node; -import com.seaofnodes.simple.type.Type; -import com.seaofnodes.simple.type.TypeMem; -import com.seaofnodes.simple.Utils; - +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.type.*; import java.util.BitSet; public class PhiNode extends Node { - final String _label; + public final String _label; // The Phi type we compute must stay within the domain of the Phi. // Example Int stays Int, Ptr stays Ptr, Control stays Control, Mem stays Mem. @@ -16,6 +14,14 @@ public class PhiNode extends Node { public PhiNode(String label, Type declaredType, Node... inputs) { super(inputs); _label = label; assert declaredType!=null; _declaredType = declaredType; } + public PhiNode(RegionNode r, Node sample) { + super(r); + _label = ""; + _declaredType = sample._type; + while( nIns() < r.nIns() ) + addDef(sample); + } + @Override public String label() { return "Phi_"+MemOpNode.mlabel(_label); } @Override public String glabel() { return "φ_"+_label; } @@ -47,7 +53,7 @@ public class PhiNode extends Node { // During parsing Phis have to be computed type pessimistically. if( r.inProgress() ) return _declaredType; // Set type to local top of the starting type - Type t = _declaredType.lub(); + Type t = _declaredType.glb().dual();//Type.TOP; for (int i = 1; i < nIns(); i++) // If the region's control input is live, add this as a dependency // to the control because we can be peeped should it become dead. @@ -67,13 +73,21 @@ public class PhiNode extends Node { Node live = singleUniqueInput(); if (live != null) return live; + // No more fancy peeps + if( _disablePeephole ) + return null; + + // No bother if region is going to fold dead paths soon + for( int i=1; i 0 && idom.in(0) != iff ) idom = idom.idom(); + while( idom != null && idom.nIns() > 0 && idom.in(0) != iff ) idom = idom.idom(); if( idom instanceof CProjNode proj && proj._idx==1 ) return val; } @@ -149,7 +169,7 @@ public class PhiNode extends Node { } // True if last input is null - public final boolean inProgress() { + public boolean inProgress() { return in(nIns()-1) == null; } @@ -157,4 +177,26 @@ public class PhiNode extends Node { @Override boolean eq( Node n ) { return !inProgress(); } + + @Override + public Parser.ParseException err() { + if( _type != Type.BOTTOM ) return null; + + // BOTTOM means we mixed e.g. int and ptr + for( int i=1; i1 && in(nIns()-1) == null; - } + public boolean inProgress() { return nIns()>1 && in(nIns()-1) == null; } // Never equal if inProgress - @Override boolean eq( Node n ) { - return !inProgress(); - } + @Override boolean eq( Node n ) { return !inProgress(); } + } diff --git a/src/main/java/com/seaofnodes/simple/node/ReturnNode.java b/src/main/java/com/seaofnodes/simple/node/ReturnNode.java index b586a44f..e543f9ea 100644 --- a/src/main/java/com/seaofnodes/simple/node/ReturnNode.java +++ b/src/main/java/com/seaofnodes/simple/node/ReturnNode.java @@ -1,7 +1,9 @@ package com.seaofnodes.simple.node; +import com.seaofnodes.simple.Parser; +import com.seaofnodes.simple.SB; +import com.seaofnodes.simple.Utils; import com.seaofnodes.simple.type.*; - import java.util.BitSet; /** @@ -15,16 +17,18 @@ import java.util.BitSet; */ public class ReturnNode extends CFGNode { - public ReturnNode(Node ctrl, Node data, ScopeNode scope) { - // Add memory slices to Return, so all memory updates are live-on-exit. - super(ctrl, data); - if( scope!=null ) - for( int i=2; i>"; } - @Override public String glabel() { return ">>"; } - - @Override - StringBuilder _print1(StringBuilder sb, BitSet visited) { - in(1)._print0(sb.append("("), visited); - in(2)._print0(sb.append(">>"), visited); - return sb.append(")"); - } + @Override public String glabel() { return ">>"; } @Override public Type compute() { @@ -52,10 +46,5 @@ public class SarNode extends Node { return null; } - @Override Node copy(Node lhs, Node rhs) { return new SarNode(lhs,rhs); } - @Override String err() { - if( !(in(1)._type instanceof TypeInteger) ) return "Cannot '>>' " + in(1)._type; - if( !(in(2)._type instanceof TypeInteger) ) return "Cannot '>>' " + in(2)._type; - return null; - } + @Override Node copy(Node lhs, Node rhs) { return new SarNode(_loc,lhs,rhs); } } diff --git a/src/main/java/com/seaofnodes/simple/node/ScopeMinNode.java b/src/main/java/com/seaofnodes/simple/node/ScopeMinNode.java deleted file mode 100644 index 9134f9c3..00000000 --- a/src/main/java/com/seaofnodes/simple/node/ScopeMinNode.java +++ /dev/null @@ -1,150 +0,0 @@ -package com.seaofnodes.simple.node; - -import com.seaofnodes.simple.IterPeeps; -import com.seaofnodes.simple.Parser; -import com.seaofnodes.simple.Utils; -import com.seaofnodes.simple.type.*; -import java.util.*; - -import static com.seaofnodes.simple.Utils.TODO; - -/* */ -public class ScopeMinNode extends Node { - - /** The tracked fields are now complex enough to deserve a array-of-structs layout - */ - public static class Var { - public final int _idx; // index in containing scope - public final String _name; // Declared name - private Type _type; // Declared type - public final boolean _final; // Final field - public Var(int idx, String name, Type type, boolean xfinal) { - _idx = idx; - _name = name; - _type = type; - _final = xfinal; - } - public Type type() { - if( !_type.isFRef() ) return _type; - // Update self to no longer use the forward ref type - Type def = Parser.TYPES.get(((TypeMemPtr)_type)._obj._name); - return (_type=_type.meet(def)); - } - public Type lazyGLB() { - Type t = type(); - return t instanceof TypeMemPtr ? t : t.glb(); - } - @Override public String toString() { - return _type.toString()+(_final ? " ": " !")+_name; - } - } - - public ScopeMinNode() { _type = TypeMem.BOT; } - - - @Override public String label() { return "MEM"; } - - @Override - StringBuilder _print1(StringBuilder sb, BitSet visited) { - sb.append("MEM[ "); - for( int j=2; j= nIns() ) addDef(null); - return setDef(alias,st); - } - - - // Read or update from memory. - // A shared implementation allows us to create lazy phis both during - // lookups and updates; the lazy phi creation is part of chapter 8. - Node _mem( int alias, Node st ) { - // Memory projections are made lazily; if one does not exist - // then it must be START.proj(1) - Node old = alias(alias); - if( old instanceof ScopeNode loop ) { - ScopeMinNode loopmem = loop.mem(); - Node memdef = loopmem.alias(alias); - // Lazy phi! - old = memdef instanceof PhiNode phi && loop.ctrl()==phi.region() - // Loop already has a real Phi, use it - ? memdef - // Set real Phi in the loop head - // The phi takes its one input (no backedge yet) from a recursive - // lookup, which might have insert a Phi in every loop nest. - : loopmem.alias(alias, new PhiNode(Parser.memName(alias), TypeMem.BOT,loop.ctrl(),loopmem._mem(alias,null),null).peephole() ); - alias(alias,old); - } - // Memory projections are made lazily; expand as needed - return st==null ? old : alias(alias,st); // Not lazy, so this is the answer - } - - - void _merge(ScopeMinNode that, RegionNode r) { - int len = Math.max(nIns(),that.nIns()); - for( int i = 2; i < len; i++) - if( alias(i) != that.alias(i) ) { // No need for redundant Phis - // If we are in lazy phi mode we need to a lookup - // by name as it will trigger a phi creation - //Var v = _vars.at(i); - Node lhs = this._mem(i,null); - Node rhs = that._mem(i,null); - alias(i, new PhiNode(Parser.memName(i), TypeMem.BOT, r, lhs, rhs).peephole()); - } - } - - // Fill in the backedge of any inserted Phis - void _endLoopMem( ScopeNode scope, ScopeMinNode back, ScopeMinNode exit ) { - for( int i=2; i _vars; - // Since of each nested lexical scope + // Size of each nested lexical scope public final Ary _lexSize; - // True if parsing inside of a constructor - public final Ary _inCons; + // Lexical scope is one of normal Block, constructor or function + public enum Kind { Block, Constructor, Function }; + public final Ary _kinds; // Extra guards; tested predicates and casted results private final Ary _guards; // A new ScopeNode public ScopeNode() { + super(true); _vars = new Ary<>(Var .class); _lexSize= new Ary<>(Integer.class); - _inCons = new Ary<>(Boolean.class); + _kinds = new Ary<>(Kind .class); _guards = new Ary<>(Node .class); } @@ -50,7 +49,7 @@ public class ScopeNode extends ScopeMinNode { for( int i=0; i N ctrl(N n) { return setDef(0,n); } + public Node mem(Node n) { return setDef(1,n); } - public void push() { push(false); } - public void push(boolean inCon) { - assert _lexSize._len==_inCons._len; + public void push(Kind kind) { + assert _lexSize._len==_kinds._len; _lexSize.push(_vars.size()); - _inCons.push(inCon); + _kinds .push(kind); } + + // Pop a lexical scope public void pop() { - assert _lexSize._len==_inCons._len; + assert _lexSize._len==_kinds._len; + promote(); int n = _lexSize.pop(); - _inCons.pop(); + _kinds.pop(); popUntil(n); _vars.setLen(n); } - public boolean inCon() { return _inCons.last(); } + + // Look for forward references in the last lexical scope and promote to the + // next outer lexical scope. At the last scope declare them an error. + public void promote() { + int n = _lexSize.last(); + for( int i=n; i=0 && v._idx<_lexSize.at(i); i-- ) + if( _kinds.at(i)==Kind.Function ) + return true; + return false; + } + // Find name in reverse, return an index into _vars or -1. Linear scan // instead of hashtable, but probably doesn't matter until the scan // typically hits many dozens of variables. int find( String name ) { for( int i=_vars.size()-1; i>=0; i-- ) - if( _vars.get(i)._name.equals(name) ) + if( _vars.at(i)._name.equals(name) ) return i; return -1; } @@ -113,13 +143,24 @@ public class ScopeNode extends ScopeMinNode { /** * Create a new variable name in the current scope */ - public boolean define( String name, Type declaredType, boolean xfinal, Node init ) { - assert name.charAt(0)!='$' || _lexSize.size()==1; // Later scopes do not define memory - if( _lexSize._len > 1 ) - for( int i=_vars.size()-1; i>=_lexSize.last(); i-- ) - if( _vars.get(i)._name.equals(name) ) - return false; // Double define - _vars.add(new Var(nIns(),name,declaredType,xfinal)); + public boolean define( String name, Type declaredType, boolean xfinal, Node init, Parser.Lexer loc ) { + assert _lexSize.isEmpty() || name.charAt(0)!='$' ; // Later scopes do not define memory + if( _lexSize._len > 0 ) + for( int i=_vars.size()-1; i>=_lexSize.last(); i-- ) { + Var n = _vars.at(i); + if( n._name.equals(name) ) { + if( !n.isFRef() ) return false; // Double define + FRefNode fref = (FRefNode)in(n._idx); // Get forward ref + if( !xfinal || !declaredType.isConstant() ) throw fref.err(); // Must be a final constant + n.defFRef(declaredType,xfinal,loc); // Declare full correct type, final, source location + setDef(n._idx,fref.addDef(init)); // Set FRef to defined; tell parser also + } + } + Var v = new Var(nIns(),name,declaredType,xfinal,loc,init==Parser.XCTRL); + _vars.add(v); + // Creating a forward reference + if( init==Parser.XCTRL ) + init = new FRefNode(v).init(); addDef(init); return true; } @@ -154,7 +195,7 @@ public class ScopeNode extends ScopeMinNode { update(_vars.at(idx),n); } - public Var update( ScopeNode.Var v, Node st ) { + public Var update( Var v, Node st ) { Node old = in(v._idx); if( old instanceof ScopeNode loop ) { // Lazy Phi! @@ -168,6 +209,7 @@ public class ScopeNode extends ScopeMinNode { : loop.setDef(v._idx,new PhiNode(v._name, v.lazyGLB(), loop.ctrl(), loop.in(loop.update(v,null)._idx),null).peephole()); setDef(v._idx,old); } + assert !v._final || st==null; if( st!=null ) setDef(v._idx,st); // Set new value return v; } @@ -194,7 +236,7 @@ public class ScopeNode extends ScopeMinNode { // 3) Ensure that the order of defs is the same to allow easy merging dup._vars .addAll(_vars ); dup._lexSize.addAll(_lexSize); - dup._inCons .addAll(_inCons ); + dup._kinds .addAll(_kinds ); dup._guards .addAll(_guards ); // The dup'd guards all need dup'd keepers, to keep proper accounting // when later removing all guards @@ -204,7 +246,7 @@ public class ScopeNode extends ScopeMinNode { dup.addDef(ctrl()); // Control input is just copied // Memory input is a shallow copy - ScopeMinNode memdup = new ScopeMinNode(), mem = mem(); + MemMergeNode memdup = new MemMergeNode(true), mem = mem(); memdup.addDef(null); memdup.addDef(loop ? this : mem.in(1)); for( int i=2; i (x << i) + (c << i) - if( lhs instanceof AddNode add && add.addDep(this).in(2)._type instanceof TypeInteger c && c.isConstant() && c!=TypeInteger.ZERO ) { + if( lhs instanceof AddNode add && add.addDep(this).in(2)._type instanceof TypeInteger c && c.isConstant() ) { long sum = c.value() << shl.value(); if( Integer.MIN_VALUE <= sum && sum <= Integer.MAX_VALUE ) - return new AddNode( new ShlNode(add.in(1),rhs).peephole(), Parser.con(sum) ); + return new AddNode(new ShlNode(_loc,add.in(1),rhs).peephole(), Parser.con(sum) ); } } @@ -55,10 +49,5 @@ public class ShlNode extends Node { return null; } - @Override Node copy(Node lhs, Node rhs) { return new ShlNode(lhs,rhs); } - @Override String err() { - if( !(in(1)._type instanceof TypeInteger) ) return "Cannot '<<' " + in(1)._type; - if( !(in(2)._type instanceof TypeInteger) ) return "Cannot '<<' " + in(2)._type; - return null; - } + @Override Node copy(Node lhs, Node rhs) { return new ShlNode(_loc,lhs,rhs); } } diff --git a/src/main/java/com/seaofnodes/simple/node/ShrNode.java b/src/main/java/com/seaofnodes/simple/node/ShrNode.java index 22eb45a0..789fb8a8 100644 --- a/src/main/java/com/seaofnodes/simple/node/ShrNode.java +++ b/src/main/java/com/seaofnodes/simple/node/ShrNode.java @@ -1,23 +1,17 @@ package com.seaofnodes.simple.node; +import com.seaofnodes.simple.Parser; import com.seaofnodes.simple.type.Type; import com.seaofnodes.simple.type.TypeInteger; - import java.util.BitSet; -public class ShrNode extends Node { - public ShrNode(Node lhs, Node rhs) { super(null, lhs, rhs); } +public class ShrNode extends LogicalNode { + public ShrNode(Parser.Lexer loc, Node lhs, Node rhs) { super(loc, lhs, rhs); } @Override public String label() { return "Shr"; } + @Override public String op() { return ">>>"; } - @Override public String glabel() { return ">>>"; } - - @Override - StringBuilder _print1(StringBuilder sb, BitSet visited) { - in(1)._print0(sb.append("("), visited); - in(2)._print0(sb.append(">>>"), visited); - return sb.append(")"); - } + @Override public String glabel() { return ">>>"; } @Override public Type compute() { @@ -48,5 +42,5 @@ public class ShrNode extends Node { return null; } - @Override Node copy(Node lhs, Node rhs) { return new ShrNode(lhs,rhs); } + @Override Node copy(Node lhs, Node rhs) { return new ShrNode(_loc,lhs,rhs); } } diff --git a/src/main/java/com/seaofnodes/simple/node/StartNode.java b/src/main/java/com/seaofnodes/simple/node/StartNode.java index ab6e4931..50cba348 100644 --- a/src/main/java/com/seaofnodes/simple/node/StartNode.java +++ b/src/main/java/com/seaofnodes/simple/node/StartNode.java @@ -18,7 +18,7 @@ public class StartNode extends LoopNode implements MultiNode { final Type _arg; - public StartNode(Type arg) { super(null); _arg = arg; _type = compute(); } + public StartNode(Type arg) { super(null,null); _arg = arg; _type = compute(); } @Override public String label() { return "Start"; } @@ -29,7 +29,19 @@ public class StartNode extends LoopNode implements MultiNode { @Override public boolean isMultiHead() { return true; } @Override public boolean blockHead() { return true; } - @Override public CFGNode cfg0() { return this; } + @Override public CFGNode cfg0() { return null; } + + // Get the one control following; error to call with more than one e.g. an + // IfNode or other multi-way branch. For Start, its "main" + @Override public CFGNode uctrl() { + // Find "main", its the start. + CFGNode C = null; + for( Node use : _outputs ) + if( use instanceof FunNode fun && fun.sig().isa(TypeFunPtr.MAIN) ) + { assert C==null; C = fun; } + return C; + } + @Override public TypeTuple compute() { return TypeTuple.make(Type.CONTROL,TypeMem.TOP,_arg); diff --git a/src/main/java/com/seaofnodes/simple/node/StopNode.java b/src/main/java/com/seaofnodes/simple/node/StopNode.java index 867225cb..feb8347f 100644 --- a/src/main/java/com/seaofnodes/simple/node/StopNode.java +++ b/src/main/java/com/seaofnodes/simple/node/StopNode.java @@ -1,11 +1,7 @@ package com.seaofnodes.simple.node; +import com.seaofnodes.simple.Utils; import com.seaofnodes.simple.type.Type; -import com.seaofnodes.simple.Parser; -import com.seaofnodes.simple.IterPeeps; -import com.seaofnodes.simple.GlobalCodeMotion; -import com.seaofnodes.simple.GraphVisualizer; - import java.util.BitSet; public class StopNode extends CFGNode { @@ -15,6 +11,7 @@ public class StopNode extends CFGNode { public StopNode(String src) { super(); _src = src; + _type = compute(); } @Override @@ -24,7 +21,8 @@ public class StopNode extends CFGNode { @Override StringBuilder _print1(StringBuilder sb, BitSet visited) { - if( ret()!=null ) return ret()._print0(sb, visited); + // For the sake of many old tests, and single value prints as "return val" + if( ret()!=null ) return ret()._print0(sb,visited); sb.append("Stop[ "); for( Node ret : _inputs ) ret._print0(sb, visited).append(" "); @@ -33,10 +31,11 @@ public class StopNode extends CFGNode { @Override public boolean blockHead() { return true; } + // If a single Return, return it. // Otherwise, null because ambiguous. public ReturnNode ret() { - return nIns()==1 ? (ReturnNode)in(0) : null; + return nIns()==1 && in(0) instanceof ReturnNode ret ? ret : null; } @Override @@ -48,7 +47,7 @@ public class StopNode extends CFGNode { public Node idealize() { int len = nIns(); for( int i=0; i