From 2aa8c5d1d0659bd1c828134f1a1f14d025a2d0f1 Mon Sep 17 00:00:00 2001 From: Cliff Click Date: Fri, 7 Feb 2025 08:58:36 -0800 Subject: Ch# Chapter 19: Instruction Selection and Portable Compilation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: hadha Co-authored-by: Tamás Hadházy <85063808+Hels15@users.noreply.github.com> --- .../java/com/seaofnodes/simple/node/AddNode.java | 5 +- .../java/com/seaofnodes/simple/node/BoolNode.java | 27 +- .../java/com/seaofnodes/simple/node/CFGNode.java | 18 +- .../java/com/seaofnodes/simple/node/CProjNode.java | 4 +- .../com/seaofnodes/simple/node/CallEndNode.java | 12 +- .../java/com/seaofnodes/simple/node/CallNode.java | 20 +- .../com/seaofnodes/simple/node/ConstantNode.java | 14 +- .../java/com/seaofnodes/simple/node/CtrlNode.java | 3 +- .../java/com/seaofnodes/simple/node/FunNode.java | 38 +- .../java/com/seaofnodes/simple/node/IfNode.java | 7 +- .../java/com/seaofnodes/simple/node/LoadNode.java | 47 ++- .../java/com/seaofnodes/simple/node/LoopNode.java | 1 + .../seaofnodes/simple/node/MachConcreteNode.java | 27 ++ .../java/com/seaofnodes/simple/node/MachNode.java | 49 +++ .../com/seaofnodes/simple/node/MemMergeNode.java | 8 +- .../java/com/seaofnodes/simple/node/MemOpNode.java | 13 + .../java/com/seaofnodes/simple/node/MulNode.java | 30 +- .../java/com/seaofnodes/simple/node/MultiUse.java | 5 - .../java/com/seaofnodes/simple/node/NewNode.java | 30 +- src/main/java/com/seaofnodes/simple/node/Node.java | 107 +++--- .../java/com/seaofnodes/simple/node/ParmNode.java | 3 +- .../java/com/seaofnodes/simple/node/PhiNode.java | 7 +- .../java/com/seaofnodes/simple/node/ProjNode.java | 6 +- .../com/seaofnodes/simple/node/ReadOnlyNode.java | 1 + .../com/seaofnodes/simple/node/RegionNode.java | 70 ++-- .../com/seaofnodes/simple/node/ReturnNode.java | 5 +- .../java/com/seaofnodes/simple/node/ScopeNode.java | 4 +- .../java/com/seaofnodes/simple/node/StartNode.java | 6 +- .../java/com/seaofnodes/simple/node/StopNode.java | 1 + .../java/com/seaofnodes/simple/node/StoreNode.java | 46 +-- .../java/com/seaofnodes/simple/node/XCtrlNode.java | 3 +- .../simple/node/cpus/x86_64_v2/AddFMemX86.java | 33 ++ .../simple/node/cpus/x86_64_v2/AddFX86.java | 29 ++ .../simple/node/cpus/x86_64_v2/AddIX86.java | 39 ++ .../simple/node/cpus/x86_64_v2/AddMemX86.java | 25 ++ .../simple/node/cpus/x86_64_v2/AddX86.java | 29 ++ .../simple/node/cpus/x86_64_v2/AndIX86.java | 42 +++ .../simple/node/cpus/x86_64_v2/CallRX86.java | 31 ++ .../simple/node/cpus/x86_64_v2/CallX86.java | 40 ++ .../simple/node/cpus/x86_64_v2/CmpIX86.java | 43 +++ .../simple/node/cpus/x86_64_v2/CmpMemX86.java | 33 ++ .../simple/node/cpus/x86_64_v2/CmpX86.java | 27 ++ .../simple/node/cpus/x86_64_v2/DivFX86.java | 33 ++ .../simple/node/cpus/x86_64_v2/DivX86.java | 31 ++ .../simple/node/cpus/x86_64_v2/FltX86.java | 36 ++ .../simple/node/cpus/x86_64_v2/FunX86.java | 21 ++ .../simple/node/cpus/x86_64_v2/I2f8X86.java | 28 ++ .../simple/node/cpus/x86_64_v2/IntX86.java | 45 +++ .../simple/node/cpus/x86_64_v2/JmpX86.java | 46 +++ .../simple/node/cpus/x86_64_v2/LeaX86.java | 42 +++ .../simple/node/cpus/x86_64_v2/LoadX86.java | 25 ++ .../simple/node/cpus/x86_64_v2/MemAddX86.java | 28 ++ .../simple/node/cpus/x86_64_v2/MemOpX86.java | 87 +++++ .../simple/node/cpus/x86_64_v2/MulFX86.java | 30 ++ .../simple/node/cpus/x86_64_v2/MulIX86.java | 36 ++ .../simple/node/cpus/x86_64_v2/MulX86.java | 30 ++ .../simple/node/cpus/x86_64_v2/NewX86.java | 40 ++ .../simple/node/cpus/x86_64_v2/OrIX86.java | 44 +++ .../simple/node/cpus/x86_64_v2/ParmX86.java | 24 ++ .../simple/node/cpus/x86_64_v2/ProjX86.java | 23 ++ .../simple/node/cpus/x86_64_v2/RetX86.java | 48 +++ .../simple/node/cpus/x86_64_v2/SarX86.java | 40 ++ .../simple/node/cpus/x86_64_v2/SetX86.java | 39 ++ .../simple/node/cpus/x86_64_v2/ShlIX86.java | 38 ++ .../simple/node/cpus/x86_64_v2/ShrIX86.java | 39 ++ .../simple/node/cpus/x86_64_v2/StoreX86.java | 30 ++ .../simple/node/cpus/x86_64_v2/SubFX86.java | 29 ++ .../simple/node/cpus/x86_64_v2/SubX86.java | 29 ++ .../simple/node/cpus/x86_64_v2/TFPX86.java | 41 ++ .../simple/node/cpus/x86_64_v2/XorIX86.java | 42 +++ .../simple/node/cpus/x86_64_v2/x86_64_v2.java | 418 +++++++++++++++++++++ 71 files changed, 2200 insertions(+), 230 deletions(-) create mode 100644 src/main/java/com/seaofnodes/simple/node/MachConcreteNode.java create mode 100644 src/main/java/com/seaofnodes/simple/node/MachNode.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/MultiUse.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFMemX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddIX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddMemX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AndIX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallRX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpIX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpMemX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivFX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FltX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FunX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/I2f8X86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/IntX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/JmpX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LeaX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LoadX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemAddX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemOpX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulFX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulIX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NewX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/OrIX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ParmX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ProjX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RetX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SarX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SetX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlIX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrIX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/StoreX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubFX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TFPX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/XorIX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.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 e6704ea6..6b7f2418 100644 --- a/src/main/java/com/seaofnodes/simple/node/AddNode.java +++ b/src/main/java/com/seaofnodes/simple/node/AddNode.java @@ -4,6 +4,8 @@ import com.seaofnodes.simple.type.*; import java.util.BitSet; +import static com.seaofnodes.simple.Parser.con; + public class AddNode extends Node { public AddNode(Node lhs, Node rhs) { super(null, lhs, rhs); } @@ -53,7 +55,7 @@ public class AddNode extends Node { // Add of same to a multiply by 2 if( lhs==rhs ) - return new MulNode(lhs,new ConstantNode(TypeInteger.constant(2)).peephole()); + return new ShlNode(null,lhs,con(1)); // Goal: a left-spine set of adds, with constants on the rhs (which then fold). @@ -126,6 +128,7 @@ public class AddNode extends Node { lphi = pcon(lhs.in(2),op); // Will rotate with the Phi push } if( lphi==null ) return null; + if( lphi.region().nIns() <=2 ) return null; // Phi is collapsing // RHS is a constant or a Phi of constants if( !(rhs instanceof ConstantNode) && pcon(rhs,op)==null ) diff --git a/src/main/java/com/seaofnodes/simple/node/BoolNode.java b/src/main/java/com/seaofnodes/simple/node/BoolNode.java index ae968ca1..84be11a9 100644 --- a/src/main/java/com/seaofnodes/simple/node/BoolNode.java +++ b/src/main/java/com/seaofnodes/simple/node/BoolNode.java @@ -3,7 +3,6 @@ package com.seaofnodes.simple.node; import com.seaofnodes.simple.Parser; import com.seaofnodes.simple.Utils; import com.seaofnodes.simple.type.*; - import java.util.BitSet; import static com.seaofnodes.simple.type.TypeInteger.*; @@ -13,7 +12,7 @@ abstract public class BoolNode extends Node { super(null, lhs, rhs); } - abstract String op(); // String opcode name + abstract public String op(); // String opcode name @Override public String label() { return getClass().getSimpleName(); } @@ -37,14 +36,14 @@ abstract public class BoolNode extends Node { return BOOL.dual(); if( in(1)==in(2) ) // LT fails, both EQ and LE succeed - return this instanceof LT ? TypeInteger.FALSE : TypeInteger.TRUE; + return this instanceof LT ? FALSE : 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 doOp(f1.value(), f2.value()) ? TypeInteger.TRUE : TypeInteger.FALSE; + return doOp(f1.value(), f2.value()) ? TRUE : FALSE; return BOOL; } @@ -56,7 +55,7 @@ abstract public class BoolNode extends Node { public Node idealize() { // Compare of same if( in(1)==in(2) ) - return this instanceof LT ? Parser.ZERO : new ConstantNode(TypeInteger.TRUE); + return this instanceof LT ? Parser.ZERO : new ConstantNode(TRUE); // Equals pushes constant to the right; 5==X becomes X==5. if( this instanceof EQ ) { @@ -82,7 +81,7 @@ abstract public class BoolNode extends Node { public static class EQ extends BoolNode { public EQ(Node lhs, Node rhs) { super(lhs,rhs); } - String op() { return "=="; } + public String op() { return "=="; } TypeInteger doOp(TypeInteger i1, TypeInteger i2) { if( i1==i2 && i1.isConstant() ) return TRUE; if( i1._max < i2._min || i1._min > i2._max ) return FALSE; @@ -93,7 +92,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 op() { return "<" ; } public String glabel() { return "<"; } TypeInteger doOp(TypeInteger i1, TypeInteger i2) { if( i1._max < i2._min ) return TRUE; @@ -105,7 +104,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 op() { return "<="; } public String glabel() { return "<="; } TypeInteger doOp(TypeInteger i1, TypeInteger i2) { if( i1._max <= i2._min ) return TRUE; @@ -120,4 +119,16 @@ abstract public class BoolNode extends Node { public static class LTF extends LT { public LTF(Node lhs, Node rhs) { super(lhs,rhs); } boolean doOp(double lhs, double rhs) { return lhs < rhs; } } public static class LEF extends LE { public LEF(Node lhs, Node rhs) { super(lhs,rhs); } boolean doOp(double lhs, double rhs) { return lhs <= rhs; } } + // Unsigned less that, for range checks. Not directly user writable. + public static class ULT extends BoolNode { + public ULT(Node lhs, Node rhs) { super(lhs,rhs); } + public String op() { return "u<" ; } + public String glabel() { return "u<"; } + TypeInteger doOp(TypeInteger i1, TypeInteger i2) { + if( Long.compareUnsigned(i1._max,i2._min) < 0 ) return TRUE; + if( Long.compareUnsigned(i1._min,i2._max) >= 0 ) return FALSE; + return BOOL; + } + Node copy(Node lhs, Node rhs) { return new ULT(lhs,rhs); } + } } diff --git a/src/main/java/com/seaofnodes/simple/node/CFGNode.java b/src/main/java/com/seaofnodes/simple/node/CFGNode.java index b051b3ce..174725f5 100644 --- a/src/main/java/com/seaofnodes/simple/node/CFGNode.java +++ b/src/main/java/com/seaofnodes/simple/node/CFGNode.java @@ -1,12 +1,12 @@ package com.seaofnodes.simple.node; +import com.seaofnodes.simple.Ary; import com.seaofnodes.simple.Parser; -import com.seaofnodes.simple.type.*; import com.seaofnodes.simple.Utils; - +import com.seaofnodes.simple.type.*; import java.util.BitSet; -import java.util.HashSet; import java.util.HashMap; +import java.util.HashSet; /** Control Flow Graph Nodes *

@@ -21,7 +21,13 @@ import java.util.HashMap; */ public abstract class CFGNode extends Node { - public CFGNode(Node... nodes) { super(nodes); } + public CFGNode(Node... nodes) { super(nodes); } + public CFGNode(CFGNode cfg) { + super(cfg); + _idepth = cfg._idepth; + _ltree = cfg._ltree; + _pre = cfg._pre; + } public CFGNode cfg(int idx) { return (CFGNode)in(idx); } @@ -101,8 +107,8 @@ public abstract class CFGNode extends Node { _pre = pre++; // Pre-walk for( Node use : _outputs ) - if( use instanceof CFGNode usecfg && !skip(usecfg) ) - pre = usecfg._bltWalk(pre,use instanceof FunNode fuse ? fuse : fun,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; diff --git a/src/main/java/com/seaofnodes/simple/node/CProjNode.java b/src/main/java/com/seaofnodes/simple/node/CProjNode.java index 01119310..690ef590 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 implements MultiUse { +public class CProjNode extends CFGNode { // Which slice of the incoming multipart value public final int _idx; @@ -19,9 +19,9 @@ public class CProjNode extends CFGNode implements MultiUse { _idx = idx; _label = label; } + public CProjNode(CProjNode c) { super(c); _idx = c._idx; _label = c._label; } @Override public String label() { return _label; } - @Override public int idx() { return _idx; } @Override StringBuilder _print1(StringBuilder sb, BitSet visited) { return sb.append(_label); } diff --git a/src/main/java/com/seaofnodes/simple/node/CallEndNode.java b/src/main/java/com/seaofnodes/simple/node/CallEndNode.java index 07a2b5a3..4e50c1fc 100644 --- a/src/main/java/com/seaofnodes/simple/node/CallEndNode.java +++ b/src/main/java/com/seaofnodes/simple/node/CallEndNode.java @@ -1,8 +1,6 @@ package com.seaofnodes.simple.node; -import com.seaofnodes.simple.IterPeeps; -import com.seaofnodes.simple.Parser; -import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.*; import com.seaofnodes.simple.type.*; import java.util.BitSet; @@ -14,7 +12,8 @@ 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); } + public CallEndNode(CallNode call) { super(new Node[]{call}); } + public CallEndNode(CallEndNode cend) { super(cend); } @Override public String label() { return "CallEnd"; } @@ -46,8 +45,7 @@ public class CallEndNode extends CFGNode implements MultiNode { // Trivial inlining: call site calls a single function; single function // is only called by this call site. - if( !_folding && nIns()==2 ) { - CallNode call = call(); + if( !_folding && nIns()==2 && in(0) instanceof CallNode call ) { Node fptr = call.fptr(); if( fptr.nOuts() == 1 && // Only user is this call fptr instanceof ConstantNode && // We have an immediate call @@ -72,7 +70,7 @@ public class CallEndNode extends CFGNode implements MultiNode { fun.setDef(1,Parser.XCTRL); // No default/unknown StartNode caller fun.setDef(2,call.ctrl()); // Bypass the Call; fun.ret().setDef(3,null); // Return is folding also - IterPeeps.addAll(fun._outputs); + CodeGen.CODE.addAll(fun._outputs); return this; } } else { diff --git a/src/main/java/com/seaofnodes/simple/node/CallNode.java b/src/main/java/com/seaofnodes/simple/node/CallNode.java index 246398ee..a9ca852c 100644 --- a/src/main/java/com/seaofnodes/simple/node/CallNode.java +++ b/src/main/java/com/seaofnodes/simple/node/CallNode.java @@ -17,17 +17,14 @@ public class CallNode extends CFGNode { public final Parser.Lexer _loc; public CallNode(Parser.Lexer loc, Node... nodes) { super(nodes); _loc = loc; } + public CallNode(CallNode call) { super(call); _loc = call._loc; } @Override public String label() { return "Call"; } - @Override - StringBuilder _print1(StringBuilder sb, BitSet visited) { - String fname = null; - Node fptr = fptr(); - if( fptr._type instanceof TypeFunPtr tfp && tfp.isConstant() ) - fname = tfp._name; - if( fname==null ) fptr._print0(sb,visited); + @Override StringBuilder _print1(StringBuilder sb, BitSet visited) { + String fname = name(); + if( fname == null ) fptr()._print0(sb,visited); else sb.append(fname); sb.append("( "); for( int i=2; i * The Constant's value is the value stored in it. */ + public class ConstantNode extends Node { public final Type _con; public ConstantNode( Type type ) { - super(Parser.START); - _con = type; + super(new Node[]{CodeGen.CODE._start}); + _con = _type = type; } + public ConstantNode( ConstantNode con ) { super(con); _con = con._type; } public static Node make( Type type ) { if( type==Type. CONTROL ) return new CtrlNode(); @@ -39,6 +42,11 @@ public class ConstantNode extends Node { @Override StringBuilder _print1(StringBuilder sb, BitSet visited) { + if( _con instanceof TypeFunPtr tfp ) { + FunNode fun = CodeGen.CODE.link(tfp); + if( fun._name != null ) + return sb.append("{ ").append(fun._name).append("}"); + } return sb.append(_con.print(new SB())); } diff --git a/src/main/java/com/seaofnodes/simple/node/CtrlNode.java b/src/main/java/com/seaofnodes/simple/node/CtrlNode.java index 162417bb..0ea060bd 100644 --- a/src/main/java/com/seaofnodes/simple/node/CtrlNode.java +++ b/src/main/java/com/seaofnodes/simple/node/CtrlNode.java @@ -1,11 +1,12 @@ package com.seaofnodes.simple.node; +import com.seaofnodes.simple.CodeGen; import com.seaofnodes.simple.Parser; import com.seaofnodes.simple.type.Type; import java.util.BitSet; public class CtrlNode extends CFGNode { - public CtrlNode() { super(Parser.START); } + public CtrlNode() { super(CodeGen.CODE._start); } @Override public String label() { return "Ctrl"; } @Override StringBuilder _print1(StringBuilder sb, BitSet visited) { return sb.append("Cctrl"); } @Override public boolean isConst() { return true; } diff --git a/src/main/java/com/seaofnodes/simple/node/FunNode.java b/src/main/java/com/seaofnodes/simple/node/FunNode.java index 1f0c76eb..506e7758 100644 --- a/src/main/java/com/seaofnodes/simple/node/FunNode.java +++ b/src/main/java/com/seaofnodes/simple/node/FunNode.java @@ -4,6 +4,7 @@ import com.seaofnodes.simple.*; import com.seaofnodes.simple.type.Type; import com.seaofnodes.simple.type.TypeFunPtr; import java.util.BitSet; +import static com.seaofnodes.simple.CodeGen.CODE; public class FunNode extends RegionNode { @@ -13,10 +14,13 @@ public class FunNode extends RegionNode { private TypeFunPtr _sig; // Initial signature private ReturnNode _ret; // Return pointer - public FunNode( Parser.Lexer loc, StartNode start, TypeFunPtr sig ) { super(loc,null,start); _sig=sig; } + public String _name; // Debug name + + public FunNode( Parser.Lexer loc, TypeFunPtr sig, Node... nodes ) { super(loc,nodes); _sig = sig; } + public FunNode( FunNode fun ) { super( fun, fun._loc ); _sig = fun.sig(); _name = fun._name; } @Override - public String label() { return _sig._name == null ? "$fun" : _sig._name; } + public String label() { return _name == null ? "$fun" : _name; } // Find the one CFG user from Fun. It's not always the Return, but always // the Return *is* a CFG user of Fun. @@ -45,9 +49,10 @@ public class FunNode extends RegionNode { public TypeFunPtr sig() { return _sig; } void setSig( TypeFunPtr sig ) { assert sig.isa(_sig); - if( _sig != sig ) - IterPeeps.add(this); - _sig = sig; + if( _sig != sig ) { + CODE.add(this); + _sig = sig; + } } @Override @@ -58,6 +63,15 @@ public class FunNode extends RegionNode { @Override public Node idealize() { + + // Some linked path dies + Node progress = deadPath(); + if( progress!=null ) { + if( nIns()==3 && in(2) instanceof CallNode call ) + CODE.add(call.cend()); // If Start and one call, check for inline + return progress; + } + // When can we assume no callers? Or no other callers (except main)? // In a partial compilation, we assume Start gets access to any/all // top-level public structures and recursively what they point to. @@ -66,16 +80,18 @@ public class FunNode extends RegionNode { // In a total compilation, we can start from Start and keep things // more contained. - // If no default/unknown caller, use the normal RegionNode ideal rules // to collapse if( unknownCallers() ) return null; - Node progress = super.idealize(); - if( progress!=null ) { - IterPeeps.add( CodeGen.CODE._stop); - IterPeeps.add( _ret ); + + // If down to a single input, become that input + if( nIns()==2 && !hasPhi() ) { + CODE.add( CODE._stop ); // Stop will remove dead path + CODE.add( _ret ); // Return will compute to TOP control + return in(1); // Collapse if no Phis; 1-input Phis will collapse on their own } - return progress; + + return null; } // Bypass Region idom, always assume depth == 1, one more than Start diff --git a/src/main/java/com/seaofnodes/simple/node/IfNode.java b/src/main/java/com/seaofnodes/simple/node/IfNode.java index eafcdf5b..9e6d2dff 100644 --- a/src/main/java/com/seaofnodes/simple/node/IfNode.java +++ b/src/main/java/com/seaofnodes/simple/node/IfNode.java @@ -1,5 +1,6 @@ package com.seaofnodes.simple.node; +import com.seaofnodes.simple.CodeGen; import com.seaofnodes.simple.IterPeeps; import com.seaofnodes.simple.type.*; import java.util.BitSet; @@ -9,8 +10,9 @@ public class IfNode extends CFGNode implements MultiNode { public IfNode(Node ctrl, Node pred) { super(ctrl, pred); - IterPeeps.add(this); // Because idoms are complex, just add it + CodeGen.CODE.add(this); // Because idoms are complex, just add it } + public IfNode(IfNode iff) { super(iff); } @Override public String label() { return "If"; } @@ -34,7 +36,6 @@ public class IfNode extends CFGNode implements MultiNode { // If the If node is not reachable then neither is any following Proj if (ctrl()._type != Type.CONTROL && ctrl()._type != Type.BOTTOM ) return TypeTuple.IF_NEITHER; - if( _disablePeephole ) return TypeTuple.IF_BOTH; Node pred = pred(); Type t = pred._type; // High types mean NEITHER side is reachable. @@ -59,7 +60,7 @@ public class IfNode extends CFGNode implements MultiNode { if( !pred()._type.isHighOrConst() ) for( CFGNode dom = idom(), prior=this; dom!=null; prior = dom, dom = dom.idom() ) if( dom.addDep(this) instanceof IfNode iff && iff.pred().addDep(this)==pred() && prior instanceof CProjNode prj ) { - setDef(1,new ConstantNode(TypeInteger.constant(prj._idx==0?1:0)).peephole()); + setDef(1,con( prj._idx==0 ? 1 : 0 )); return this; } return null; diff --git a/src/main/java/com/seaofnodes/simple/node/LoadNode.java b/src/main/java/com/seaofnodes/simple/node/LoadNode.java index eee5169d..6c74042b 100644 --- a/src/main/java/com/seaofnodes/simple/node/LoadNode.java +++ b/src/main/java/com/seaofnodes/simple/node/LoadNode.java @@ -47,31 +47,38 @@ public class LoadNode extends MemOpNode { @Override public Node idealize() { Node ptr = ptr(); + Node mem = mem(); // Simple Load-after-Store on same address. - if( mem() instanceof StoreNode st && + if( mem instanceof StoreNode st && ptr == st.ptr() && off() == st.off() ) { // Must check same object assert _name.equals(st._name); // Equiv class aliasing is perfect - return st.val(); + return extend(st.val()); } // Simple Load-after-New on same address. - if( mem() instanceof ProjNode p && p.in(0) instanceof NewNode nnn && + if( mem instanceof ProjNode p && p.in(0) instanceof NewNode nnn && ptr == nnn.proj(1) ) // Must check same object - return nnn.in(nnn.findAlias(_alias)); // Load from New init + return zero(nnn); // Load zero from new + + // Uplift control to a prior dominating load. + for( Node memuse : mem._outputs ) + // Find a prior load, has same mem,ptr,off but higher ctrl + if( memuse != this && memuse instanceof LoadNode ld && ptr==ld.ptr() && off()==ld.off() && + cfg0()!=null && cfg0()._idom(ld.cfg0(),this) == ld.cfg0() ) // Higher control means load is legal earlier + return ld; // Load-after-Store on same address, but bypassing provably unrelated // stores. This is a more complex superset of the above two peeps. // "Provably unrelated" is really weak. if( ptr instanceof ReadOnlyNode ro ) ptr = ro.in(1); - Node mem = mem(); outer: while( true ) { switch( mem ) { case StoreNode st: if( ptr == st.ptr().addDep(this) && off() == st.off() ) - return castRO(st.val()); // Proved equal + return extend(castRO(st.val())); // Proved equal // Can we prove unequal? Offsets do not overlap? if( !off()._type.join(st.off()._type).isHigh() && // Offsets overlap !neverAlias(ptr,st.ptr()) ) // And might alias @@ -88,10 +95,10 @@ public class LoadNode extends MemOpNode { switch( mproj.in(0) ) { case NewNode nnn1: if( ptr instanceof ProjNode pproj && pproj.in(0) == mproj.in(0) ) - return castRO(nnn1.in(nnn1.findAlias(_alias))); // Load from New init - if( !(ptr instanceof ProjNode pproj && pproj.in(0) instanceof NewNode nnn2) ) + return zero(nnn1); + if( !(ptr instanceof ProjNode pproj && pproj.in(0) instanceof NewNode) ) break outer; // Cannot tell, ptr not related to New - mem = nnn1.in(_alias);// Bypass unrelated New + mem = nnn1.in(nnn1.findAlias(_alias));// Bypass unrelated New break; case StartNode start: break outer; case CallEndNode cend: break outer; // TODO: Bypass no-alias call @@ -131,6 +138,13 @@ public class LoadNode extends MemOpNode { return null; } + // Load a flavored zero from a New + private Node zero(NewNode nnn) { + TypeStruct ts = nnn._ptr._obj; + Type zero = ts._fields[ts.findAlias(_alias)]._type.makeZero(); + return castRO(new ConstantNode(zero).peephole()); + } + private Node ld( int idx ) { Node mem = mem(), ptr = ptr(); return new LoadNode(_loc,_name,_alias,_declaredType,mem.in(idx),ptr instanceof PhiNode && ptr.in(0)==mem.in(0) ? ptr.in(idx) : ptr, off()).peephole(); @@ -178,4 +192,19 @@ public class LoadNode extends MemOpNode { return new ReadOnlyNode(rez).peephole(); return rez; } + + // When a load bypasses a store, the store might truncate bits - and the + // load will need to zero/sign-extend. + private Node extend(Node val) { + if( !(_declaredType instanceof TypeInteger ti) ) return val; + if( ti._min==0 ) // Unsigned + return new AndNode(null,val,con(ti._max)); + // Signed extension + int shift = Long.numberOfLeadingZeros(ti._max)-1; + Node shf = con(shift); + if( shf._type==TypeInteger.ZERO ) + return val; + //return peep(new SarNode(null,peep(new ShlNode(null,val,shf.keep())),shf.unkeep())); + throw Utils.TODO(); + } } diff --git a/src/main/java/com/seaofnodes/simple/node/LoopNode.java b/src/main/java/com/seaofnodes/simple/node/LoopNode.java index f70a1012..d2b0e36c 100644 --- a/src/main/java/com/seaofnodes/simple/node/LoopNode.java +++ b/src/main/java/com/seaofnodes/simple/node/LoopNode.java @@ -10,6 +10,7 @@ import java.util.HashSet; public class LoopNode extends RegionNode { public LoopNode( Parser.Lexer loc, Node entry ) { super(loc,null,entry,null); } + public LoopNode( LoopNode loop ) { super(loop); } public CFGNode entry() { return cfg(1); } public CFGNode back () { return cfg(2); } diff --git a/src/main/java/com/seaofnodes/simple/node/MachConcreteNode.java b/src/main/java/com/seaofnodes/simple/node/MachConcreteNode.java new file mode 100644 index 00000000..0c49fc64 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/MachConcreteNode.java @@ -0,0 +1,27 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.Utils; +import java.io.ByteArrayOutputStream; +import java.util.BitSet; + +// Generic machine-specific class, has a few Node implementations that have to +// exist (abstract) but are not useful past the optimizer. +public abstract class MachConcreteNode extends Node implements MachNode{ + + public MachConcreteNode(Node node) { super(node); } + public MachConcreteNode(Node[]nodes) { super(nodes); } + + @Override public String label() { return op(); } + @Override public Type compute () { throw Utils.TODO(); } + @Override public Node idealize() { throw Utils.TODO(); } + + @Override public StringBuilder _print1(StringBuilder sb, BitSet visited) { + sb.append("(").append(op()).append(","); + for( int i=1; i(Node.class); Collections.addAll(_inputs,inputs); _outputs = new Ary<>(Node.class); @@ -66,6 +60,18 @@ public abstract class Node { n.addUse( this ); } + // Make a Node using the existing arrays of nodes. + // Used by any pass rewriting all Node classes but not the edges. + Node( Node n ) { + assert CodeGen.CODE._phase == CodeGen.Phase.InstructionSelection; + _nid = CODE.getUID(); // allocate unique dense ID + _inputs = new Ary<>(n._inputs.asAry()); + _outputs = new Ary<>(Node.class); + _type = n._type; + _deps = null; + _hash = 0; + } + // Easy reading label for debugger, e.g. "Add" or "Region" or "EQ" public abstract String label(); @@ -79,6 +85,9 @@ public abstract class Node { // Graphical label, e.g. "+" or "Region" or "==" public String glabel() { return label(); } + // Extra fun stuff, for assembly printing. Jump labels, parser locations, + // variable types, etc. + public String comment() { return null; } // ------------------------------------------------------------------------ @@ -163,10 +172,10 @@ public abstract class Node { 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 - 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 + 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 CODE.add(old_def); // Else old lost a use, so onto worklist } moveDepsToWorklist(); // Return new_def for easy flow-coding @@ -246,11 +255,9 @@ public abstract class Node { _type=null; // Flag as dead while( nIns()>0 ) { // Set all inputs to null, recursively killing unused Nodes Node old_def = _inputs.removeLast(); - if( old_def != null ) { - IterPeeps.add(old_def);// Revisit neighbor because removed use - if( old_def.delUse(this) ) // If we removed the last use, the old def is now dead - old_def.kill(); // Kill old def - } + // Revisit neighbor because removed use + if( old_def != null && CODE.add(old_def).delUse(this) ) + old_def.kill(); // If we removed the last use, the old def is now dead } assert isDead(); // Really dead now } @@ -283,7 +290,7 @@ public abstract class Node { int idx = n._inputs.find(this); n._inputs.set(idx,nnn); nnn.addUse(n); - IterPeeps.addAll(n._outputs); + CODE.addAll(n._outputs); } kill(); } @@ -291,12 +298,6 @@ public abstract class Node { // ------------------------------------------------------------------------ // Graph-based optimizations - /** - * We allow disabling peephole opt so that we can observe the - * full graph, vs the optimized graph. - */ - public static boolean _disablePeephole = false; - /** * Try to peephole at this node and return a better replacement Node. * Always returns some not-null Node (often this). @@ -329,28 +330,22 @@ public abstract class Node { * */ public final Node peepholeOpt( ) { - ITER_CNT++; + CODE.iterCnt(); // Compute initial or improved Type Type old = setType(compute()); - // 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(); + if( _type.isHighOrConst() && !isConst() ) + return ConstantNode.make(_type).peephole(); // Global Value Numbering if( _hash==0 ) { - Node n = GVN.get(this); // Will set _hash as a side effect + Node n = CODE._gvn.get(this); // Will set _hash as a side effect if( n==null ) - GVN.put(this,this); // Put in table now + CODE._gvn.put(this,this); // Put in table now else { // Because of random worklist ordering, the two equal nodes // might have different types. Because of monotonicity, both @@ -368,10 +363,9 @@ public abstract class Node { return n; // Report progress if( old!=_type ) return this; // Report progress; - ITER_NOP_CNT++; + CODE.iterNop(); return null; // No progress } - public static int ITER_CNT, ITER_NOP_CNT; // m is the new Node, self is the old. // Return 'm', which may have zero uses but is alive nonetheless. @@ -415,7 +409,7 @@ public abstract class Node { assert old==null || type.isa(old); // Since _type not set, can just re-run this in assert in the debugger if( old == type ) return old; _type = type; // Set _type late for easier assert debugging - IterPeeps.addAll(_outputs); + CODE.addAll(_outputs); moveDepsToWorklist(); return old; } @@ -485,7 +479,8 @@ public abstract class Node { Node addDep( Node dep ) { // Running peepholes during the big assert cannot have side effects // like adding dependencies. - if( IterPeeps.midAssert() ) return this; + if( CODE._midAssert ) return this; + if( dep == null ) return this; if( _deps==null ) _deps = new Ary<>(Node.class); if( _deps .find(dep) != -1 ) return this; // Already on list if( _inputs .find(dep) != -1 ) return this; // No need for deps on immediate neighbors @@ -497,15 +492,11 @@ public abstract class Node { // Move the dependents onto a worklist, and clear for future dependents. public void moveDepsToWorklist( ) { if( _deps==null ) return; - IterPeeps.addAll(_deps); + CODE.addAll(_deps); _deps.clear(); } - // Global Value Numbering. Hash over opcode and inputs; hits in this table - // are structurally equal. - public static final HashMap GVN = new HashMap<>(); - // Two nodes are equal if they have the same inputs and the same "opcode" // which means the same Java class, plus same internal parts. @Override public final boolean equals( Object o ) { @@ -534,7 +525,7 @@ public abstract class Node { // If the _hash is set, then the Node is in the GVN table; remove it. void unlock() { if( _hash==0 ) return; - Node old = GVN.remove(this); // Pull from table + Node old = CODE._gvn.remove(this); // Pull from table assert old==this; _hash=0; // Out of table now } @@ -618,31 +609,21 @@ public abstract class Node { // Report any post-optimize errors public Parser.ParseException err() { return null; } - /** - * Used to allow repeating tests in the same JVM. This just resets the - * Node unique id generator, and is done as part of making a new Parser. - */ - public static void reset() { - UNIQUE_ID = 1; - _disablePeephole=false; - GVN.clear(); - ITER_CNT = ITER_NOP_CNT = 0; - } - + // Common integer constants + public static ConstantNode con(long x) { return (ConstantNode)(new ConstantNode(TypeInteger.constant(x)).peephole()); } // Utility to walk the entire graph applying a function; return the first // not-null result. - private static final BitSet WVISIT = new BitSet(); final public E walk( Function pred ) { - assert WVISIT.isEmpty(); + assert CODE._visit.isEmpty(); E rez = _walk(pred); - WVISIT.clear(); + CODE._visit.clear(); return rez; } private E _walk( Function pred ) { - if( WVISIT.get(_nid) ) return null; // Been there, done that - WVISIT.set(_nid); + if( CODE._visit.get(_nid) ) return null; // Been there, done that + CODE._visit.set(_nid); E x = pred.apply(this); if( x != null ) return x; for( Node def : _inputs ) if( def != null && (x = def._walk(pred)) != null ) return x; diff --git a/src/main/java/com/seaofnodes/simple/node/ParmNode.java b/src/main/java/com/seaofnodes/simple/node/ParmNode.java index 784b9146..8d7c4205 100644 --- a/src/main/java/com/seaofnodes/simple/node/ParmNode.java +++ b/src/main/java/com/seaofnodes/simple/node/ParmNode.java @@ -13,6 +13,7 @@ public class ParmNode extends PhiNode { super(label,declaredType,inputs); _idx = idx; } + public ParmNode(ParmNode parm) { super(parm, parm._label, parm._declaredType); _idx = parm._idx; } @Override public String label() { return MemOpNode.mlabel(_label); } @@ -22,7 +23,7 @@ public class ParmNode extends PhiNode { @Override StringBuilder _print1(StringBuilder sb, BitSet visited) { - if( "main".equals(fun().sig()._name) && _label.equals("arg") ) + if( "main".equals(fun()._name) && _label.equals("arg") ) return sb.append("arg"); sb.append("Parm_").append(_label).append("("); for( Node in : _inputs ) { diff --git a/src/main/java/com/seaofnodes/simple/node/PhiNode.java b/src/main/java/com/seaofnodes/simple/node/PhiNode.java index 9aed8287..17748afe 100644 --- a/src/main/java/com/seaofnodes/simple/node/PhiNode.java +++ b/src/main/java/com/seaofnodes/simple/node/PhiNode.java @@ -13,9 +13,11 @@ public class PhiNode extends Node { final Type _declaredType; public PhiNode(String label, Type declaredType, Node... inputs) { super(inputs); _label = label; assert declaredType!=null; _declaredType = declaredType; } + public PhiNode(PhiNode phi, String label, Type declaredType) { super(phi); _label = label; _declaredType = declaredType; } + public PhiNode(PhiNode phi) { super(phi); _label = phi._label; _declaredType = phi._declaredType; } public PhiNode(RegionNode r, Node sample) { - super(r); + super(new Node[]{r}); _label = ""; _declaredType = sample._type; while( nIns() < r.nIns() ) @@ -73,9 +75,6 @@ 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 RegMask.EMPTY; + case 1 -> RegMask.EMPTY; + case 2 -> x86_64_v2.RET_MASK; + case 3 -> RegMask.EMPTY; // RPC is always on stack + default -> throw Utils.TODO(); + }; + } + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return RegMask.EMPTY; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + // Human-readable form appended to the SB. Things like the encoding, + // indentation, leading address or block labels not printed here. + // Just something like "ld4\tR17=[R18+12] // Load array base". + // General form: "op\tdst=src+src" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(in(2))); + } + + @Override public String op() { return "ret"; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SarX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SarX86.java new file mode 100644 index 00000000..3d907eef --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SarX86.java @@ -0,0 +1,40 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeInteger; +import java.io.ByteArrayOutputStream; +import java.util.BitSet; +import java.lang.StringBuilder; + + +public class SarX86 extends MachConcreteNode implements MachNode { + final TypeInteger _ti; + SarX86(Node sar, TypeInteger ti) {super(sar); _inputs.pop(); _ti = ti;} + + // Register mask allowed on input i. + // This is the normal calling convention + @Override public RegMask regmap(int i) { + // assert i==1; + return x86_64_v2.RMASK; } + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return x86_64_v2.WMASK; } + + // Output is same register as input#1 + @Override public int twoAddress() { return 1; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + // General form + // General form: "sari dst << #imm" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" >> #"); + _ti.print(sb); + } + + @Override public String op() { return "sari"; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SetX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SetX86.java new file mode 100644 index 00000000..25367e03 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SetX86.java @@ -0,0 +1,39 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeInteger; +import java.io.ByteArrayOutputStream; + +// Compare immediate. Sets flags. +// Corresponds to the x86 instruction "sete && setne". +// Use result of comparison without jump. +public class SetX86 extends MachConcreteNode implements MachNode { + final String _bop; // One of <,<=,== + // Constructor expects input is an X86 and not an Ideal node. + SetX86( Node cmp, String bop ) { + super(cmp); + _inputs.setLen(1); // Pop the cmp inputs + // Replace with the matched cmp + _inputs.push(cmp); + _bop = bop; + } + + @Override public RegMask regmap(int i) { assert i==1; return x86_64_v2.FLAGS_MASK; } + @Override public RegMask outregmap() { return x86_64_v2.WMASK; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)); + String src = code.reg(in(1)); + if( src!="FLAGS" ) sb.p(" = ").p(src); + } + + @Override public String op() { return "set"+_bop; } + +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlIX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlIX86.java new file mode 100644 index 00000000..61e0d262 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlIX86.java @@ -0,0 +1,38 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeInteger; +import java.io.ByteArrayOutputStream; +import java.util.BitSet; + +public class ShlIX86 extends MachConcreteNode implements MachNode{ + final TypeInteger _ti; + ShlIX86(Node shl, TypeInteger ti) {super(shl); _inputs.pop(); _ti = ti;} + + // Register mask allowed on input i. + // This is the normal calling convention + @Override public RegMask regmap(int i) { + //assert i==1; + return x86_64_v2.RMASK; } + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return x86_64_v2.WMASK; } + + // Output is same register as input#1 + @Override public int twoAddress() { return 1; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + // General form + // General form: "shli dst << #imm" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" << #"); + _ti.print(sb); + } + + @Override public String op() { return "shli"; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrIX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrIX86.java new file mode 100644 index 00000000..08cae93e --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrIX86.java @@ -0,0 +1,39 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeInteger; +import java.io.ByteArrayOutputStream; +import java.util.BitSet; +import java.lang.StringBuilder; + +public class ShrIX86 extends MachConcreteNode implements MachNode{ + final TypeInteger _ti; + ShrIX86(Node shr, TypeInteger ti) {super(shr); _inputs.pop(); _ti = ti;} + + // Register mask allowed on input i. + // This is the normal calling convention + @Override public RegMask regmap(int i) { + //assert i==1; + return x86_64_v2.RMASK; } + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return x86_64_v2.WMASK; } + + // Output is same register as input#1 + @Override public int twoAddress() { return 1; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + // General form + // General form: "shri dst >>> #imm" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" >>> #"); + _ti.print(sb); + } + + @Override public String op() { return "shri"; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/StoreX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/StoreX86.java new file mode 100644 index 00000000..c9191b87 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/StoreX86.java @@ -0,0 +1,30 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.CodeGen; +import com.seaofnodes.simple.RegMask; +import com.seaofnodes.simple.SB; +import com.seaofnodes.simple.node.Node; +import com.seaofnodes.simple.node.StoreNode; +import java.util.BitSet; + +public class StoreX86 extends MemOpX86 { + StoreX86( StoreNode st, Node base, Node idx, int off, int scale, int imm, Node val ) { + super(st,st, base, idx, off, scale, imm, val); + } + + @Override public String op() { return "st"+_sz; } + + @Override public StringBuilder _printMach(StringBuilder sb, BitSet visited) { + return sb.append(".").append(_name).append("=").append(val()==null ? _imm : val()).append(";"); + } + + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return RegMask.EMPTY; } + + // General form: "stN [base + idx<<2 + 12],val" + @Override public void asm(CodeGen code, SB sb) { + asm_address(code,sb).p(","); + if( val()==null ) sb.p("#").p(_imm); + else sb.p(code.reg(val())); + } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubFX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubFX86.java new file mode 100644 index 00000000..ed19ad79 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubFX86.java @@ -0,0 +1,29 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.*; +import java.io.ByteArrayOutputStream; + +public class SubFX86 extends MachConcreteNode implements MachNode { + SubFX86( Node subf) { super(subf); } + + + // Register mask allowed on input i. + @Override public RegMask regmap(int i) { assert i==1 || i==2; return x86_64_v2.XMASK; } + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return x86_64_v2.XMASK; } + // Output is same register as input#1 + @Override public int twoAddress() { return 1; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + // General form: "subf dst -= src" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" - ").p(code.reg(in(2))); + } + + @Override public String op() { return "subf"; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubX86.java new file mode 100644 index 00000000..cac59d3b --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubX86.java @@ -0,0 +1,29 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.TypeInteger; +import java.io.ByteArrayOutputStream; + +public class SubX86 extends MachConcreteNode implements MachNode { + SubX86( Node sub ) { super(sub); } + + // Register mask allowed on input i. + @Override public RegMask regmap(int i) { assert i==1 || i==2; return x86_64_v2.RMASK; } + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return x86_64_v2.WMASK; } + // Output is same register as input#1 + @Override public int twoAddress() { return 1; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + // General form: "sub dst -= src" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" - ").p(code.reg(in(2))); + } + + @Override public String op() { return "sub"; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TFPX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TFPX86.java new file mode 100644 index 00000000..bd88011f --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TFPX86.java @@ -0,0 +1,41 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.ConstantNode; +import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeFunPtr; + +import java.io.ByteArrayOutputStream; + +// Function constants +public class TFPX86 extends ConstantNode implements MachNode { + TFPX86( ConstantNode con ) { super(con); } + // Register mask allowed on input i. 0 for no register. + @Override public RegMask regmap(int i) { return RegMask.EMPTY; } + // General int registers + @Override public RegMask outregmap() { return x86_64_v2.WMASK; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + // Human-readable form appended to the SB. Things like the encoding, + // indentation, leading address or block labels not printed here. + // Just something like "ld4\tR17=[R18+12] // Load array base". + // General form: "op\tdst=src+src" + @Override public void asm(CodeGen code, SB sb) { + String reg = code.reg(this); + if( _con == Type.NIL ) + sb.p(reg).p(",").p(reg); + else + _con.print(sb.p(reg).p(" #")); + } + + @Override public String op() { + if( _con == Type.NIL ) + return "xor"; + return "ldx"; // Some fancier encoding + } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/XorIX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/XorIX86.java new file mode 100644 index 00000000..795fa814 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/XorIX86.java @@ -0,0 +1,42 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.CodeGen; +import com.seaofnodes.simple.RegMask; +import com.seaofnodes.simple.SB; +import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.node.MachConcreteNode; +import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.node.Node; +import com.seaofnodes.simple.type.TypeInteger; + +import java.io.ByteArrayOutputStream; + +public class XorIX86 extends MachConcreteNode implements MachNode { + + final TypeInteger _ti; + XorIX86(Node xor, TypeInteger ti) {super(xor); _inputs.pop(); _ti = ti;} + + // Register mask allowed on input i. + // This is the normal calling convention + @Override public RegMask regmap(int i) { assert i==1; return x86_64_v2.WMASK; } + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return x86_64_v2.WMASK; } + + + // Output is same register as input#1 + @Override public int twoAddress() { return 1; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + // General form + // General form: "ori xori ^ #imm" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" ^ #"); + _ti.print(sb); + } + + @Override public String op() { return "xori"; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java new file mode 100644 index 00000000..45ae19dc --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java @@ -0,0 +1,418 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.CodeGen; +import com.seaofnodes.simple.Machine; +import com.seaofnodes.simple.RegMask; +import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.*; + +public class x86_64_v2 extends Machine { + // X86-64 V2. Includes e.g. SSE4.2 and POPCNT. + @Override public String name() { return "x86_64_v2"; } + + public static int RAX = 0, RCX = 1, RDX = 2, RBX = 3, RSP = 4, RBP = 5, RSI = 6, RDI = 7; + public static int R08 = 8, R09 = 9, R10 = 10, R11 = 11, R12 = 12, R13 = 13, R14 = 14, R15 = 15; + public static int FLAGS = 32; + + public static int XMM0 = 16, XMM1 = 17, XMM2 = 18, XMM3 = 19, XMM4 = 20, XMM5 = 21, XMM6 = 22, XMM7 = 23; + public static int XMM8 = 24, XMM9 = 25, XMM10 = 26, XMM11 = 27, XMM12 = 28, XMM13 = 29, XMM14 = 30, XMM15 = 31; + + // General purpose register mask: pointers and ints, not floats + public static RegMask RMASK = new RegMask(0b1111111111111111); + // No RSP in the *write* general set. + public static RegMask WMASK = new RegMask(0b1111111111101111); + // Xmm register mask + public static RegMask XMASK = new RegMask( 0b1111111111111111L << XMM0); + + public static RegMask FLAGS_MASK = new RegMask(1L< callInArgSystemV(idx); + case CodeGen.CallingConv.Win64 -> callInArgWin64 (idx); + }; + } + + // WIN64(param passing) + static RegMask[] CALLINMASK_WIN64 = new RegMask[] { + RegMask.EMPTY, + RegMask.EMPTY, + RCX_MASK, + RDX_MASK, + R08_MASK, + R09_MASK, + RegMask.EMPTY, + RegMask.EMPTY + }; + static int[] CALLINARG_WIN64 = new int[] { + 0, // Control, no register + 0, // Memory, no register + RCX, // + RDX, + R08, + R09, + 0, + 0, + }; + static int callInArgWin64( int idx ) { return CALLINARG_WIN64[idx]; } + + // caller saved(win64) + public static final long WIN64_ABI_CALLER_SAVED = + (1L << RAX) | (1L << RCX) | (1L << RDX) | (1L << R08) | (1L << R09) | (1L << R10) | (1L << R11); + + // callee saved(win64) + public static final long WIN64_ABI_CALLEE_SAVED = ~WIN64_ABI_CALLER_SAVED; + + // SystemV(param passing) + static RegMask[] CALLINMASK_SYSTEMV = new RegMask[] { + RegMask.EMPTY, + RegMask.EMPTY, + RDI_MASK, + RSI_MASK, + RDX_MASK, + RCX_MASK, + R08_MASK, + R09_MASK + }; + static int[] CALLINARG_SYSTEMV = new int[] { + 0, // Control, no register + 0, // Memory, no register + RDI, + RSI, + RDX, + RCX, + R08, + R09, + }; + static int callInArgSystemV( int idx ) { return CALLINARG_SYSTEMV[idx]; } + + // caller saved(systemv) + // caller saved(win64) + public static final long SYSTEMV_ABI_CALLER_SAVED = + (1L << RAX) | (1L << RDI) | (1L << RSI) | (1L << RCX) | (1L << RDX) | (1L << R08) | (1L << R09) << (1L << R10) << (1L << R11); + // callee saved(systemv) + public static final long SYSTEMV_ABI_CALLE_SAVED = ~SYSTEMV_ABI_CALLER_SAVED; + + + static RegMask callInMask( int idx ) { + return switch( CodeGen.CODE._callingConv ) { + case CodeGen.CallingConv.SystemV -> CALLINMASK_SYSTEMV[idx]; + case CodeGen.CallingConv.Win64 -> CALLINMASK_WIN64 [idx]; + }; + } + + // Create a split op; any register to any register, including stack slots + @Override public Node split() { + throw Utils.TODO(); + } + + // Return a MachNode unconditional branch + @Override public CFGNode jump() { + throw Utils.TODO(); + } + + // Break an infinite loop + @Override public IfNode never( CFGNode ctrl ) { + throw Utils.TODO(); + } + + // Instruction selection + @Override public Node instSelect( Node n ) { + return switch( n ) { + case AddFNode addf -> addf(addf); + case AddNode add -> add(add); + case AndNode and -> and(and); + case BoolNode bool -> cmp(bool); + case CallEndNode cend -> new CallEndNode((CallNode)cend.in(0)); + case CallNode call -> call(call); + case CProjNode c -> new CProjNode(c); + case ConstantNode con -> con(con); + case DivFNode divf -> new DivFX86(divf); + case DivNode div -> new DivX86(div); + case FunNode fun -> new FunX86(fun); + case IfNode iff -> jmp(iff); + case LoadNode ld -> ld(ld); + case MemMergeNode mem -> new MemMergeNode(mem); + case MulFNode mulf -> new MulFX86(mulf); + case MulNode mul -> mul(mul); + case NewNode nnn -> new NewX86(nnn); + case OrNode or -> or(or); + case ParmNode parm -> new ParmX86(parm); + case PhiNode phi -> new PhiNode(phi); + case ProjNode prj -> prj(prj); + case ReadOnlyNode read -> new ReadOnlyNode(read); + case ReturnNode ret -> new RetX86(ret,ret.fun()); + case SarNode sar -> sar(sar); + case ShlNode shl -> shl(shl); + case ShrNode shr -> shr(shr); + case StartNode start -> new StartNode(start); + case StopNode stop -> new StopNode(stop); + case StoreNode st -> st(st); + case SubFNode subf -> new SubFX86(subf); + case SubNode sub -> sub(sub); + case ToFloatNode tfn -> i2f8(tfn); + case XorNode xor -> xor(xor); + + case LoopNode loop -> new LoopNode(loop); + case RegionNode region-> new RegionNode(region); + default -> throw Utils.TODO(); + }; + } + + + // Attempt a full LEA-style break down. + private Node add( AddNode add ) { + Node lhs = add.in(1); + Node rhs = add.in(2); + if( lhs instanceof LoadNode ld && ld.nOuts()==1 ) + return new AddMemX86(add,address(ld),ld.ptr(),idx,off,scale, imm(rhs),val); + + if( rhs instanceof LoadNode ld && ld.nOuts()==1 ) + throw Utils.TODO(); // Swap load sides + + // Attempt a full LEA-style break down. + // Returns one of AddX86, AddIX86, LeaX86, or LHS + if( rhs instanceof ConstantNode off && off._con instanceof TypeInteger toff ) { + if( lhs instanceof AddNode ladd ) + // ((base + (idx << scale)) + off) + return _lea(add,ladd.in(1),ladd.in(2),toff.value()); + if( lhs instanceof ShlNode shift ) + // (idx << scale) + off; no base + return _lea(add,null,shift,toff.value()); + + // lhs + rhs1 + if( toff.value()==0 ) return add; + return new AddIX86(add, toff); + } + return _lea(add,lhs,rhs,0); + } + + + private Node addf( AddFNode addf ) { + if( addf.in(1) instanceof LoadNode ld && ld.nOuts()==1 ) + return new AddFMemX86(addf,address(ld),ld.ptr(),idx,off,scale, addf.in(2)); + + if( addf.in(2) instanceof LoadNode ld && ld.nOuts()==1 ) + throw Utils.TODO(); // Swap load sides + + return new AddFX86(addf); + } + + + private Node _lea( Node add, Node base, Node idx, long off ) { + int scale = 1; + if( base instanceof ShlNode && !(idx instanceof ShlNode) ) throw Utils.TODO(); // Bug in canonicalization, should on RHS + if( idx instanceof ShlNode shift && shift.in(2) instanceof ConstantNode shfcon && + shfcon._con instanceof TypeInteger tscale && 0 <= tscale.value() && tscale.value() <= 3 ) { + idx = shift.in(1); + scale = 1 << ((int)tscale.value()); + } + // (base + idx) + off + return off==0 && scale==1 + ? new AddX86(add) + : new LeaX86(add,base,idx,scale,off); + } + + + private Node and(AndNode and) { + if( and.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti ) + return new AndIX86(and, ti); + throw Utils.TODO(); + } + + private Node call(CallNode call) { + if( call.fptr() instanceof ConstantNode con && con._con instanceof TypeFunPtr tfp ) + return new CallX86(call, tfp); + return new CallRX86( call ); + } + + // Because X86 flags, a normal ideal Bool is 2 X86 ops: a "cmp" and at "setz". + // Ideal If reading from a setz will skip it and use the "cmp" instead. + private Node cmp( BoolNode bool ) { + Node cmp = _cmp(bool); + return new SetX86(cmp,bool.op()); + } + private Node _cmp( BoolNode bool ) { + Node lhs = bool.in(1); + Node rhs = bool.in(2); + if( lhs instanceof LoadNode ld && ld.nOuts()==1 ) + return new CmpMemX86(bool,address(ld),ld.ptr(),idx,off,scale, imm(rhs),val,false); + + if( rhs instanceof LoadNode ld && ld.nOuts()==1 ) + return new CmpMemX86(bool,address(ld),ld.ptr(),idx,off,scale, imm(lhs),val,true); + + return rhs instanceof ConstantNode con && con._con instanceof TypeInteger ti + ? new CmpIX86(bool, ti) + : new CmpX86(bool); + } + + private Node con( ConstantNode con ) { + return switch( con._con ) { + case TypeInteger ti -> new IntX86(con); + case TypeFloat tf -> new FltX86(con); + case TypeFunPtr tfp -> new TFPX86(con); + case TypeMemPtr tmp -> new ConstantNode(con); + case TypeNil tn -> throw Utils.TODO(); + // TOP, BOTTOM, XCtrl, Ctrl, etc. Never any executable code. + case Type t -> new ConstantNode(con); + }; + } + + private Node i2f8(ToFloatNode tfn) { + assert tfn.in(1)._type instanceof TypeInteger ti; + return new I2f8X86(tfn); + } + + private Node jmp( IfNode iff ) { + // If/Bool combos will match to a Cmp/Set which sets flags. + // Most general arith ops will also set flags, which the Jmp needs directly. + // Loads do not set the flags, and will need an explicit TEST + if( !(iff.in(1) instanceof BoolNode) ) + iff.setDef(1,new BoolNode.EQ(iff.in(1),new ConstantNode(TypeInteger.ZERO))); + return new JmpX86(iff, ((BoolNode)iff.in(1)).op()); + } + + private Node ld( LoadNode ld ) { + return new LoadX86(address(ld),ld.ptr(),idx,off,scale); + } + + private Node mul(MulNode mul) { + return mul.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti + ? new MulIX86(mul, ti) + : new MulX86(mul); + } + + private Node or(OrNode or) { + if( or.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti) + return new OrIX86(or, ti); + throw Utils.TODO(); + } + + private Node prj( ProjNode prj ) { + return new ProjX86(prj); + } + + private Node sar( SarNode sar ) { + if( sar.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti) + return new SarX86(sar, ti); + throw Utils.TODO(); + } + + private Node shl( ShlNode shl ) { + if( shl.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti ) + return new ShlIX86(shl, ti); + throw Utils.TODO(); + } + + private Node shr(ShrNode shr) { + if( shr.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti ) + return new ShrIX86(shr, ti); + throw Utils.TODO(); + } + + private Node st( StoreNode st ) { + // Look for "*ptr op= val" + Node op = st.val(); + if( op instanceof AddNode ) { + if( op.in(1) instanceof LoadNode ld && + ld.in(0)==st.in(0) && + ld.mem()==st.mem() && + ld.ptr()==st.ptr() && + ld.off()==st.off() ) { + if( op instanceof AddNode ) { + return new MemAddX86(address(st),st.ptr(),idx,off,scale,imm(op.in(2)),val); + } + throw Utils.TODO(); + } + } + + return new StoreX86(address(st),st.ptr(),idx,off,scale,imm(st.val()),val); + } + + private Node sub( SubNode sub ) { + return sub.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti + ? new AddIX86(sub, TypeInteger.constant(-ti.value())) + : new SubX86(sub); + } + + private Node xor(XorNode xor) { + if(xor.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti) + return new XorIX86(xor, ti); + throw Utils.TODO(); + } + + + // Gather X86 addressing mode bits prior to constructing. This is a + // builder pattern, but saving the bits in a *local* *global* here to keep + // mess contained. + private static int off, scale, imm; + private static Node idx, val; + private N address( N mop ) { + off = scale = imm = 0; // Reset + idx = val = null; + Node base = mop.ptr(); + // Skip/throw-away a ReadOnly, only used to typecheck + if( base instanceof ReadOnlyNode read ) base = read.in(1); + assert !(base instanceof AddNode) && base._type instanceof TypeMemPtr; // Base ptr always, not some derived + if( mop.off() instanceof AddNode add && add.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti ) { + off = (int)ti.value(); + assert off == ti.value(); // In 32-bit range + idx = add.in(1); + if( idx instanceof ShlNode shift && shift.in(2) instanceof ConstantNode shfcon && + shfcon._con instanceof TypeInteger tscale && 0 <= tscale.value() && tscale.value() <= 3 ) { + idx = shift.in(1); + scale = (int)tscale.value(); + } + } else { + if( mop.off() instanceof ConstantNode con && con._con instanceof TypeInteger ti ) { + off = (int)ti.value(); + assert off == ti.value(); // In 32-bit range + } else { + idx = mop.off(); + } + } + return mop; + } + private int imm( Node xval ) { + assert val==null && imm==0; + if( xval instanceof ConstantNode con && con._con instanceof TypeInteger ti ) { + val = null; + imm = (int)ti.value(); + assert imm == ti.value(); // In 32-bit range + } else { + val = xval; + } + return imm; + } + +} -- cgit v1.2.3