summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorCliff Click <cliffc@acm.org>2025-02-07 08:58:36 -0800
committerCliff Click <cliffc@acm.org>2026-10-08 16:52:27 -0700
commit18fd28edce813d36c491e42d9e64c53c7251d26e (patch)
treee54ac214fb1ff76d0758a6ed544396bcbcbfe256 /src
parent84a70fd744f83e2d701e15fc3d17109eb59ac707 (diff)
downloadSimple-18fd28edce813d36c491e42d9e64c53c7251d26e.tar.gz
Simple-18fd28edce813d36c491e42d9e64c53c7251d26e.zip
Chapter 19: Instruction Selection and Portable Compilation
Co-authored-by: Tamás Hadházy <85063808+Hels15@users.noreply.github.com> Co-authored-by: Hsiang-Ying Fu <66062782+fuhsnn@users.noreply.github.com> Co-authored-by: hadha <helsgithub15@gmail.com> Co-authored-by: godalming123 <68993177+godalming123@users.noreply.github.com> Co-authored-by: fuhsnn <66062782+fuhsnn@users.noreply.github.com> Co-authored-by: Symious <14933944+symious@users.noreply.github.com> Co-authored-by: RobertObkircher <robert.obkircher@gmail.com> Co-authored-by: Jun Kawasaki <jun784@gmail.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Diffstat (limited to 'src')
-rw-r--r--src/main/java/com/seaofnodes/simple/ASMPrinter.java51
-rw-r--r--src/main/java/com/seaofnodes/simple/Ary.java34
-rw-r--r--src/main/java/com/seaofnodes/simple/CodeGen.java234
-rw-r--r--src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java85
-rw-r--r--src/main/java/com/seaofnodes/simple/IRPrinter.java4
-rw-r--r--src/main/java/com/seaofnodes/simple/IterPeeps.java50
-rw-r--r--src/main/java/com/seaofnodes/simple/ListScheduler.java245
-rw-r--r--src/main/java/com/seaofnodes/simple/Machine.java21
-rw-r--r--src/main/java/com/seaofnodes/simple/Parser.java97
-rw-r--r--src/main/java/com/seaofnodes/simple/RegMask.java47
-rw-r--r--src/main/java/com/seaofnodes/simple/SB.java17
-rw-r--r--src/main/java/com/seaofnodes/simple/node/AddNode.java3
-rw-r--r--src/main/java/com/seaofnodes/simple/node/BoolNode.java26
-rw-r--r--src/main/java/com/seaofnodes/simple/node/BulkMemPhiNode.java16
-rw-r--r--src/main/java/com/seaofnodes/simple/node/CFGNode.java19
-rw-r--r--src/main/java/com/seaofnodes/simple/node/CProjNode.java4
-rw-r--r--src/main/java/com/seaofnodes/simple/node/CallEndNode.java15
-rw-r--r--src/main/java/com/seaofnodes/simple/node/CallNode.java24
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ConstantNode.java17
-rw-r--r--src/main/java/com/seaofnodes/simple/node/CtrlNode.java3
-rw-r--r--src/main/java/com/seaofnodes/simple/node/FunNode.java38
-rw-r--r--src/main/java/com/seaofnodes/simple/node/IfNode.java6
-rw-r--r--src/main/java/com/seaofnodes/simple/node/LoadNode.java71
-rw-r--r--src/main/java/com/seaofnodes/simple/node/LoopNode.java1
-rw-r--r--src/main/java/com/seaofnodes/simple/node/MachConcreteNode.java27
-rw-r--r--src/main/java/com/seaofnodes/simple/node/MachNode.java49
-rw-r--r--src/main/java/com/seaofnodes/simple/node/MemMergeNode.java13
-rw-r--r--src/main/java/com/seaofnodes/simple/node/MemOpNode.java41
-rw-r--r--src/main/java/com/seaofnodes/simple/node/MemPhiNode.java6
-rw-r--r--src/main/java/com/seaofnodes/simple/node/MulNode.java28
-rw-r--r--src/main/java/com/seaofnodes/simple/node/MultiUse.java5
-rw-r--r--src/main/java/com/seaofnodes/simple/node/NewNode.java17
-rw-r--r--src/main/java/com/seaofnodes/simple/node/Node.java103
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ParmNode.java3
-rw-r--r--src/main/java/com/seaofnodes/simple/node/PhiNode.java4
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ProjNode.java6
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ReadOnlyNode.java1
-rw-r--r--src/main/java/com/seaofnodes/simple/node/RegionNode.java66
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ReturnNode.java3
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ScopeNode.java6
-rw-r--r--src/main/java/com/seaofnodes/simple/node/StartNode.java7
-rw-r--r--src/main/java/com/seaofnodes/simple/node/StopNode.java1
-rw-r--r--src/main/java/com/seaofnodes/simple/node/StoreNode.java49
-rw-r--r--src/main/java/com/seaofnodes/simple/node/XCtrlNode.java3
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddFRISC.java30
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddIRISC.java39
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddRISC.java36
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndIRISC.java40
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndRISC.java41
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/CBranchRISC.java49
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRISC.java37
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRRISC.java31
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/CmpIRISC.java44
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/CmpRISC.java28
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivFRISC.java30
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivRISC.java33
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/F8ToIRISC.java27
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/FltRISC.java39
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/FunRISC.java22
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/I2F8RISC.java29
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/IntRISC.java43
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/LoadRISC.java34
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/MemOpRISC.java39
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulFRISC.java30
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulIRISC.java35
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulRISC.java33
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/NewRISC.java44
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrIRISC.java43
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrRISC.java43
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/ParmRISC.java25
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/ProjRISC.java23
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/RetRISC.java48
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetRISC.java35
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllIRISC.java40
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllRISC.java36
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraIRISC.java43
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraRISC.java42
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlIRISC.java43
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlRISC.java38
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/StoreRISC.java40
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubFRISC.java30
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubRISC.java29
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/TFPRISC.java43
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorIRISC.java45
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorRISC.java43
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java332
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFMemX86.java33
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFX86.java29
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddIX86.java39
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddMemX86.java25
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddX86.java29
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AndIX86.java42
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AndX86.java33
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallEndX86.java37
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallRX86.java31
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallX86.java41
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpIX86.java43
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpMemX86.java33
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpX86.java28
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivFX86.java33
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivX86.java31
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/F8ToIX86.java27
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FltX86.java36
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FunX86.java21
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/I2f8X86.java28
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/IntX86.java45
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/JmpX86.java46
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LeaX86.java42
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LoadX86.java25
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemAddX86.java28
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemOpX86.java90
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulFX86.java30
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulIX86.java36
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulX86.java30
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NewX86.java40
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/OrIX86.java44
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/OrX86.java33
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ParmX86.java23
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ProjX86.java25
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RetX86.java48
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SarIX86.java37
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SarX86.java42
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SetX86.java39
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlIX86.java38
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlX86.java39
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrIX86.java40
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrX86.java38
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/StoreX86.java31
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubFX86.java29
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubX86.java29
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TFPX86.java41
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/XorIX86.java42
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java443
-rw-r--r--src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/print/SimpleGraphObserver.java9
-rw-r--r--src/main/java/com/seaofnodes/simple/type/Type.java4
-rw-r--r--src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java68
-rw-r--r--src/main/java/com/seaofnodes/simple/type/TypeStruct.java8
-rw-r--r--src/test/java/com/seaofnodes/simple/BrainFuckTest.java2
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter03Test.java2
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter04Test.java21
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter05Test.java64
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter06Test.java30
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter08Test.java4
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter09Test.java4
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter11Test.java10
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter12Test.java2
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter15Test.java12
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter17bTest.java8
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter18Test.java58
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter19Test.java529
-rw-r--r--src/test/java/com/seaofnodes/simple/Eval2.java6
-rw-r--r--src/test/java/com/seaofnodes/simple/FuzzerWrap.java7
-rw-r--r--src/test/java/com/seaofnodes/simple/Main.java17
-rw-r--r--src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java13
-rw-r--r--src/test/java/com/seaofnodes/simple/evaluator/Scheduler.java2
-rw-r--r--src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java15
-rw-r--r--src/test/java/com/seaofnodes/simple/fuzzer/FuzzerUtils.java36
-rw-r--r--src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java16
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/fcn.smp15
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/fcn2.smp5
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/fcn3.smp42
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/sieve.smp32
163 files changed, 5802 insertions, 733 deletions
diff --git a/src/main/java/com/seaofnodes/simple/ASMPrinter.java b/src/main/java/com/seaofnodes/simple/ASMPrinter.java
new file mode 100644
index 00000000..3c4162ea
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/ASMPrinter.java
@@ -0,0 +1,51 @@
+package com.seaofnodes.simple;
+
+import com.seaofnodes.print.AssemblyAdapter;
+import com.seaofnodes.print.ASMPrinter.Data;
+import com.seaofnodes.print.ASMPrinter.Pool;
+import com.seaofnodes.simple.CodeGen;
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.*;
+import java.util.ArrayList;
+
+/** Machine-specific facts; traversal, columns and byte display live in print/. */
+public final class ASMPrinter extends AssemblyAdapter<Node> {
+ private final CodeGen _code;
+ private ASMPrinter(CodeGen code) {
+ super(new IRPrinter());
+ _code=code;
+ }
+ public static SB print(SB sb, CodeGen code) {
+ return sb.p(new com.seaofnodes.print.ASMPrinter<>(new ASMPrinter(code)).print());
+ }
+ @Override public ArrayList<Node> blocks() {
+ var blocks=new ArrayList<Node>();
+ var cfg=_code._cfg;
+ if( cfg!=null ) for( Node n : cfg ) blocks.add(n);
+ return blocks;
+ }
+ @Override public String op(Node n) { return n instanceof MachNode m ? m.op() : n.label(); }
+ @Override public String register(Node n) { return _code.reg(n); }
+ @Override public String comment(Node n) { return n.comment(); }
+ @Override public String phiName(Node n) { return ((PhiNode)n)._label; }
+ @Override public String operands(Node n) {
+ SB sb=new SB();
+ if( n instanceof MachNode m ) m.asm(_code,sb);
+ else if( !(n._type instanceof TypeMem) ) {
+ sb.p(n._nid).p(": ");
+ for( Node def : n._inputs ) sb.p(def==null ? "___" : Integer.toString(def._nid)).p(' ');
+ }
+ return sb.toString();
+ }
+ @Override public boolean labelBlock(Node n) {
+ return !(n instanceof FunNode) && !(n instanceof IfNode) && !(n instanceof CallNode) &&
+ !(n instanceof CallEndNode) && !(n instanceof CProjNode && ir.input0(n) instanceof CallEndNode);
+ }
+ @Override public boolean hidden(Node n) {
+ if( n instanceof PhiNode || n instanceof ProjNode )
+ if( n._type instanceof TypeMem || n._type instanceof TypeRPC ) return true;
+ if( n instanceof MemMergeNode ) return true;
+ return n.getClass()==ConstantNode.class;
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/Ary.java b/src/main/java/com/seaofnodes/simple/Ary.java
index b5184948..73ada8e0 100644
--- a/src/main/java/com/seaofnodes/simple/Ary.java
+++ b/src/main/java/com/seaofnodes/simple/Ary.java
@@ -42,8 +42,7 @@ public class Ary<E> extends AbstractList<E> implements List<E> {
}
/** @return remove and return last element */
public E pop( ) {
- range_check(0);
- return _es[--_len];
+ return _len==0 ? null : _es[--_len];
}
@Override public E removeLast() { return pop(); }
@@ -93,6 +92,22 @@ public class Ary<E> extends AbstractList<E> implements List<E> {
return old;
}
+ /** Set existing element
+ * @param i element to set
+ * @param e value to set
+ * @return old value
+ * @exception AIOOBE if !(0 <= i < _len)
+ */
+ public E setX( int i, E e ) {
+ if( i >= _len ) {
+ while( i >= _es.length ) _es = Arrays.copyOf( _es, _es.length << 1 );
+ _len = i+1;
+ }
+ E old = _es[i];
+ _es[i] = e;
+ return old;
+ }
+
public Ary<E> setLen( int len ) {
if( len > _len )
while( len>= _es.length ) _es = Arrays.copyOf(_es,_es.length<<1);
@@ -103,13 +118,14 @@ public class Ary<E> extends AbstractList<E> implements List<E> {
return this;
}
- public void swap( int i, int j ) {
+ public E swap( int i, int j ) {
range_check(i);
range_check(j);
- if( i==j ) return;
E tmp = _es[i];
+ if( i==j ) return tmp;
_es[i] = _es[j];
_es[j] = tmp;
+ return tmp;
}
@@ -139,12 +155,12 @@ public class Ary<E> extends AbstractList<E> implements List<E> {
return this;
}
- /** @return compact array version */
- public E[] asAry() { return Arrays.copyOf(_es,_len); }
+ /** @return compact array version */
+ public E[] asAry() { return Arrays.copyOf(_es,_len); }
- /** Sorts in-place
- * @param c Comparator to sort by */
- public void sort_update(Comparator<? super E> c ) { Arrays.sort(_es, 0, _len, c); }
+ /** Sorts in-place
+ * @param c Comparator to sort by */
+ public void sort_update(Comparator<? super E> c ) { Arrays.sort(_es, 0, _len, c); }
/** Find the first matching element using ==, or -1 if none. Note that
* most del calls shuffle the list, so the first element might be random.
diff --git a/src/main/java/com/seaofnodes/simple/CodeGen.java b/src/main/java/com/seaofnodes/simple/CodeGen.java
index 864b3a1a..0a3a60e8 100644
--- a/src/main/java/com/seaofnodes/simple/CodeGen.java
+++ b/src/main/java/com/seaofnodes/simple/CodeGen.java
@@ -3,8 +3,11 @@
import com.seaofnodes.graph.GraphObserver;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.node.cpus.x86_64_v2.MemOpX86;
import com.seaofnodes.simple.type.*;
+import java.util.BitSet;
import java.util.HashMap;
+import java.util.IdentityHashMap;
public class CodeGen {
public GraphObserver<Node> _obs;
@@ -18,66 +21,154 @@ public class CodeGen {
}
+ // Last created CodeGen as a global; used all over to avoid passing about a
+ // "context".
+ public static CodeGen CODE;
public enum Phase {
- Parse, Opto, TypeCheck, Schedule, LocalSched, RegAlloc;
+ Parse, // Parse ASCII text into Sea-of-Nodes IR
+ Opto, // Run ideal optimizations
+ TypeCheck, // Last check for bad programs
+ InstructionSelection, // Convert to target hardware nodes
+ Schedule, // Global schedule (code motion) nodes
+ LocalSched, // Local schedule
+ RegAlloc; // Register allocation
+ }
+ public enum CallingConv {
+ Win64,
+ SystemV
}
public Phase _phase;
+ // ---------------------------
+ // Compilation source code
public final String _src;
// Compile-time known initial argument type
public final TypeInteger _arg;
+ public CallingConv _callingConv;
+
+ // ---------------------------
+ public CodeGen( String src ) { this(src, TypeInteger.BOT, 123L ); }
+ public CodeGen( String src, TypeInteger arg, long workListSeed ) {
+ CODE = this;
+ _phase = null;
+ _callingConv = null;
+ _start = new StartNode(arg);
+ _stop = new StopNode(src);
+ _src = src;
+ _arg = arg;
+ _iter = new IterPeeps(workListSeed);
+ P = new Parser(this,arg);
+ }
+
+
+ // ---------------------------
+ /**
+ * A counter, for unique node id generation. Starting with value 1, to
+ * avoid bugs confusing node ID 0 with uninitialized values.
+ */
+ private int _uid = 1;
+ public int UID() { return _uid; }
+ public int getUID() { return _uid++; }
+
+ // Next available memory alias number
+ private int _alias = 2; // 0 is for control, 1 for memory
+ public int getALIAS() { return _alias++; }
- public Parser P;
+
+ // Popular visit bitset, declared here, so it gets reused all over
+ public final BitSet _visit = new BitSet();
+ public BitSet visit() { assert _visit.isEmpty(); return _visit; }
+
+ // Start and stop; end points of the generated IR
public StartNode _start;
- public StopNode _stop ;
+ public StopNode _stop;
+
+ // Global Value Numbering. Hash over opcode and inputs; hits in this table
+ // are structurally equal.
+ public final HashMap<Node,Node> _gvn = new HashMap<>();
+
+ // Compute "function indices": FIDX.
+ // Each new request at the same signature gets a new FIDX.
+ private final HashMap<TypeTuple,Integer> FIDXS = new HashMap<>();
+ public TypeFunPtr makeFun( TypeTuple sig, Type ret ) {
+ Integer i = FIDXS.get(sig);
+ int fidx = i==null ? 0 : i;
+ FIDXS.put(sig,fidx+1); // Track count per sig
+ assert fidx<64; // TODO: need a larger FIDX space
+ return TypeFunPtr.make((byte)2,sig,ret, 1L<<fidx );
+ }
+ // Signature for MAIN
+ public TypeFunPtr _main = makeFun(TypeTuple.MAIN,Type.BOTTOM);
+ // Reverse from a constant function pointer to the IR function being called
+ public FunNode link( TypeFunPtr tfp ) {
+ assert tfp.isConstant();
+ return _linker.get(tfp.makeFrom(Type.BOTTOM));
+ }
+ // Insert linker mapping from constant function signature to the function
+ // being called.
+ public void link(FunNode fun) {
+ _linker.put(fun.sig().makeFrom(Type.BOTTOM),fun);
+ }
// "Linker" mapping from constant TypeFunPtrs to heads of function. These
// TFPs all have exact single fidxs and their return is wiped to BOTTOM (so
// the return is not part of the match).
private final HashMap<TypeFunPtr,FunNode> _linker = new HashMap<>();
+ // Parser object
+ public final Parser P;
- // Last created CodeGen as a global, for easier debugging prints
- public static CodeGen CODE;
-
- CodeGen( String src, TypeInteger arg ) { _phase = null; _src = src; _arg=arg; CODE=this; }
- public CodeGen( String src ) { this(src,TypeInteger.BOT); }
-
+ // Parse ASCII text into Sea-of-Nodes IR
public CodeGen parse() {
assert _phase == null;
_phase = Phase.Parse;
- _linker.clear();
- P = new Parser(_src,_arg);
- _stop = P.parse();
- _start = Parser.START; // Capture global start
+ P.parse();
if( _obs != null ) _obs.phase("Parse");
- P = null; // No longer parsing
return this;
}
+
+ // ---------------------------
+ // Iterator peepholes.
+ public final IterPeeps _iter;
+ // Stop tracking deps while assertin
+ public boolean _midAssert;
+ // Statistics on peepholes
+ public int _iter_cnt, _iter_nop_cnt;
+ // Stat gathering
+ public void iterCnt() { if( !_midAssert ) _iter_cnt++; }
+ public void iterNop() { if( !_midAssert ) _iter_nop_cnt++; }
+
+ // Run ideal optimizations
public CodeGen opto() {
assert _phase == Phase.Parse;
_phase = Phase.Opto;
- // Optimization
- IterPeeps.iterate(_stop);
+ // Pessimistic peephole optimization on a worklist
+ _iter.iterate(this);
// TODO:
// Optimistic
+ // TODO:
+ // loop unroll, peel, RCE, etc
if( _obs != null ) _obs.phase("Opto");
return this;
}
+ public <N extends Node> N add( N n ) { return (N)_iter.add(n); }
+ public void addAll( Ary<Node> ary ) { _iter.addAll(ary); }
+
+ // ---------------------------
+ // Last check for bad programs
public CodeGen typeCheck() {
// Demand phase Opto for cleaning up dead control flow at least,
// required for the following GCM.
- assert _phase == Phase.Opto;
+ assert _phase.ordinal() <= Phase.Opto.ordinal();
_phase = Phase.TypeCheck;
- // Type check
Parser.ParseException err = _stop.walk(n -> {
Parser.ParseException msg = n.err();
if( msg != null && _obs != null ) _obs.error(n, msg.getMessage());
@@ -90,20 +181,96 @@ public class CodeGen {
}
+ // ---------------------------
+ // Code generation CPU target
+ public Machine _mach;
+
+ // Convert to target hardware nodes
+ public CodeGen instSelect( String cpu, String callingConv ) {
+ assert _phase.ordinal() <= Phase.TypeCheck.ordinal();
+ _phase = Phase.InstructionSelection;
+
+ _callingConv = switch(callingConv) {
+ case "SystemV" -> CallingConv.SystemV;
+ case "Win64" -> CallingConv.Win64;
+ default -> throw Utils.TODO(); // Unknown calling convention
+ };
+
+ // Look for CPU in fixed named place:
+ // com.seaofnodes.simple.node.cpus."cpu"."cpu.class"
+ String clzFile = "com.seaofnodes.simple.node.cpus."+cpu+"."+cpu;
+ try { _mach = ((Class<Machine>) Class.forName( clzFile )).getDeclaredConstructor().newInstance(); }
+ catch( Exception e ) { throw new RuntimeException(e); }
+
+ // Convert to machine ops
+ _uid = 1; // All new machine nodes reset numbering
+ var map = new IdentityHashMap<Node,Node>();
+ _instSelect( _stop, map );
+ _stop = ( StopNode)map.get(_stop );
+ _start = (StartNode)map.get(_start);
+ _instOuts(_stop,visit());
+ _visit.clear();
+ return this;
+ }
+
+ // Walk all ideal nodes, recursively mapping ideal to machine nodes, then
+ // make a machine node for "this".
+ private Node _instSelect( Node n, IdentityHashMap<Node,Node> map ) {
+ if( n==null ) return null;
+ Node x = map.get(n);
+ if( x !=null ) return x; // Been there, done that
+
+ // If n is a MachConcrete, then its part of a multi-node expansion.
+ // It does not need instruction selection (already selected!)
+ // but it does need its inputs walked.
+ if( n instanceof MachConcreteNode || n instanceof MemOpX86) {
+ for( int i=0; i < n.nIns(); i++ )
+ n._inputs.set(i, _instSelect(n.in(i),map) );
+ return n;
+ }
+
+ // Produce a machine node from n; map it to flag as done so stops cycles.
+ map.put(n, x=_mach.instSelect(n) );
+ // Walk machine op and replace inputs with mapped inputs
+ for( int i=0; i < x.nIns(); i++ )
+ x._inputs.set(i, _instSelect(x.in(i),map) );
+ if( x instanceof MachNode mach )
+ mach.postSelect(); // Post selection action
+
+ // Updates forward edges only.
+ n._outputs.clear();
+ return x;
+ }
+
+ // Walk all machine Nodes, and set their output edges
+ private void _instOuts( Node n, BitSet visit ) {
+ if( visit.get(n._nid) ) return;
+ visit.set(n._nid);
+ for( Node in : n._inputs )
+ if( in!=null ) {
+ in._outputs.push(n);
+ _instOuts(in,visit);
+ }
+ }
+
+
+ // ---------------------------
+ // Control Flow Graph in RPO order.
+ Ary<CFGNode> _cfg = new Ary<>(CFGNode.class);
+
+ // Global schedule (code motion) nodes
public CodeGen GCM() {
- assert _phase == Phase.TypeCheck;
+ assert _phase.ordinal() <= Phase.InstructionSelection.ordinal();
_phase = Phase.Schedule;
// Build the loop tree, fix never-exit loops
_start.buildLoopTree(_stop);
+ GlobalCodeMotion.buildCFG(this);
-
- // TODO:
- // loop unroll, peel, RCE, etc
- GlobalCodeMotion.buildCFG(_start,_stop);
return this;
}
+ // ---------------------------
// Local (basic block) scheduler phase, a classic list scheduler
public CodeGen localSched() {
assert _phase == Phase.Schedule;
@@ -112,25 +279,26 @@ public class CodeGen {
return this;
}
- // Reverse from a constant function pointer to the IR function being called
- public FunNode link( TypeFunPtr tfp ) {
- assert tfp.isConstant();
- return _linker.get(tfp.makeFrom(Type.BOTTOM));
- }
- public void link(FunNode fun) {
- _linker.put(fun.sig().makeFrom(Type.BOTTOM),fun);
+ // ---------------------------
+ public String reg(Node n) {
+ if( _phase == Phase.RegAlloc ) throw Utils.TODO();
+ return "N"+ n._nid;
}
+
+ // ---------------------------
+ SB asm(SB sb) { return ASMPrinter.print(sb,this); }
+ String asm() { return asm(new SB()).toString(); }
+
+
// Testing shortcuts
Node ctrl() { return _stop.ret().ctrl(); }
Node expr() { return _stop.ret().expr(); }
String print() { return _stop.print(); }
// Debugging helper
- @Override public String toString() {
- return IRPrinter.prettyPrint(this);
- }
+ @Override public String toString() { return IRPrinter.prettyPrint(this); }
// Debugging helper
public Node f(int idx) { return _stop.find(idx); }
diff --git a/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java b/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java
index 8221ede1..95ada83d 100644
--- a/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java
+++ b/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java
@@ -10,12 +10,36 @@ public abstract class GlobalCodeMotion {
// Arrange that the existing isCFG() Nodes form a valid CFG. The
// Node.use(0) is always a block tail (either IfNode or head of the
// following block). There are no unreachable infinite loops.
- public static void buildCFG( StartNode start, StopNode stop ) {
- schedEarly(start);
- breakUpGlobalConstants(start);
- schedLate(stop);
+ public static void buildCFG( CodeGen code ) {
+ Ary<CFGNode> rpo = new Ary<>(CFGNode.class);
+ _rpo_cfg(null, code._start, code.visit(), rpo);
+ // Reverse in-place
+ for( int i=0; i< rpo.size()>>1; i++ ) {
+ int j = rpo.size()-1-i;
+ CFGNode tmp = rpo.get(i);
+ rpo.set(i, rpo.get(j));
+ rpo.set(j, tmp);
+ }
+ // Set global CFG
+ code._cfg = rpo;
+
+ schedEarly(code);
+ breakUpGlobalConstants(code._start);
+ code._visit.clear();
+ schedLate (code);
}
+ // Post-Order of CFG
+ private static void _rpo_cfg(CFGNode def, Node use, BitSet visit, Ary<CFGNode> rpo) {
+ if( !(use instanceof CFGNode cfg) || visit.get(cfg._nid) )
+ return; // Been there, done that
+ if( def instanceof CallNode && cfg instanceof FunNode )
+ return; // Ignore linked function calls
+ visit.set(cfg._nid);
+ for( Node useuse : cfg._outputs )
+ _rpo_cfg(cfg,useuse,visit,rpo);
+ rpo.add(cfg);
+ }
// After early scheduling, every global chain member has Start in slot 0.
// Give each function one private copy of the entire dependency graph.
@@ -24,7 +48,7 @@ public abstract class GlobalCodeMotion {
var cons = new ArrayList<Node>();
for( Node con : start._outputs )
if( con!=null && !(con instanceof CFGNode) &&
- con.isConst() ) {
+ (con.isConst() || (con instanceof MachNode mach && mach.outregmap()!=null)) ) {
globals.put(con,true);
cons.add(con.keep()); // Preserve original inputs while rewiring users.
}
@@ -72,38 +96,22 @@ public abstract class GlobalCodeMotion {
// (except at loops). Since defs are visited first - and hoisted as early
// as possible, when we come to a use we place it just after its deepest
// input.
- private static void schedEarly(StartNode start) {
- ArrayList<CFGNode> rpo = new ArrayList<>();
- BitSet visit = new BitSet();
- _rpo_cfg(null, start, visit, rpo);
+ private static void schedEarly(CodeGen code) {
// Reverse Post-Order on CFG
- for( int j=rpo.size()-1; j>=0; j-- ) {
- CFGNode cfg = rpo.get(j);
+ for( CFGNode cfg : code._cfg ) {
cfg.loopDepth();
for( Node n : cfg._inputs )
- _schedEarly(n,visit);
+ _schedEarly(n,code._visit );
// In dead infinite loops, entire code blocks may be unreachable
// from below. Reach down from the CFG to their Phis so their
// inputs are scheduled too.
if( cfg instanceof RegionNode )
for( Node phi : cfg._outputs )
if( phi instanceof PhiNode )
- _schedEarly(phi,visit);
+ _schedEarly(phi,code._visit );
}
}
- // Post-Order of CFG
- private static void _rpo_cfg(CFGNode def, Node use, BitSet visit, ArrayList<CFGNode> rpo) {
- if( !(use instanceof CFGNode cfg) || visit.get(cfg._nid) )
- return; // Been there, done that
- if( def instanceof CallNode && cfg instanceof FunNode )
- return; // Ignore linked function calls
- visit.set(cfg._nid);
- for( Node useuse : cfg._outputs )
- _rpo_cfg(cfg,useuse,visit,rpo);
- rpo.add(cfg);
- }
-
private static void _schedEarly(Node n, BitSet visit) {
if( n==null || visit.get(n._nid) ) return; // Been there, done that
assert !(n instanceof CFGNode);
@@ -113,26 +121,27 @@ public abstract class GlobalCodeMotion {
for( Node def : n._inputs )
if( def!=null && !(def instanceof PhiNode) )
_schedEarly(def,visit);
+
// An existing edge 0 already supplies control (or a Phi/Proj binding).
if( n.in(0)==null ) {
// Schedule at deepest input
- CFGNode early = Parser.START; // Maximally early, lowest idepth
+ CFGNode early = CodeGen.CODE._start; // Maximally early, lowest idepth
for( int i=1; i<n.nIns(); i++ )
if( n.in(i)!=null && n.in(i).cfg0().idepth() > early.idepth() )
early = n.in(i).cfg0(); // Latest/deepest input
- n.setDef(0,early); // First place this can go
+ n.setDef(0,early);
}
}
// ------------------------------------------------------------------------
- private static void schedLate( StopNode stop) {
- CFGNode[] late = new CFGNode[Node.UID()];
- Node[] ns = new Node[Node.UID()];
+ private static void schedLate( CodeGen code ) {
+ CFGNode[] late = new CFGNode[code.UID()];
+ Node[] ns = new Node[code.UID()];
// Record Load NIDs at all their CFG block choices, then check against
// Store block choices to force a Load above an anti-dependent Store.
- int[] anti = new int[Node.UID()];
+ int[] anti = new int[code.UID()];
// Breadth-first scheduling
- breadth(stop,ns,late,anti);
+ breadth(code._stop,ns,late,anti);
// Copy the best placement choice into the control slot
for( int i=0; i<late.length; i++ )
@@ -160,7 +169,7 @@ public abstract class GlobalCodeMotion {
continue outer; // Nope, await all uses done
// Loads need their memory inputs' uses also done
- if( n instanceof LoadNode ld )
+ if( n instanceof MemOpNode ld && !ld.isMem() )
for( Node memuse : ld.antiDeps() )
if( late[memuse._nid]==null )
continue outer;
@@ -193,7 +202,7 @@ public abstract class GlobalCodeMotion {
lca = use_block(n,use, late).domLCA(lca,null);
// Loads may need anti-dependencies, raising their LCA
- if( n instanceof LoadNode load )
+ if( n instanceof MemOpNode load && !load.isMem() )
lca = find_anti_dep(lca,load,early,late,anti);
// Walk up from the LCA to the early, looking for best place. This is
@@ -237,13 +246,13 @@ public abstract class GlobalCodeMotion {
if( visit.get(def._nid) ) return;
visit.set(def._nid);
for( Node out : def._outputs )
- if( out instanceof LoadNode ld && late[ld._nid]==null ) work.push(ld);
+ if( out instanceof MemOpNode ld && !ld.isMem() && late[ld._nid]==null ) work.push(ld);
if( def instanceof MemMergeNode )
for( int i=1; i<def.nIns(); i++ )
if( def.in(i)!=null ) wakeLoads(def.in(i),late,work,visit);
}
- private static CFGNode find_anti_dep(CFGNode lca, LoadNode load, CFGNode early, CFGNode[] late, int[] anti) {
+ private static CFGNode find_anti_dep(CFGNode lca, MemOpNode load, CFGNode early, CFGNode[] late, int[] anti) {
// We could skip final-field loads here.
// Walk LCA->early, flagging Load's block location choices
for( CFGNode cfg=lca; early!=null && cfg!=early.idom(); cfg = cfg.idom() )
@@ -251,7 +260,7 @@ public abstract class GlobalCodeMotion {
// Walk load->mem uses, looking for Stores causing an anti-dep
for( Node mem : load.antiDeps() ) {
switch( mem ) {
- case StoreNode st:
+ case MemOpNode st:
assert late[st._nid]!=null;
lca = anti_dep(load,late[st._nid],st.cfg0(),lca,st,anti);
break;
@@ -277,7 +286,7 @@ public abstract class GlobalCodeMotion {
}
//
- private static CFGNode anti_dep( LoadNode load, CFGNode stblk, CFGNode defblk, CFGNode lca, Node st, int[] anti ) {
+ private static CFGNode anti_dep( MemOpNode load, CFGNode stblk, CFGNode defblk, CFGNode lca, Node st, int[] anti ) {
for( ; stblk != defblk.idom(); stblk = stblk.idom() ) {
// Store and Load overlap, need anti-dependence
if( anti[stblk._nid]==load._nid ) {
diff --git a/src/main/java/com/seaofnodes/simple/IRPrinter.java b/src/main/java/com/seaofnodes/simple/IRPrinter.java
index c7ca8cf4..f0ac717a 100644
--- a/src/main/java/com/seaofnodes/simple/IRPrinter.java
+++ b/src/main/java/com/seaofnodes/simple/IRPrinter.java
@@ -35,10 +35,10 @@ public final class IRPrinter extends IRAdapter<Node> {
if( n instanceof CProjNode p ) return p._idx;
return n._nid;
}
- @Override public String functionName(Node n) { return n.label(); }
+ @Override public String functionName(Node n) { return ((FunNode)n)._name==null ? "" : ((FunNode)n)._name; }
@Override public String signature(Node n) {
FunNode fun=(FunNode)n;
- return fun.sig().str();
+ return (fun._name==null ? "" : fun._name)+" "+fun.sig().str();
}
public static String prettyPrint(Node n, int depth) { return PRINT.prettyPrint(n,depth); }
diff --git a/src/main/java/com/seaofnodes/simple/IterPeeps.java b/src/main/java/com/seaofnodes/simple/IterPeeps.java
index 1792dada..087a990b 100644
--- a/src/main/java/com/seaofnodes/simple/IterPeeps.java
+++ b/src/main/java/com/seaofnodes/simple/IterPeeps.java
@@ -42,30 +42,28 @@ import java.util.Random;
* progressOnList(stop);`</li>
* </ul>
*/
-public abstract class IterPeeps {
+public class IterPeeps {
- private static final WorkList<Node> WORK = new WorkList<>();
+ private final WorkList<Node> _work;
- public static <N extends Node> N add( N n ) {
- return (N)WORK.push(n);
- }
+ IterPeeps( long seed ) { _work = new WorkList<>(seed); }
- public static void addAll( Ary<Node> ary ) {
- WORK.addAll(ary);
- }
+ public <N extends Node> N add( N n ) { return (N)_work.push(n); }
+
+ public void addAll( Ary<Node> ary ) { _work.addAll(ary); }
/**
* Iterate peepholes to a fixed point
*/
- public static void iterate( StopNode stop) {
- assert progressOnList(stop);
+ public void iterate( CodeGen code ) {
+ assert progressOnList(code);
int cnt=0;
Node n;
- while( (n=WORK.pop()) != null ) {
+ while( (n=_work.pop()) != null ) {
if( n.isDead() ) continue;
cnt++; // Useful for debugging, searching which peephole broke things
- var obs = CodeGen.CODE == null ? null : CodeGen.CODE._obs;
+ var obs = CodeGen.CODE._obs;
if( obs != null ) obs.before(n);
Node x = n.peepholeOpt();
if( x != null ) {
@@ -78,20 +76,20 @@ public abstract class IterPeeps {
// Changes require neighbors onto the worklist
if( x != n || !(x instanceof ConstantNode) ) {
// All outputs of n (changing node) not x (prior existing node).
- for( Node z : n._outputs ) WORK.push(z);
+ for( Node z : n._outputs ) _work.push(z);
// Everybody gets a free "go again" in case they didn't get
// made in their final form.
- WORK.push(x);
+ _work.push(x);
// If the result is not self, revisit all inputs (because
// there's a new user), and replace in the graph.
if( x != n ) {
- for( Node z : n. _inputs ) WORK.push(z);
+ for( Node z : n. _inputs ) _work.push(z);
n.subsume(x);
}
}
// If there are distant neighbors, move to worklist
n.moveDepsToWorklist();
- assert progressOnList(stop); // Very expensive assert
+ assert progressOnList(code); // Very expensive assert
}
if( n.isUnused() && !(n instanceof StopNode) )
n.kill(); // Just plain dead
@@ -101,7 +99,7 @@ public abstract class IterPeeps {
}
// Visit ALL nodes and confirm the invariant:
- // Either you are on the WORK worklist OR running `iter()` makes no progress.
+ // Either you are on the _work worklist OR running `iter()` makes no progress.
// This invariant ensures that no progress is missed, i.e., when the
// worklist is empty we have indeed done all that can be done. To help
@@ -112,30 +110,22 @@ public abstract class IterPeeps {
// neighbors and these should fail, but will then try to add dependencies
// {@link #Node.addDep} which is a side effect in an assert. The {@link
// #midAssert} is used to stop this side effect.
- private static boolean MID_ASSERT;
- public static boolean midAssert() { return MID_ASSERT; }
- private static boolean progressOnList(Node stop) {
- MID_ASSERT = true;
- int old_cnt = Node.ITER_CNT, old_nop = Node.ITER_NOP_CNT;
- Node changed = stop.walk( n -> {
+ private boolean progressOnList(CodeGen code) {
+ code._midAssert = true;
+ Node changed = code._stop.walk( n -> {
Node m = n;
if( n.compute().isa(n._type) && (!n.iskeep() || n._nid<=6) ) { // Types must be forwards, even if on worklist
- if( WORK.on(n) ) return null;
+ if( _work.on(n) ) return null;
m = n.peepholeOpt();
if( m==null ) return null;
}
System.err.println("BREAK HERE FOR BUG");
return m;
});
- Node.ITER_CNT = old_cnt; Node.ITER_NOP_CNT = old_nop;
- MID_ASSERT = false;
+ code._midAssert = false;
return changed==null;
}
- public static void reset() {
- WORK.clear();
- }
-
/**
* Classic WorkList, with a fast add/remove, dup removal, random pull.
* The Node's nid is used to check membership in the worklist.
diff --git a/src/main/java/com/seaofnodes/simple/ListScheduler.java b/src/main/java/com/seaofnodes/simple/ListScheduler.java
index 252a8f8e..efca945a 100644
--- a/src/main/java/com/seaofnodes/simple/ListScheduler.java
+++ b/src/main/java/com/seaofnodes/simple/ListScheduler.java
@@ -7,91 +7,157 @@ import java.util.*;
public abstract class ListScheduler {
- private static final BitSet VISIT = new BitSet();
- private static int[] BCNTS;
- public static void sched( CodeGen code ) {
- assert VISIT.isEmpty();
- BCNTS = new int[Node.UID()];
- walk(code._start,VISIT);
- VISIT.clear();
- }
+ // eXtra per-node stuff for scheduling.
+ private static final IdentityHashMap<Node,XSched> XS = new IdentityHashMap<>();
+ private static class XSched {
+ static final Ary<XSched> FREE = new Ary<>(XSched.class);
+ static void alloc(CFGNode bb, Node n) {
+ XSched x = FREE.pop();
+ XS.put(n, (x==null ? new XSched() : x).init(bb,n));
+ }
+ void free() { _n = null; FREE.push(this); }
- // Visit all CFG nodes
- static void walk( CFGNode cfg, BitSet visit ) {
- if( visit.get(cfg._nid) ) return;
- visit.set(cfg._nid);
- local(cfg);
- for( Node n : cfg._outputs )
- if( n instanceof CFGNode c )
- walk(c,visit);
- }
+ static XSched get(Node n) { return n==null ? null : XS.get(n); }
- // Local schedule this one CFG
- static void local( CFGNode bb ) {
- if( !bb.blockHead() || bb instanceof StopNode ) return;
- Node[] uses = bb._outputs._es;
- int ulen = bb._outputs._len;
-
- // Count block-locals
- for( int i=0; i<ulen; i++ ) {
- Node use = uses[i];
- if( use.in(0)==bb && !(use instanceof PhiNode) ) {
- // Normal basic block member: in(0)==bb and not a Phi
- for( Node def : use._inputs )
- // Has defs from same block? Then block-local inputs
- if( def != null && def.cfg0()==bb ) // Same block?
- BCNTS[use._nid]++; // Raise block-local input count
+ private XSched init(CFGNode bb, Node n) {
+ _n = n;
+ _bcnt = 0;
+ _ruse = _rdef = _single = false;
+
+ // Count block-locals and remote inputs
+ if( !(n instanceof PhiNode) )
+ for( Node def : n._inputs )
+ if( def != null && def != bb ) {
+ if( def.cfg0()==bb ) _bcnt++; // Raise block-local input count
+ else _ruse = true;
+ }
+ // Count remote outputs and allowed registers
+ if( n instanceof MultiNode )
+ for( Node use : n._inputs )
+ computeSingleRDef(bb,use);
+ else
+ computeSingleRDef(bb,n);
+ return this;
+ }
+
+ private void computeSingleRDef(CFGNode bb, Node n) {
+ RegMaskRW rmask = n instanceof MachNode mach && mach.outregmap()!=null ? mach.outregmap().copy() : null;
+ for( Node use : n._outputs ) {
+ // Remote use, so this is a remote def
+ if( use!=null && use.cfg0()!=bb ) _rdef = true;
+ // And mask between n's def mask and all n's users masks'
+ if( use instanceof MachNode mach && rmask != null )
+ for( int i=1; i<use.nIns(); i++ )
+ if( use.in(i)==n )
+ rmask.and(mach.regmap(i));
}
+ // Defines in a single register
+ _single = rmask!=null && rmask.size1();
+ }
+
+ // If _bcnt==0, declare ready; move user bcnts into rcnts.
+ boolean isReady() {
+ if( _bcnt > 0 || _rcnt > 0 ) return false;
+ if( _n instanceof MultiNode )
+ for( Node use : _n._outputs )
+ if( use instanceof ProjNode ) readyUp(use);
+ // anti-dep from a multireg directly
+ else _readyUp(use);
+ else
+ readyUp(_n);
+ return true;
+ }
+ private static void readyUp(Node n) {
+ for( Node use : n._outputs )
+ _readyUp(use);
+ }
+ private static void _readyUp(Node use) {
+ XSched xs = get(use);
+ if( xs!=null && !(use instanceof PhiNode) )
+ { assert xs._bcnt>0; xs._bcnt--; xs._rcnt++; }
+ }
+
+ boolean decIsReady() {
+ assert _rcnt > 0;
+ _rcnt--;
+ return isReady();
}
+ Node _n; // Node this extra is for
+ int _bcnt; // Not-ready not-scheduled inputs
+ int _rcnt; // Ready not-scheduled inputs
+ boolean _ruse; // Node IS a "remote use", uses a other-block value
+ boolean _rdef; // Node IS a "remote def" of a value used in other block
+ boolean _single; // Defines a single register, likely to conflict
+ }
+
+
+ // List schedule every block
+ public static void sched( CodeGen code ) {
+ XS.clear();
+ for( CFGNode bb : code._cfg )
+ if( bb.blockHead() )
+ local(bb);
+ }
+
+ // Classic list scheduler
+ private static void local( CFGNode bb ) {
+ assert XS.isEmpty();
+ int len = bb.nOuts();
+
+ // Count block-locals, remote inputs and outputs
+ for( int i=0; i<len; i++ )
+ XSched.alloc(bb,bb.out(i));
+
// Nodes are ready if they are not used by other nodes in this block.
- // Move ready up front. We will never look at remaining nodes, later
- // nodes are found by looking at users of ready nodes.
+ // Move ready up front.
int ready = 0;
- for( int i=0; i<ulen; i++ )
- if( BCNTS[uses[i]._nid]==0 ) // No block-local inputs?
- uses[ready++] = uses[i]; // Move into ready set
- assert ready > 0;
+ for( int i=0; i<len; i++ )
+ if( XSched.get(bb.out(i)).isReady() ) // No block-local inputs?
+ bb._outputs.swap(ready++,i); // Move into ready set
+ assert len==0 || ready > 0;
// Classic list scheduler. Behind sched is scheduled. Between sched
// and ready have zero counts and are ready to schedule. Ahead of
// ready is undefined.
//
- // As nodes are scheduled, the nodes they use decrement their _bcnt. Once
+ // As nodes are scheduled, the nodes they use decrement their bcnts[]. Once
// that gets to 0, all the nodes using it are scheduled, so it's ready.
int sched = 0;
- while( sched < ulen ) {
- assert sched < ready : "Scheduling nodes that are not ready. Probably due to duplicate nodes in the user list.";
- int pick = best(uses, sched,ready); // Pick best
- Node best = uses[pick]; // Swap best to head of ready
- uses[pick] = uses[sched];
- uses[sched++] = best; // And move it into the scheduled set
- // Lower ready count of users
+ while( sched < len ) {
+ assert sched < ready;
+ int pick = best(bb._outputs,sched,ready); // Pick best
+ Node best = bb._outputs.swap(pick,sched++); // Swap best to head of ready and move into the scheduled set
+ // Lower ready count of users, and since projections are ready as
+ // soon as their multi is ready, lower those now too.
for( Node use : best._outputs )
- if( use instanceof ProjNode ) {
+ if( use instanceof ProjNode )
for( Node useuse : use._outputs )
ready = ready(bb,useuse,ready);
- } else
- ready = ready(bb, use,ready);
+ else
+ ready = ready(bb,use,ready);
}
+
+ // Reset for next block
+ for( XSched x : XS.values() )
+ x.free();
+ XS.clear();
}
private static int ready(CFGNode bb, Node use, int ready) {
- if( use != null && use.in(0)==bb && !(use instanceof PhiNode) ) {
- assert BCNTS[use._nid] >= 1;
- if( --BCNTS[use._nid] == 0 )
- bb._outputs._es[ready++] = use; // Became ready, move into ready set
- }
+ if( use!=null && !(use instanceof PhiNode) && use.in(0)==bb && XSched.get(use).decIsReady() )
+ bb._outputs.set(ready++, use); // Became ready, move into ready set
+ //bb._outputs.swap(ready++,use); // Became ready, move into ready set
return ready;
}
// Pick best between sched and ready.
- private static int best( Node[] uses, int sched, int ready ) {
+ private static int best( Ary<Node> blk, int sched, int ready ) {
int pick = sched;
- int score = score(uses[pick]);
+ int score = score(blk.at(pick));
for( int i=sched+1; i<ready; i++ ) {
- int nscore = score(uses[i]);
+ int nscore = score(blk.at(i));
if( nscore > score )
{ score = nscore; pick = i; }
}
@@ -99,12 +165,67 @@ public abstract class ListScheduler {
}
// Highest score wins. Max at 1000, min at 10, except specials.
- private static int score( Node n ) {
- if( n.isMultiTail() ) return 1001; // Pinned just behind the multi-head
- if( n instanceof CFGNode ) return n instanceof ReturnNode ? 2 : 1; // Pinned at block exit
+ private static final int[] CNT = new int[3];
+ static int score( Node n ) {
+ if( n instanceof ProjNode ) return 1001; // Pinned at block entry
+ if( n instanceof CProjNode ) return 1001; // Pinned at block entry
+ if( n instanceof PhiNode ) return 1000;
+ if( n instanceof CFGNode ) return 1; // Pinned at block exit
+
+ int score = 500;
+ // Ideal nodes ignore register pressure scheduling
+ if( !(n instanceof MachNode) )
+ return score;
+
+ // Register pressure local scheduling: avoid overlapping specialized
+ // registers usages.
+
+ // Subtract 10 if this op forces a live range to exist that cannot be
+ // resolved immediately; i.e., live count goes up. Scale to 20 if
+ // multi-def.
+
+ // Subtract 100 if this op forces a single-def live range to exist
+ // which might conflict on the same register with other live ranges.
+ // Defines a single register based on def & uses, and the output is not
+ // ready. Scale to 200 for multi-def.
+ CNT[1]=CNT[2]=0;
+ XSched xn = XSched.get(n);
+ if( xn._rdef ) score = 200; // If defining a remote value, just generically stall alot
+ if( n instanceof MultiNode ) {
+ for( Node use : n._outputs )
+ singleUseNotReady( use, xn._single );
+ } else if( n.nOuts() == 1 )
+ singleUseNotReady( n, xn._single );
+ score += -10 * Math.min( CNT[1], 2 );
+ score += -100 * Math.min( CNT[2], 2 );
+
+ // Add 10 if this op ends a single-def live range, add 20 for 2 or more.
+ // Scale to 100,200 if the single-def is also single-register.
+ CNT[1]=CNT[2]=0;
+ for( int i=1; i<n.nIns(); i++ ) {
+ XSched xd = XSched.get( n.in(i) );
+ if( xd != null && xd._n.nOuts()==1 )
+ CNT[xd._single || ((MachNode)n).regmap(i)!=null && ((MachNode)n).regmap(i).size1() ? 2 : 1]++;
+ }
+ score += 10 * Math.min( CNT[1], 2 );
+ score += 100 * Math.min( CNT[2], 2 );
+
+ //// Nothing special
+ //if( n instanceof Pe ) return 500; // RISC want to go after PE
+ //if( n instanceof Risc ) return 400; // RISC want to go after PE
+ assert 10 <= score && score <= 990;
+ return score;
+ }
- // Nothing special
- return 500;
+ // Single use, and that use is not ready to fire once this one does.
+ // If true, stalling 'n' might reduce 'n's lifetime.
+ // Return 0 for false, 1 if true, 10 if also single register
+ private static void singleUseNotReady( Node n, boolean single ) {
+ if( n.nOuts() != 1 ) return;
+ XSched xu = XSched.get(n.out(0));
+ if( xu !=null && xu._bcnt==0 )
+ return;
+ CNT[single ? 2 : 1]++;
}
}
diff --git a/src/main/java/com/seaofnodes/simple/Machine.java b/src/main/java/com/seaofnodes/simple/Machine.java
new file mode 100644
index 00000000..b29e9aba
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/Machine.java
@@ -0,0 +1,21 @@
+package com.seaofnodes.simple;
+
+import com.seaofnodes.simple.RegMask;
+import com.seaofnodes.simple.node.CFGNode;
+import com.seaofnodes.simple.node.IfNode;
+import com.seaofnodes.simple.node.Node;
+
+abstract public class Machine {
+ // Human readable machine name. Something like "x86-64" or "arm" or "risc5"
+ public abstract String name();
+ // Human-readable name for a register number, e.g. "RAX" or "R0"
+ public abstract String reg( int reg );
+ // Create a split op; any register to any register, including stack slots
+ public abstract Node split();
+ // Return a MachNode unconditional branch
+ public abstract CFGNode jump();
+ // Break an infinite loop
+ public abstract IfNode never( CFGNode ctrl );
+ // Instruction select from ideal nodes
+ public abstract Node instSelect( Node n );
+}
diff --git a/src/main/java/com/seaofnodes/simple/Parser.java b/src/main/java/com/seaofnodes/simple/Parser.java
index 82ad8d04..00aa117e 100644
--- a/src/main/java/com/seaofnodes/simple/Parser.java
+++ b/src/main/java/com/seaofnodes/simple/Parser.java
@@ -2,6 +2,7 @@ package com.seaofnodes.simple;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.*;
+import java.text.ParseException;
import java.util.*;
/**
@@ -13,23 +14,12 @@ import java.util.*;
*/
public class Parser {
- /**
- * A Global Static, unique to each compilation. This is a public, so we
- * can make constants everywhere without having to thread the StartNode
- * through the entire parser and optimizer.
- * <p>
- * To make the compiler multithreaded, this field will have to move into a TLS.
- */
- public static StartNode START;
-
public static ConstantNode ZERO; // Very common node, cached here
public static ConstantNode NIL; // Very common node, cached here
public static XCtrlNode XCTRL; // Very common node, cached here
- // Next available memory alias number
- static int ALIAS;
-
- public StopNode STOP;
+ // Compile driver
+ public final CodeGen _code;
// The Lexer. Thin wrapper over a byte[] buffer with a cursor.
private final Lexer _lexer;
@@ -51,7 +41,7 @@ public class Parser {
/**
* List of keywords disallowed as identifiers
*/
- private static final HashSet<String> KEYWORDS = new HashSet<>(){{
+ public static final HashSet<String> KEYWORDS = new HashSet<>(){{
add("bool");
add("break");
add("byte");
@@ -98,21 +88,14 @@ public class Parser {
public final HashMap<String, StructNode> INITS;
- public Parser(String source) { this(source, TypeInteger.BOT); }
-
- public Parser(String source, TypeInteger arg) {
- Node.reset();
- IterPeeps.reset();
- Type.reset();
- _lexer = new Lexer(source);
+ public Parser(CodeGen code, TypeInteger arg) {
+ _code = code;
+ _lexer = new Lexer(code._src);
_scope = new ScopeNode();
_continueScope = _breakScope = null;
- START = new StartNode(arg);
- STOP = new StopNode(source);
ZERO = con(TypeInteger.ZERO).keep();
NIL = con(Type.NIL).keep();
XCTRL= new XCtrlNode().peephole().keep();
- ALIAS = 2; // alias 0 for the control, 1 for memory
TYPES = defaultTypes();
INITS = new HashMap<>();
}
@@ -142,12 +125,12 @@ public class Parser {
}
// Debugging utility to find a Node by index
- public static Node find(int nid) { return START.find(nid); }
+ public Node f(int nid) { return _code.f(nid); }
private Node ctrl() { return _scope.ctrl(); }
private <N extends Node> N ctrl(N n) { return _scope.ctrl(n); }
- public StopNode parse() {
+ public void parse() {
_xScopes.push(_scope);
_scope.define(ScopeNode.CTRL, Type.CONTROL , false, null, _lexer);
_scope.define(ScopeNode.MEM0, TypeMem.BOT , false, null, _lexer);
@@ -157,7 +140,8 @@ public class Parser {
_scope.mem(con(TypeMem.BOT));
// Parse whole program, as-if function header "{ int arg -> body }"
- parseFunctionBody(TypeFunPtr.MAIN,loc(),"arg");
+ parseFunctionBody(_code._main,loc(),"arg");
+ _code.link(_code._main)._name = "main";
if( !_lexer.isEOF() ) throw _errorSyntax("unexpected");
@@ -167,8 +151,7 @@ public class Parser {
for( StructNode init : INITS.values() )
init.unkeep().kill();
INITS.clear();
- STOP.peephole();
- return STOP;
+ _code._stop.peephole();
}
/**
@@ -186,24 +169,24 @@ public class Parser {
ScopeNode breakScope = _breakScope; _breakScope = null;
ScopeNode continueScope = _continueScope; _continueScope = null;
- FunNode fun = _fun = (FunNode)peep(new FunNode(loc(),START,sig));
+ FunNode fun = _fun = (FunNode)peep(new FunNode(loc(),sig,null,_code._start));
// Once the function header is available, install in linker table -
// allowing recursive functions. Linker matches on declared args and
// exact fidx, and ignores the return (because the fidx will only match
// the exact single function).
- CodeGen.CODE.link(fun);
+ _code.link(fun);
Node rpc = new ParmNode("$rpc",0,TypeRPC.BOT,fun,con(TypeRPC.BOT)).peephole();
// Build a multi-exit return point for all function returns
- RegionNode r = new RegionNode((Node)null,null).init();
+ RegionNode r = new RegionNode((Lexer)null,null,null).init();
assert r.inProgress();
PhiNode rmem = new BulkMemPhiNode(ScopeNode.MEM0,r,null).init();
PhiNode rrez = new PhiNode(ScopeNode.ARG0,Type.BOTTOM,r,null).init();
ReturnNode ret = new ReturnNode(r, rmem, rrez, rpc, fun).init();
fun.setRet(ret);
assert ret.inProgress();
- STOP.addDef(ret);
+ _code._stop.addDef(ret);
// Pre-call the function from Start, with worse-case arguments. This
// represents all the future, yet-to-be-parsed functions calls and
@@ -398,7 +381,7 @@ public class Parser {
// Our current scope is the body Scope
ctrl(ifT.unkeep()); // set ctrl token to ifTrue projection
_scope.addGuards(ifT,pred.unkeep(),false); // Up-cast predicate
- Node expr = parseStatement().keep(); // Parse loop body
+ parseStatement(); // Parse loop body
_scope.removeGuards(ifT);
// Merge the loop bottom into other continue statements
@@ -438,7 +421,7 @@ public class Parser {
_xScopes.pop();
_xScopes.push(exit);
_scope = exit;
- return peep(expr.unkeep());
+ return ZERO;
}
private ScopeNode jumpTo(ScopeNode toScope) {
@@ -696,7 +679,6 @@ public class Parser {
boolean xfinal = bindingFinal(t);
Lexer loc = loc();
String name = requireId();
-
// Optional initializing expression follows
Node expr;
if( match("=") ) {
@@ -709,7 +691,7 @@ public class Parser {
if( !xfinal ) t = inferredType(t); // Widen if not final
}
if( t instanceof TypeFunPtr && expr._type instanceof TypeFunPtr tfp && tfp.isConstant() )
- tfp.setName(name);
+ _code.link(tfp)._name = name;
} else {
// Need an expression to infer the type.
@@ -774,7 +756,7 @@ public class Parser {
s.addDef(_scope.in(i));
Var v = _scope._vars.at(i);
if( !v.isFRef() ) // Promote to outer scope, not defined here
- fs.push(Field.make(v._name,v.type(),ALIAS++,v._final));
+ fs.push(Field.make(v._name,v.type(),_code.getALIAS(),v._final));
}
TypeStruct ts = s._ts = TypeStruct.make(typeName, fs.asAry());
TYPES.put(typeName, TypeMemPtr.make(ts));
@@ -842,7 +824,7 @@ public class Parser {
int old2 = pos();
if( !match("!") ) match("~");
String id = _lexer.matchId();
- if( !(peek(',') || peek(';')) )
+ if( !(peek(',') || peek(';') || match("->")) )
return posT(old1);
pos(old2); // Reset lexer to reparse
if( id==null )
@@ -865,10 +847,10 @@ public class Parser {
if( t instanceof TypeMemPtr ptr && (ptr._obj._fields==null || !ptr._obj.isAry()) &&
ary._fields[1]._type instanceof TypeMemPtr old )
t = old.withAccess(ptr._ro);
- return TypeMemPtr.make(TypeStruct.makeAry(TypeInteger.BOT,ary._fields[0]._alias,t,ary._fields[1]._alias));
+ return TypeMemPtr.make(TypeStruct.makeAry(TypeInteger.U32,ary._fields[0]._alias,t,ary._fields[1]._alias));
}
// Need make an array type.
- TypeStruct ts = TypeStruct.makeAry(TypeInteger.BOT,ALIAS++,t,ALIAS++);
+ TypeStruct ts = TypeStruct.makeAry(TypeInteger.U32,_code.getALIAS(),t,_code.getALIAS());
assert ts.str().equals(tname);
TypeMemPtr tary = TypeMemPtr.make(ts);
TYPES.put(tname,tary);
@@ -1225,14 +1207,13 @@ public class Parser {
if( fs==null )
throw error("Unknown struct type '" + obj._name + "'");
Node prior = mem().keep();
- MemMergeNode input = new MemMergeNode(null);
+ MemMergeNode input = new MemMergeNode((Node)null);
for( Field f : fs ) input.alias(f._alias,prior);
- // Constructor values follow control, memory, and allocation size.
- Node[] ns = new Node[3+fs.length];
+ // Allocation zeroes its fields; nonzero initializers become explicit Stores.
+ Node[] ns = new Node[3];
ns[0] = ctrl();
ns[1] = input.peephole();
ns[2] = size;
- for( int i=0; i<fs.length; i++ ) ns[3+i] = init.get(idx+i);
Node nnn = new NewNode(TypeMemPtr.make(obj),ns).peephole().keep();
Node ptr = new ProjNode(nnn,0,obj._name).peephole().keep();
Node out = new ProjNode(nnn,1,"$mem").peephole().keep();
@@ -1242,6 +1223,14 @@ public class Parser {
out.unkeep();
nnn.unkeep();
prior.unkill();
+ for( int i=0; i<fs.length; i++ ) {
+ Node val = init.get(idx+i);
+ if( val._type != val._type.makeZero() ) {
+ Field f = fs[i];
+ Node st = new StoreNode(loc(),f._fname,f._alias,f._type,mem(),ptr,con(obj.offset(i)),val,true).peephole();
+ mem(new MemMergeNode(mem(),f._alias,st).peephole());
+ }
+ }
return ptr.unkeep();
}
@@ -1293,8 +1282,9 @@ public class Parser {
throw error("Accessing unknown field '" + name + "' from 'null'");
// Sanity check expr for being a reference
- if( !(expr._type instanceof TypeMemPtr ptr) )
- throw error("Expected reference but got " + expr._type.str());
+ if( !(expr._type instanceof TypeMemPtr ptr) ) {
+ throw error( "Expected "+(name=="#" || name=="[]" ? "array" : "reference")+" but found " + expr._type.str() );
+ }
// Sanity check field name for existing
TypeMemPtr tmp = (TypeMemPtr)TYPES.get(ptr._obj._name);
@@ -1330,7 +1320,7 @@ public class Parser {
// Arrays include control, as a proxy for a safety range check
// Structs don't need this; they only need a NPE check which is
// done via the type system.
- if( base.isAry() ) load.setDef(0,ctrl());
+ if( base.isAry() && !name.equals("#") ) load.setDef(0,ctrl());
load = peep(load);
// Check for assign-update, "ptr.fld += expr" or "ary[idx]++"
@@ -1393,7 +1383,7 @@ public class Parser {
}
require("->");
// Make a concrete function type, with a fidx
- TypeFunPtr tfp = TypeFunPtr.makeFun(TypeTuple.make(ts.asAry()),Type.BOTTOM);
+ TypeFunPtr tfp = _code.makeFun(TypeTuple.make(ts.asAry()),Type.BOTTOM);
ReturnNode ret = parseFunctionBody(tfp,loc,finals,ids.asAry());
return con(ret._fun.sig());
}
@@ -1427,6 +1417,14 @@ public class Parser {
// Unkeep them all
for( Node arg : args )
arg.unkeep();
+ // Dead into the call? Skip all the node gen
+ if( ctrl()._type == Type.XCONTROL ) {
+ for( Node arg : args )
+ if( arg.isUnused() )
+ arg.kill();
+ return con(Type.TOP);
+ }
+
// Into the call
CallNode call = (CallNode)new CallNode(loc(), args.asAry()).peephole();
@@ -1680,7 +1678,6 @@ public class Parser {
private String parseNumberString() {
int old = _position;
int len = Math.abs(isLongOrDouble());
- _position += len;
return new String(_input,old,len);
}
diff --git a/src/main/java/com/seaofnodes/simple/RegMask.java b/src/main/java/com/seaofnodes/simple/RegMask.java
new file mode 100644
index 00000000..073b472b
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/RegMask.java
@@ -0,0 +1,47 @@
+package com.seaofnodes.simple;
+
+import java.util.BitSet;
+
+// A "register mask" - 1 bit set for each allowed register. In addition "stack
+// slot" registers may be allowed, effectively making the set infinite.
+public class RegMask {
+
+ public static final RegMask EMPTY = new RegMask();
+
+ final BitSet _bits;
+
+ public RegMask(long x ) { _bits = BitSet.valueOf(new long[]{x}); }
+ public RegMask(long[] xs) { _bits = BitSet.valueOf( xs); }
+
+ public RegMask or(RegMask other) {
+ BitSet resultBits = (BitSet)this._bits.clone();
+ resultBits.or(other._bits);
+ return new RegMask(resultBits);
+ }
+
+ // Internal constructor
+ RegMask() { _bits = new BitSet(); }
+ RegMask(BitSet bs) { _bits = bs; }
+
+ public boolean get(int r) { return _bits.get(r); }
+
+ // Defensive writable copy
+ public RegMaskRW copy() { return new RegMaskRW( (BitSet)_bits.clone() ); }
+
+ boolean size1() { return _bits.cardinality()==1; }
+}
+
+class RegMaskRW extends RegMask {
+ public RegMaskRW() { super(); }
+ public RegMaskRW(long x) { super(new long[]{x}); }
+ public RegMaskRW(long[] xs) { super(xs); }
+ public RegMaskRW(BitSet bs) { super(bs); }
+ public void set(int r) { _bits.set(r); }
+ public void clr(int r) { _bits.clear(r); }
+ public void set(int r, boolean b) { _bits.set(r,b); }
+ public RegMaskRW and( RegMask r ) {
+ if( r==null ) return this;
+ _bits.and(r._bits);
+ return this;
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/SB.java b/src/main/java/com/seaofnodes/simple/SB.java
index 6f5a29ee..7d580c21 100644
--- a/src/main/java/com/seaofnodes/simple/SB.java
+++ b/src/main/java/com/seaofnodes/simple/SB.java
@@ -29,6 +29,23 @@ public final class SB {
public SB p( int s ) { _sb.append(s); return this; }
public SB p( long s ) { _sb.append(s); return this; }
public SB p( boolean s) { _sb.append(s); return this; }
+ // 4 hex digits
+ public SB hex4(int s) {
+ assert (s>>4*4)==0; // Fits in 16 bits
+ for( int i=0; i<4; i++ ) {
+ int digit = (s>>((3-i)*4)) & 0xf;
+ _sb.append((char)((digit <= 9 ? '0' : ('A'-10))+digit));
+ }
+ return this;
+ }
+ // Fixed width field
+ public SB fix( int sz, String s ) {
+ assert sz >=0;
+ for( int i=0; i<sz; i++ )
+ _sb.append( i < s.length() ? s.charAt(i) : ' ');
+ return this;
+ }
+
// Not spelled "p" on purpose: too easy to accidentally say "p(1.0)" and
// suddenly call the autoboxed version.
public SB pobj( Object s ) { _sb.append(s.toString()); return this; }
diff --git a/src/main/java/com/seaofnodes/simple/node/AddNode.java b/src/main/java/com/seaofnodes/simple/node/AddNode.java
index f88c2175..2dfedbb5 100644
--- a/src/main/java/com/seaofnodes/simple/node/AddNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/AddNode.java
@@ -47,7 +47,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).
@@ -120,6 +120,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 aa577f61..c0ccfd9b 100644
--- a/src/main/java/com/seaofnodes/simple/node/BoolNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/BoolNode.java
@@ -5,7 +5,6 @@ import com.seaofnodes.print.ExprPrinter;
import com.seaofnodes.simple.Parser;
import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.type.*;
-
import static com.seaofnodes.simple.type.TypeInteger.*;
abstract public class BoolNode extends Node {
@@ -14,7 +13,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(); }
@@ -32,14 +31,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;
}
@@ -50,7 +49,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 ) {
@@ -76,7 +75,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;
@@ -87,7 +86,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 "<" ; }
TypeInteger doOp(TypeInteger i1, TypeInteger i2) {
if( i1._max < i2._min ) return TRUE;
if( i1._min >= i2._max ) return FALSE;
@@ -98,7 +97,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 "<="; }
TypeInteger doOp(TypeInteger i1, TypeInteger i2) {
if( i1._max <= i2._min ) return TRUE;
if( i1._min > i2._max ) return FALSE;
@@ -112,4 +111,15 @@ 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<" ; }
+ 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/BulkMemPhiNode.java b/src/main/java/com/seaofnodes/simple/node/BulkMemPhiNode.java
index de5f8b4c..5d8a4aba 100644
--- a/src/main/java/com/seaofnodes/simple/node/BulkMemPhiNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/BulkMemPhiNode.java
@@ -1,6 +1,6 @@
package com.seaofnodes.simple.node;
-import com.seaofnodes.simple.IterPeeps;
+import com.seaofnodes.simple.CodeGen;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeMem;
import java.util.BitSet;
@@ -29,6 +29,8 @@ public class BulkMemPhiNode extends PhiNode {
public boolean isSplit(int alias) { return _aliases.get(alias); }
+ public BulkMemPhiNode(BulkMemPhiNode phi) { super(phi); _aliases = phi._aliases; }
+
@Override public String label() { return "BulkPhi"+_aliases; }
@Override
@@ -49,7 +51,7 @@ public class BulkMemPhiNode extends PhiNode {
// "Peek through" a MemMerge that covers this alias set on its default
for( int i=1; i<nIns(); i++ )
if( in(i) instanceof MemMergeNode mmm && canPeek(mmm) ) {
- setDef(i,IterPeeps.add(mmm.in(1)));
+ setDef(i,CodeGen.CODE.add(mmm.in(1)));
return this;
}
@@ -168,14 +170,14 @@ public class BulkMemPhiNode extends PhiNode {
// set. Queue the original memory inputs before bphi itself can peep
// away and hide those neighbor relationships.
for( int i=1; i<bphi.nIns(); i++ )
- IterPeeps.add(bphi.in(i));
+ CodeGen.CODE.add(bphi.in(i));
// Do not peephole bphi here, as BulkMemPhi can recursively start a
// second bulk rewrite before this one has finished. Let the worklist
// discover any further splits
bphi.setType(TypeMem.BOT);
Node bulk = bphi;
- IterPeeps.add(bulk);
- IterPeeps.add(mem);
+ CodeGen.CODE.add(bulk);
+ CodeGen.CODE.add(mem);
return aggregate(bulk,alias,mem);
}
@@ -201,8 +203,8 @@ public class BulkMemPhiNode extends PhiNode {
// Due to cycles, must set before calling peephole
mphi.setType(TypeMem.make(alias,Type.BOTTOM));
for( int i=1; i<nIns(); i++ )
- mphi.addDef(IterPeeps.add(preciseInput(in(i),alias)));
- return IterPeeps.add(mphi);
+ mphi.addDef(CodeGen.CODE.add(preciseInput(in(i),alias)));
+ return CodeGen.CODE.add(mphi);
}
MemPhiNode _findPhi(int alias) {
diff --git a/src/main/java/com/seaofnodes/simple/node/CFGNode.java b/src/main/java/com/seaofnodes/simple/node/CFGNode.java
index 49398838..383f18d0 100644
--- a/src/main/java/com/seaofnodes/simple/node/CFGNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/CFGNode.java
@@ -1,13 +1,13 @@
package com.seaofnodes.simple.node;
import com.seaofnodes.simple.CodeGen;
+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
* <p>
@@ -22,7 +22,14 @@ 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;
+ _idepthVersion = cfg._idepthVersion;
+ _ltree = cfg._ltree;
+ _pre = cfg._pre;
+ }
public CFGNode cfg(int idx) { return (CFGNode)in(idx); }
@@ -118,8 +125,8 @@ public abstract class CFGNode extends Node {
}
// 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 70238f8c..c338ff4c 100644
--- a/src/main/java/com/seaofnodes/simple/node/CProjNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/CProjNode.java
@@ -7,7 +7,7 @@ import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeMem;
import com.seaofnodes.simple.type.TypeTuple;
-public class CProjNode extends CFGNode implements MultiUse {
+public class CProjNode extends CFGNode {
// Which slice of the incoming multipart value
public final int _idx;
@@ -20,9 +20,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 protected ExprPrinter<Node> _print1(ExprPrinter<Node> p) { return p.p(_label); }
diff --git a/src/main/java/com/seaofnodes/simple/node/CallEndNode.java b/src/main/java/com/seaofnodes/simple/node/CallEndNode.java
index 2da09cfc..83c98b97 100644
--- a/src/main/java/com/seaofnodes/simple/node/CallEndNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/CallEndNode.java
@@ -2,10 +2,7 @@ package com.seaofnodes.simple.node;
import com.seaofnodes.print.ExprPrinter;
-import com.seaofnodes.simple.CodeGen;
-import com.seaofnodes.simple.IterPeeps;
-import com.seaofnodes.simple.Parser;
-import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.*;
import com.seaofnodes.simple.type.*;
/**
@@ -17,14 +14,15 @@ public class CallEndNode extends CFGNode implements MultiNode {
private boolean _folding;
public boolean folding() { return _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"; }
@Override public boolean isMultiHead() { return true; }
@Override public boolean blockHead() { return true; }
- CallNode call() { return (CallNode)in(0); }
+ public CallNode call() { return (CallNode)in(0); }
@Override
protected ExprPrinter<Node> _print1(ExprPrinter<Node> p) {
@@ -48,8 +46,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
@@ -74,7 +71,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);
CodeGen.CODE.invalidateIDepthCaches();
return this;
}
diff --git a/src/main/java/com/seaofnodes/simple/node/CallNode.java b/src/main/java/com/seaofnodes/simple/node/CallNode.java
index 10b04b38..22cef22d 100644
--- a/src/main/java/com/seaofnodes/simple/node/CallNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/CallNode.java
@@ -18,23 +18,29 @@ 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
- protected ExprPrinter<Node> _print1(ExprPrinter<Node> p) {
- String fname = null;
- Node fptr = fptr();
- if( fptr._type instanceof TypeFunPtr tfp && tfp.isConstant() )
- fname = tfp._name;
- if( fname==null ) p.n(fptr);
+ @Override protected ExprPrinter<Node> _print1(ExprPrinter<Node> p) {
+ String fname = name();
+ if( fname == null ) p.n(fptr());
else p.p(fname);
p.p("( ");
for( int i=2; i<nIns()-1; i++ )
p.n(in(i)).p(",");
return p.unchar(',').unchar(' ').close();
}
+ public String name() {
+ if( fptr()._type instanceof TypeFunPtr tfp && tfp.isConstant() && tfp.notNull() ) {
+ FunNode fun = CodeGen.CODE.link(tfp);
+ return fun==null ? null : fun._name;
+ }
+ return null;
+ }
+
+
Node ctrl() { return in(0); }
Node mem () { return in(1); }
@@ -44,6 +50,8 @@ public class CallNode extends CFGNode {
int nargs() { return nIns()-3; } // Minus control, memory, fptr
// args from input 2 to last; last is function input
public Node fptr() { return _inputs.last(); }
+ // Error if not a TFP
+ public TypeFunPtr tfp() { return (TypeFunPtr)fptr()._type; }
// Find the Call End from the Call
public CallEndNode cend() {
@@ -118,7 +126,7 @@ public class CallNode extends CFGNode {
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());
+ CodeGen.CODE.add(cend()).addDef(fun.ret());
assert linked(fun);
}
diff --git a/src/main/java/com/seaofnodes/simple/node/ConstantNode.java b/src/main/java/com/seaofnodes/simple/node/ConstantNode.java
index 02d0289b..89014205 100644
--- a/src/main/java/com/seaofnodes/simple/node/ConstantNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/ConstantNode.java
@@ -2,10 +2,11 @@ package com.seaofnodes.simple.node;
import com.seaofnodes.print.ExprPrinter;
+import com.seaofnodes.simple.CodeGen;
import com.seaofnodes.simple.Parser;
import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.type.Type;
-
+import com.seaofnodes.simple.type.TypeFunPtr;
/**
* A Constant node represents a constant value. At present, the only constants
@@ -19,12 +20,14 @@ import com.seaofnodes.simple.type.Type;
* <p>
* 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();
@@ -38,7 +41,13 @@ public class ConstantNode extends Node {
@Override public String label() { return "Con"; }
- @Override protected ExprPrinter<Node> _print1(ExprPrinter<Node> p) {
+ @Override
+ protected ExprPrinter<Node> _print1(ExprPrinter<Node> p) {
+ if( _con instanceof TypeFunPtr tfp && tfp.isConstant() && tfp.notNull() ) {
+ FunNode fun = CodeGen.CODE.link(tfp);
+ if( fun!=null && fun._name != null )
+ return p.p("{ ").p(fun._name).p("}");
+ }
return p.p(_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 9268381a..e647a68a 100644
--- a/src/main/java/com/seaofnodes/simple/node/CtrlNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/CtrlNode.java
@@ -2,11 +2,12 @@ package com.seaofnodes.simple.node;
import com.seaofnodes.print.ExprPrinter;
+import com.seaofnodes.simple.CodeGen;
import com.seaofnodes.simple.Parser;
import com.seaofnodes.simple.type.Type;
public class CtrlNode extends CFGNode {
- public CtrlNode() { super(Parser.START); }
+ public CtrlNode() { super(CodeGen.CODE._start); }
@Override public String label() { return "Ctrl"; }
@Override protected ExprPrinter<Node> _print1(ExprPrinter<Node> p) { return p.p("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 ec60f215..d24f4829 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 {
@@ -14,10 +15,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.
@@ -46,9 +50,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
@@ -59,6 +64,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.
@@ -67,16 +81,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;
}
// Unfolded functions are roots; inlining reconnects their depth to the caller.
diff --git a/src/main/java/com/seaofnodes/simple/node/IfNode.java b/src/main/java/com/seaofnodes/simple/node/IfNode.java
index a3061b63..a01eb8bd 100644
--- a/src/main/java/com/seaofnodes/simple/node/IfNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/IfNode.java
@@ -1,6 +1,7 @@
package com.seaofnodes.simple.node;
+import com.seaofnodes.simple.CodeGen;
import com.seaofnodes.simple.IterPeeps;
import com.seaofnodes.simple.type.*;
import java.util.HashSet;
@@ -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"; }
@@ -54,7 +56,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 5190bf08..a06d1291 100644
--- a/src/main/java/com/seaofnodes/simple/node/LoadNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/LoadNode.java
@@ -2,12 +2,10 @@ package com.seaofnodes.simple.node;
import com.seaofnodes.print.ExprPrinter;
+import com.seaofnodes.simple.CodeGen;
import com.seaofnodes.simple.Parser;
import com.seaofnodes.simple.Utils;
-import com.seaofnodes.simple.IterPeeps;
import com.seaofnodes.simple.type.*;
-import java.util.BitSet;
-import java.util.ArrayList;
/**
* Load represents extracting a value from inside a memory object,
@@ -65,29 +63,36 @@ public class LoadNode extends MemOpNode {
@Override
public Node idealize() {
if( mem() instanceof MemMergeNode merge ) {
- setDef(1,IterPeeps.add(merge.alias(_alias)));
+ setDef(1,CodeGen.CODE.add(merge.alias(_alias)));
return this;
}
Node ptr = ptr();
+ Node mem = mem();
// Simple Load-after-Store on same address.
if( mem() instanceof StoreNode st &&
_alias==st._alias && ptr == st.ptr() && off() == st.off() ) { // Must check same object
assert _name.equals(st._name); // Equiv class aliasing is perfect
- return castRO(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(0) ) // Must check same object
- return castRO(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().domLCA(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 ) {
mem.addDep(this);
@@ -97,7 +102,7 @@ public class LoadNode extends MemOpNode {
break;
case StoreNode st:
if( _alias==st._alias && 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( _alias==st._alias && !off()._type.join(st.off()._type).isHigh() && // Offsets overlap
!neverAlias(ptr,st.ptr()) ) // And might alias
@@ -114,8 +119,8 @@ 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.mem();// Bypass unrelated New
break;
@@ -156,6 +161,14 @@ 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();
+ assert zero.isa(_type); // Catch an uninitialized non-null field
+ return castRO(new ConstantNode(zero).peephole());
+ }
+
private Node ld( int idx ) {
Node mem = mem(), ptr = ptr();
LoadNode ld = new LoadNode(_loc,_name,_alias,_declaredType,mem.in(idx),ptr instanceof PhiNode && ptr.in(0)==mem.in(0) ? ptr.in(idx) : ptr,off());
@@ -206,29 +219,19 @@ public class LoadNode extends MemOpNode {
return rez;
}
- // Memory writers which must follow this read. A packaging node is not a
- // write, but can hide New's input, so follow this alias through aggregates.
- public ArrayList<Node> antiDeps() {
- ArrayList<Node> deps = new ArrayList<>();
- antiDeps(mem(),deps,new BitSet());
- return deps;
+ // 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;
+ if( shift==0 )
+ return val;
+ Node shf = con(shift);
+ Node shl = new ShlNode(null,val,shf.keep()).peephole();
+ return new SarNode(null,shl,shf.unkeep());
}
- private void antiDeps(Node mem, ArrayList<Node> deps, BitSet visit) {
- if( visit.get(mem._nid) ) return;
- visit.set(mem._nid);
- for( Node use : mem._outputs ) {
- if( use instanceof MemMergeNode merge ) {
- Node slice = _alias<merge.nIns() ? merge.in(_alias) : null;
- if( (slice==mem) || (slice==null && merge.in(1)==mem) )
- antiDeps(merge,deps,visit);
- } else if( use instanceof CallNode ||
- (use instanceof StoreNode st && st._alias==_alias) ||
- (use instanceof NewNode nnn && nnn.field(_alias)!=null) ||
- (use instanceof MemPhiNode phi && phi._alias==_alias) ||
- (use instanceof BulkMemPhiNode phi && !phi.isSplit(_alias)) ) {
- if( !deps.contains(use) ) deps.add(use);
- }
- }
- }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/LoopNode.java b/src/main/java/com/seaofnodes/simple/node/LoopNode.java
index f26df076..db353c1d 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..c1f007e8
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/MachConcreteNode.java
@@ -0,0 +1,27 @@
+package com.seaofnodes.simple.node;
+
+import com.seaofnodes.print.ExprPrinter;
+
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.Utils;
+import java.io.ByteArrayOutputStream;
+
+// 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 protected ExprPrinter<Node> _print1(ExprPrinter<Node> p) {
+ p.open().p(op()).p(",");
+ for( int i=1; i<nIns(); i++ )
+ (in(i)==null ? p.p(" ---") : p.n(in(i))).p(",");
+ return p.unchar(',').close();
+ }
+
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/MachNode.java b/src/main/java/com/seaofnodes/simple/node/MachNode.java
new file mode 100644
index 00000000..9f2fee85
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/MachNode.java
@@ -0,0 +1,49 @@
+package com.seaofnodes.simple.node;
+
+import com.seaofnodes.simple.*;
+import java.io.ByteArrayOutputStream;
+
+public interface MachNode {
+
+ // Easy access to the abstract Machine
+ default Machine machine() { return CodeGen.CODE._mach; }
+
+ // Run a post-instruction-selection action
+ default void postSelect() { }
+
+ // Register mask allowed on input i. 0 for no register.
+ abstract public RegMask regmap(int i);
+ // Register mask allowed as a result. 0 for no register.
+ abstract public RegMask outregmap();
+ // Multi-reg-defining machine op; idx comes from Proj.
+ // Sometimes these Projs have no uses, and just exist to kill a register.
+ default RegMask outregmap(int idx) { throw Utils.TODO(); }
+
+ // If this op updates the same register as input 'idx', e.g. many X86 "R1 +=
+ // R2" style ops. This forces the output live range to equal the input live
+ // range, so they both get the same register.
+
+ // This can hold true for projections, if the instruction updates many
+ // registers that it also takes in. Note that ops also use the same
+ // registers for both inputs and outputs but these are pinned and without any
+ // choices (e.g. always RCX for both an input and output) and are better
+ // served with just declaring as a fixed register.
+
+ // Returns either 0 for not-two-adr or the updated input; for projections
+ // this is their Multi's updated input.
+ default int twoAddress( ) { return 0; }
+
+ // Instructions cheaper to recreate than to spill, such as loading small constants
+ default boolean isClone() { return false; }
+ // Encoding is appended into the byte array; size is returned
+ abstract public int encoding(ByteArrayOutputStream bytes);
+
+ // Human-readable form appended to the SB. Things like the encoding,
+ // indentation, leading address or block labels not printed here.
+ // Just something like "R17=[R18+12] // Load array base".
+ // General form: "dst=src+src".
+ default void asm(CodeGen code, SB sb) { throw Utils.TODO(); }
+
+ // Human readable opcode; e.g. "ld4" or "call".
+ default String op() { throw Utils.TODO(); }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/MemMergeNode.java b/src/main/java/com/seaofnodes/simple/node/MemMergeNode.java
index 5cb908bb..5f4cf128 100644
--- a/src/main/java/com/seaofnodes/simple/node/MemMergeNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/MemMergeNode.java
@@ -2,7 +2,7 @@ package com.seaofnodes.simple.node;
import com.seaofnodes.print.ExprPrinter;
-import com.seaofnodes.simple.IterPeeps;
+import com.seaofnodes.simple.CodeGen;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeMem;
@@ -10,6 +10,8 @@ import com.seaofnodes.simple.type.TypeMem;
* memory: absent aliases are not covered, rather than having unknown contents.
*/
public class MemMergeNode extends Node {
+ public MemMergeNode(MemMergeNode mem) { super(mem); }
+
public MemMergeNode(Node bulk) { super(null,bulk); _type = TypeMem.BOT; }
public MemMergeNode(Node bulk, int alias, Node precise) {
this(bulk);
@@ -30,7 +32,7 @@ public class MemMergeNode extends Node {
setDef(alias,mem);
}
- // Scalar contents of one alias. New's field inputs supply the initialized type;
+ // Scalar contents of one alias. New zeroes the allocated fields;
// join it with the incoming contents for all previously allocated objects.
// Phis use their cached types, so this query does not recurse around loops.
static Type contents(Node mem, int alias, Node dep) {
@@ -40,8 +42,7 @@ public class MemMergeNode extends Node {
if( mem instanceof ProjNode proj && proj.in(0) instanceof NewNode nnn ) {
assert proj._idx==1 && nnn.field(alias)!=null;
nnn.addDep(dep);
- Node init = nnn.in(nnn.findAlias(alias)).addDep(dep);
- return contents(nnn.mem(),alias,dep).meet(init._type);
+ return contents(nnn.mem(),alias,dep).meet(nnn.field(alias)._type.makeZero());
}
// Function parameters and call results may contain arbitrary heap values.
// Their bulk memory types do not imply empty or zero-filled storage.
@@ -68,7 +69,7 @@ public class MemMergeNode extends Node {
for( int i=2; i<nIns(); i++ ) {
if( in(i)!=null && in(i)==in(1) ) { setDef(i,null); progress=true; }
if( in(i) instanceof MemMergeNode mem ) {
- setDef(i,IterPeeps.add(mem.alias(i)));
+ setDef(i,CodeGen.CODE.add(mem.alias(i)));
progress=true;
}
if( in(i)!=null ) allDefault=false;
@@ -79,7 +80,7 @@ public class MemMergeNode extends Node {
for( int i=2; i<mem.nIns(); i++ )
if( mem.in(i)!=null && (i>=nIns() || in(i)==null) )
alias(i,mem.in(i));
- IterPeeps.add(mem.in(1));
+ CodeGen.CODE.add(mem.in(1));
setDef(1,mem.in(1));
return this;
}
diff --git a/src/main/java/com/seaofnodes/simple/node/MemOpNode.java b/src/main/java/com/seaofnodes/simple/node/MemOpNode.java
index c09f2725..e9fccc74 100644
--- a/src/main/java/com/seaofnodes/simple/node/MemOpNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/MemOpNode.java
@@ -1,9 +1,14 @@
package com.seaofnodes.simple.node;
+import com.seaofnodes.print.ExprPrinter;
+
import com.seaofnodes.simple.Parser;
import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeMemPtr;
+import java.lang.StringBuilder;
+import java.util.BitSet;
+import java.util.ArrayList;
/**
* Convenience common base for Load and Store.
@@ -29,6 +34,13 @@ public abstract class MemOpNode extends Node {
this(loc,name, alias, glb, mem, ptr, off);
addDef(value);
}
+ public MemOpNode( Node mach, MemOpNode mop ) {
+ super(mach);
+ _name = mop._name;
+ _alias = mop._alias;
+ _declaredType = mop._declaredType;
+ _loc = mop._loc;
+ }
//
static String mlabel(String name) { return name.equals("[]") ? "ary" : (name.equals("#") ? "len" : name); }
@@ -38,6 +50,10 @@ public abstract class MemOpNode extends Node {
public Node ptr() { return in(2); }
public Node off() { return in(3); }
+ @Override protected ExprPrinter<Node> _print1(ExprPrinter<Node> p) { return _printMach(p); }
+ protected ExprPrinter<Node> _printMach(ExprPrinter<Node> p) { throw Utils.TODO(); }
+
+
// Extra conditions for factoring matching memory operations through a Phi.
// The caller has already checked the opcode, input shape and control.
boolean canDrop(MemOpNode other, Node dep) {
@@ -65,4 +81,29 @@ public abstract class MemOpNode extends Node {
return null;
return Parser.error( "Might be null accessing '" + _name + "'",_loc);
}
+ // Memory writers which must follow this read. A packaging node is not a
+ // write, but can hide New's input, so follow this alias through aggregates.
+ public ArrayList<Node> antiDeps() {
+ ArrayList<Node> deps = new ArrayList<>();
+ antiDeps(mem(),deps,new BitSet());
+ return deps;
+ }
+
+ private void antiDeps(Node mem, ArrayList<Node> deps, BitSet visit) {
+ if( visit.get(mem._nid) ) return;
+ visit.set(mem._nid);
+ for( Node use : mem._outputs ) {
+ if( use instanceof MemMergeNode merge ) {
+ Node slice = _alias<merge.nIns() ? merge.in(_alias) : null;
+ if( (slice==mem) || (slice==null && merge.in(1)==mem) )
+ antiDeps(merge,deps,visit);
+ } else if( use instanceof CallNode ||
+ (use instanceof MemOpNode st && st.isMem() && st._alias==_alias) ||
+ (use instanceof NewNode nnn && nnn.field(_alias)!=null) ||
+ (use instanceof MemPhiNode phi && phi._alias==_alias) ||
+ (use instanceof BulkMemPhiNode phi && !phi.isSplit(_alias)) ) {
+ if( !deps.contains(use) ) deps.add(use);
+ }
+ }
+ }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/MemPhiNode.java b/src/main/java/com/seaofnodes/simple/node/MemPhiNode.java
index 693ad944..38cbcd5b 100644
--- a/src/main/java/com/seaofnodes/simple/node/MemPhiNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/MemPhiNode.java
@@ -1,6 +1,6 @@
package com.seaofnodes.simple.node;
-import com.seaofnodes.simple.IterPeeps;
+import com.seaofnodes.simple.CodeGen;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeMem;
@@ -14,6 +14,8 @@ public class MemPhiNode extends PhiNode {
_alias = alias;
}
+ public MemPhiNode(MemPhiNode phi) { super(phi); _alias = phi._alias; }
+
@Override public String label() { return "MemPhi_"+_alias; }
@Override public Type compute() {
@@ -31,7 +33,7 @@ public class MemPhiNode extends PhiNode {
@Override public Node idealize() {
for( int i=1; i<nIns(); i++ )
if( in(i) instanceof MemMergeNode mem ) {
- setDef(i,IterPeeps.add(mem.alias(_alias)));
+ setDef(i,CodeGen.CODE.add(mem.alias(_alias)));
return this;
}
if( nIns()==3 && region() instanceof LoopNode loop && !loop.inProgress() &&
diff --git a/src/main/java/com/seaofnodes/simple/node/MulNode.java b/src/main/java/com/seaofnodes/simple/node/MulNode.java
index 2c1ba3de..720f15d7 100644
--- a/src/main/java/com/seaofnodes/simple/node/MulNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/MulNode.java
@@ -34,15 +34,33 @@ public class MulNode extends Node {
Type t1 = lhs._type;
Type t2 = rhs._type;
- // Mul of 1. We do not check for (1*x) because this will already
- // canonicalize to (x*1)
- if ( t2.isConstant() && t2 instanceof TypeInteger i && i.value()==1 && lhs._type instanceof TypeInteger )
- return lhs;
-
// Move constants to RHS: con*arg becomes arg*con
if ( t1.isConstant() && !t2.isConstant() )
return swap12();
+ // Multiply by constant
+ if ( t2.isConstant() && t2 instanceof TypeInteger i ) {
+ // Mul of 1. We do not check for (1*x) because this will already
+ // canonicalize to (x*1)
+ long c = i.value();
+ if( c==1 ) return lhs._type instanceof TypeInteger ? lhs : null;
+ if( c==0 ) return Parser.ZERO;
+ // Mul by a power of 2, +/-1. Bit patterns more complex than this
+ // are unlikely to win on an X86 vs the normal "imul", and so
+ // become machine-specific.
+ if( (c & (c-1)) == 0 )
+ return new ShlNode(null,lhs,con(Long.numberOfTrailingZeros(c)));
+ // 2**n + 1, e.g. 9
+ long d = c-1;
+ if( (d & (d-1)) == 0 )
+ return new AddNode(new ShlNode(null,lhs,con(Long.numberOfTrailingZeros(d))).peephole(),lhs);
+ // 2**n - 1, e.g. 31
+ long e = c+1;
+ if( (e & (e-1)) == 0 )
+ return new SubNode(new ShlNode(null,lhs,con(Long.numberOfTrailingZeros(e))).peephole(),lhs);
+
+ }
+
// Do we have ((x * (phi cons)) * con) ?
// Do we have ((x * (phi cons)) * (phi cons)) ?
// Push constant up through the phi: x * (phi con0*con0 con1*con1...)
diff --git a/src/main/java/com/seaofnodes/simple/node/MultiUse.java b/src/main/java/com/seaofnodes/simple/node/MultiUse.java
deleted file mode 100644
index 436d3432..00000000
--- a/src/main/java/com/seaofnodes/simple/node/MultiUse.java
+++ /dev/null
@@ -1,5 +0,0 @@
-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 cd639d7e..68871918 100644
--- a/src/main/java/com/seaofnodes/simple/node/NewNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/NewNode.java
@@ -4,7 +4,7 @@ import com.seaofnodes.print.ExprPrinter;
import com.seaofnodes.simple.type.*;
-/** Allocate and initialize an object. Inputs {ctrl, $mem, size, fields...};
+/** Allocate a zeroed object. Inputs {ctrl, $mem, size};
* results {ptr, $mem}.
* The memory input and result cover only the aliases in the allocated struct.
*/
@@ -16,11 +16,10 @@ public class NewNode extends Node implements MultiNode {
super(nodes);
assert !ptr.nullable();
_ptr = ptr;
- assert nodes.length==3+ptr._obj._fields.length;
+ assert nodes.length==3;
assert nodes[0]._type==Type.CONTROL || nodes[0]._type==Type.XCONTROL;
assert nodes[1]._type instanceof TypeMem;
assert nodes[2]._type instanceof TypeInteger || nodes[2]._type==Type.NIL;
- for( int i=3; i<nodes.length; i++ ) assert nodes[i]._type!=null;
}
public Node mem() { return in(1); }
@@ -32,18 +31,14 @@ public class NewNode extends Node implements MultiNode {
return null;
}
- @Override public String label() {
- return "new_"+(_ptr._obj.isAry() ? "ary_"+_ptr._obj._fields[1]._type.str() : _ptr._obj.str());
- }
+ public NewNode(NewNode nnn) { super(nnn); _ptr = nnn._ptr; }
+
+
+ @Override public String label() { return "new_"+(_ptr._obj.isAry() ? "ary_"+_ptr._obj._fields[1]._type.str() : _ptr._obj.str()); }
@Override protected ExprPrinter<Node> _print1(ExprPrinter<Node> p) {
return p.p("new ").p(_ptr._obj.str());
}
- // Find matching alias input
- int findAlias(int alias) {
- return 3+_ptr._obj.findAlias(alias);
- }
-
@Override
public TypeTuple compute() {
diff --git a/src/main/java/com/seaofnodes/simple/node/Node.java b/src/main/java/com/seaofnodes/simple/node/Node.java
index 114a652a..dd37b051 100644
--- a/src/main/java/com/seaofnodes/simple/node/Node.java
+++ b/src/main/java/com/seaofnodes/simple/node/Node.java
@@ -3,12 +3,13 @@ package com.seaofnodes.simple.node;
import com.seaofnodes.print.BaseNode;
import com.seaofnodes.simple.CodeGen;
-
import com.seaofnodes.simple.*;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeFloat;
+import com.seaofnodes.simple.type.TypeInteger;
import java.util.*;
import java.util.function.Function;
+import static com.seaofnodes.simple.CodeGen.CODE;
/**
* All Nodes in the Sea of Nodes IR inherit from the Node class.
@@ -43,16 +44,8 @@ public abstract class Node extends BaseNode<Node> implements Cloneable {
*/
public Type _type;
- /**
- * A private Global Static mutable counter, for unique node id generation.
- * To make the compiler multithreaded, this field will have to move into a TLS.
- * Starting with value 1, to avoid bugs confusing node ID 0 with uninitialized values.
- * */
- private static int UNIQUE_ID = 1;
- public static int UID() { return UNIQUE_ID; }
-
- protected Node(Node... inputs) {
- super(UNIQUE_ID++); // allocate unique dense ID
+ Node(Node... inputs) {
+ super(CODE.getUID()); // allocate unique dense ID
_inputs = new Ary<>(Node.class);
Collections.addAll(_inputs,inputs);
_outputs = new Ary<>(Node.class);
@@ -61,6 +54,18 @@ public abstract class Node extends BaseNode<Node> implements Cloneable {
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 ) {
+ super(CODE.getUID()); // allocate unique dense ID
+ assert CodeGen.CODE._phase == CodeGen.Phase.InstructionSelection;
+ _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();
@@ -119,10 +124,10 @@ public abstract class Node extends BaseNode<Node> implements Cloneable {
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
@@ -202,11 +207,9 @@ public abstract class Node extends BaseNode<Node> implements Cloneable {
_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
}
@@ -236,7 +239,7 @@ public abstract class Node extends BaseNode<Node> implements Cloneable {
n._inputs.set(idx,nnn);
n.moveDepsToWorklist(); // Rewiring can change a dependent query without changing type.
nnn.addUse(n);
- IterPeeps.addAll(n._outputs);
+ CODE.addAll(n._outputs);
}
kill();
}
@@ -249,7 +252,7 @@ public abstract class Node extends BaseNode<Node> implements Cloneable {
* Always returns some not-null Node (often this).
*/
public final Node peephole() {
- var obs = CodeGen.CODE == null || IterPeeps.midAssert() || CodeGen.CODE._phase == CodeGen.Phase.Parse && CodeGen.CODE.P == null ? null : CodeGen.CODE._obs;
+ var obs = CodeGen.CODE._midAssert ? null : CodeGen.CODE._obs;
if( obs != null ) obs.before(this);
Node n = peepholeOpt();
Node rez = n == null ? this : deadCodeElim(n._nid >= _nid ? n.peephole() : n);
@@ -276,19 +279,22 @@ public abstract class Node extends BaseNode<Node> implements Cloneable {
* </ul>
*/
public final Node peepholeOpt( ) {
- ITER_CNT++;
+ CODE.iterCnt();
// Compute initial or improved Type
Type old = setType(compute());
- // Replace constant computations from non-constants with a constant node.
+ // 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() )
- return ConstantNode.make(_type).peepholeOpt();
+ 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
@@ -306,10 +312,9 @@ public abstract class Node extends BaseNode<Node> implements Cloneable {
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.
@@ -353,7 +358,7 @@ public abstract class Node extends BaseNode<Node> implements Cloneable {
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;
}
@@ -432,9 +437,10 @@ public abstract class Node extends BaseNode<Node> implements Cloneable {
private Node addDep(Node dep, boolean forwards) {
// Running peepholes during the big assert cannot have side effects
// like adding dependencies.
- if( IterPeeps.midAssert() ) return this;
- var obs = CodeGen.CODE == null || IterPeeps.midAssert() || CodeGen.CODE._phase == CodeGen.Phase.Parse && CodeGen.CODE.P == null ? null : CodeGen.CODE._obs;
+ if( CODE._midAssert ) return this;
+ var obs = CodeGen.CODE._midAssert ? null : CodeGen.CODE._obs;
if( obs != null ) obs.dep(this, dep);
+ if( dep == null ) return this;
if( _deps==null ) _deps = new Ary<>(Node.class);
if( _deps .find(dep) != -1 ) return this; // Already on list
if( !forwards && _inputs .find(dep) != -1 ) return this; // No need for deps on immediate neighbors
@@ -446,14 +452,10 @@ public abstract class Node extends BaseNode<Node> implements Cloneable {
// 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<Node,Node> 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 ) {
@@ -481,7 +483,7 @@ public abstract class Node extends BaseNode<Node> implements Cloneable {
// 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
}
@@ -562,7 +564,7 @@ public abstract class Node extends BaseNode<Node> implements Cloneable {
Node n;
try { n = (Node)clone(); }
catch( Exception e ) { throw new RuntimeException(e); }
- n._nid = UNIQUE_ID++; // allocate unique dense ID
+ n._nid = CODE.getUID(); // allocate unique dense ID
n._inputs = new Ary<>(Node.class);
n._outputs = new Ary<>(Node.class);
n._deps = null;
@@ -573,28 +575,21 @@ public abstract class Node extends BaseNode<Node> implements Cloneable {
// 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;
- 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> E walk( Function<Node,E> pred ) {
- assert WVISIT.isEmpty();
- try { return _walk(pred); }
- finally { WVISIT.clear(); }
+ assert CODE._visit.isEmpty();
+ E rez = _walk(pred);
+ CODE._visit.clear();
+ return rez;
}
private <E> E _walk( Function<Node,E> 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 7f5924cc..c3a0cf21 100644
--- a/src/main/java/com/seaofnodes/simple/node/ParmNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/ParmNode.java
@@ -14,6 +14,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); }
@@ -21,7 +22,7 @@ public class ParmNode extends PhiNode {
@Override
protected ExprPrinter<Node> _print1(ExprPrinter<Node> p) {
- if( "main".equals(fun().sig()._name) && _label.equals("arg") )
+ if( "main".equals(fun()._name) && _label.equals("arg") )
return p.p("arg");
p.p("Parm_").p(_label).open();
for( Node in : _inputs ) p.n(in).p(",");
diff --git a/src/main/java/com/seaofnodes/simple/node/PhiNode.java b/src/main/java/com/seaofnodes/simple/node/PhiNode.java
index 9b5e1c26..6fb315b4 100644
--- a/src/main/java/com/seaofnodes/simple/node/PhiNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/PhiNode.java
@@ -14,9 +14,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() )
diff --git a/src/main/java/com/seaofnodes/simple/node/ProjNode.java b/src/main/java/com/seaofnodes/simple/node/ProjNode.java
index 1e192676..6ae4a60d 100644
--- a/src/main/java/com/seaofnodes/simple/node/ProjNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/ProjNode.java
@@ -7,7 +7,7 @@ import com.seaofnodes.simple.type.TypeMem;
import com.seaofnodes.simple.type.TypeTuple;
-public class ProjNode extends Node implements MultiUse {
+public class ProjNode extends Node {
// Which slice of the incoming multipart value
public final int _idx;
@@ -16,13 +16,13 @@ public class ProjNode extends Node implements MultiUse {
public final String _label;
public ProjNode(Node ctrl, int idx, String label) {
- super(ctrl);
+ super(new Node[]{ctrl});
_idx = idx;
_label = label;
}
+ public ProjNode(ProjNode p) { super(p); _idx = p._idx; _label = p._label; }
@Override public String label() { return _label; }
- @Override public int idx() { return _idx; }
@Override
protected ExprPrinter<Node> _print1(ExprPrinter<Node> p) {
diff --git a/src/main/java/com/seaofnodes/simple/node/ReadOnlyNode.java b/src/main/java/com/seaofnodes/simple/node/ReadOnlyNode.java
index fa4e0242..d79f2815 100644
--- a/src/main/java/com/seaofnodes/simple/node/ReadOnlyNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/ReadOnlyNode.java
@@ -10,6 +10,7 @@ import com.seaofnodes.simple.type.TypeMemPtr;
*/
public class ReadOnlyNode extends Node {
public ReadOnlyNode( Node n ) { super(null,n); }
+ public ReadOnlyNode( ReadOnlyNode n ) { super(n); }
@Override public String label() { return "ReadOnly"; }
@Override protected String format() { return "(const)%1"; }
diff --git a/src/main/java/com/seaofnodes/simple/node/RegionNode.java b/src/main/java/com/seaofnodes/simple/node/RegionNode.java
index 945398e9..e1f095a9 100644
--- a/src/main/java/com/seaofnodes/simple/node/RegionNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/RegionNode.java
@@ -2,20 +2,17 @@ package com.seaofnodes.simple.node;
import com.seaofnodes.print.ExprPrinter;
-import com.seaofnodes.simple.IterPeeps;
-import com.seaofnodes.simple.Parser;
-import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.*;
import com.seaofnodes.simple.type.Type;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
+import java.util.*;
public class RegionNode extends CFGNode {
// Source location for late discovered errors
public Parser.Lexer _loc;
- public RegionNode(Node... nodes) { this(null,nodes); }
- public RegionNode(Parser.Lexer loc, Node... nodes) { super(nodes); _loc = loc; }
+ public RegionNode(Parser.Lexer loc, Node... nodes) { super(nodes); _loc = loc; }
+ public RegionNode(RegionNode r, Parser.Lexer loc) { super(r); _loc = loc; }
+ public RegionNode(RegionNode r) { super(r); _loc = r._loc; }
@Override
public String label() { return "Region"; }
@@ -39,30 +36,10 @@ public class RegionNode extends CFGNode {
@Override
public Node idealize() {
if( inProgress() ) return null;
- // Delete dead paths into a Region
- int path = findDeadInput();
- if( path != 0 &&
- // Do not delete the entry path of a loop (ok to remove the back
- // edge and make the loop a single-entry Region which folds away
- // the Loop). Folding the entry path confused the loop structure,
- // moving the backedge to the entry point.
- !(this instanceof LoopNode loop && loop.entry()==in(path)) ) {
- // Cannot use the obvious output iterator here, because a Phi
- // deleting an input might recursively delete *itself*. This
- // shuffles the output array, and we might miss iterating an
- // unrelated Phi. So on rare occasions we repeat the loop to get
- // all the Phis.
- int nouts = 0;
- while( nouts != nOuts() ) {
- nouts = nOuts();
- for( int i=0; i<nOuts(); i++ )
- if( out(i) instanceof PhiNode phi && phi.nIns()==nIns() ) {
- phi.delDef(path);
- IterPeeps.addAll(phi._outputs);
- }
- }
- return isDead() ? Parser.XCTRL : delDef(path);
- }
+
+ Node progress = deadPath();
+ if( progress!=null ) return progress;
+
// If down to a single input, become that input
if( nIns()==2 && !hasPhi() )
return in(1); // Collapse if no Phis; 1-input Phis will collapse on their own
@@ -83,6 +60,31 @@ public class RegionNode extends CFGNode {
return null;
}
+ Node deadPath() {
+ // Delete dead paths into a Region
+ int path = findDeadInput();
+ if( path==0 ) return null;
+ // Do not delete the entry path of a loop (ok to remove the back edge
+ // and make the loop a single-entry Region which folds away the Loop).
+ // Folding the entry path confused the loop structure, moving the
+ // backedge to the entry point.
+ if( this instanceof LoopNode loop && loop.entry()==in(path) )
+ return null;
+ // Cannot use the obvious output iterator here, because a Phi deleting
+ // an input might recursively delete *itself*. This shuffles the
+ // output array, and we might miss iterating an unrelated Phi. So on
+ // rare occasions we repeat the loop to get all the Phis.
+ int nouts = 0;
+ while( nouts != nOuts() ) {
+ nouts = nOuts();
+ for( int i=0; i<nOuts(); i++ )
+ if( out(i) instanceof PhiNode phi && phi.nIns()==nIns() )
+ CodeGen.CODE.addAll(phi.delDef(path)._outputs);
+ }
+ return isDead() ? Parser.XCTRL : delDef(path);
+ }
+
+
private int findDeadInput() {
for( int i=1; i<nIns(); i++ )
if( in(i)._type==Type.XCONTROL )
diff --git a/src/main/java/com/seaofnodes/simple/node/ReturnNode.java b/src/main/java/com/seaofnodes/simple/node/ReturnNode.java
index d4b4094d..0716aa36 100644
--- a/src/main/java/com/seaofnodes/simple/node/ReturnNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/ReturnNode.java
@@ -10,12 +10,13 @@ import com.seaofnodes.simple.type.*;
/** Keeps all preceding memory effects and the return value alive: {ctrl, $mem, value, rpc}. */
public class ReturnNode extends CFGNode {
- public final FunNode _fun;
+ public FunNode _fun;
public ReturnNode(Node ctrl, Node mem, Node data, Node rpc, FunNode fun ) {
super(ctrl, mem, data, rpc);
_fun = fun;
}
+ public ReturnNode( ReturnNode ret, FunNode fun ) { super(ret); _fun = fun; }
public Node ctrl() { return in(0); }
public Node mem () { return in(1); }
diff --git a/src/main/java/com/seaofnodes/simple/node/ScopeNode.java b/src/main/java/com/seaofnodes/simple/node/ScopeNode.java
index de199d2d..881f65d6 100644
--- a/src/main/java/com/seaofnodes/simple/node/ScopeNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/ScopeNode.java
@@ -271,7 +271,7 @@ public class ScopeNode extends Node {
RegionNode r = ctrl(new RegionNode(loc,null,ctrl(), that.ctrl()).keep());
_merge(that,r);
that.kill(); // Kill merged scope
- IterPeeps.add(r);
+ CodeGen.CODE.add(r);
return r.unkeep();
}
@@ -336,7 +336,7 @@ public class ScopeNode extends Node {
if( in(i) instanceof PhiNode phi ) {
// Do an eager useless-phi removal
Node in = phi.peephole();
- IterPeeps.addAll(phi._outputs);
+ CodeGen.CODE.addAll(phi._outputs);
phi.moveDepsToWorklist();
if( in != phi ) {
if( !phi.iskeep() ) // Keeping phi around for parser elsewhere
@@ -370,7 +370,7 @@ public class ScopeNode extends Node {
}
// Invert the If conditional
if( invert )
- pred = pred instanceof NotNode not ? not.in(1) : IterPeeps.add(new NotNode(pred).peephole());
+ pred = pred instanceof NotNode not ? not.in(1) : CodeGen.CODE.add(new NotNode(pred).peephole());
// This is a zero/null test.
// Compute the positive test type.
Type tnz = pred._type.nonZero();
diff --git a/src/main/java/com/seaofnodes/simple/node/StartNode.java b/src/main/java/com/seaofnodes/simple/node/StartNode.java
index 8bdb60cf..d1151090 100644
--- a/src/main/java/com/seaofnodes/simple/node/StartNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/StartNode.java
@@ -2,6 +2,8 @@ package com.seaofnodes.simple.node;
import com.seaofnodes.print.ExprPrinter;
+import com.seaofnodes.simple.CodeGen;
+import com.seaofnodes.simple.Parser;
import com.seaofnodes.simple.type.*;
import java.util.HashSet;
import static com.seaofnodes.simple.Utils.TODO;
@@ -11,7 +13,8 @@ public class StartNode extends LoopNode implements MultiNode {
final Type _arg;
- public StartNode(Type arg) { super(null,null); _arg = arg; _type = compute(); }
+ public StartNode(Type arg) { super((Parser.Lexer)null,null); _arg = arg; _type = compute(); }
+ public StartNode(StartNode start) { super(start); _arg = start._arg; }
@Override public String label() { return "Start"; }
@@ -29,7 +32,7 @@ public class StartNode extends LoopNode implements MultiNode {
// Find "main", its the start.
CFGNode C = null;
for( Node use : _outputs )
- if( use instanceof FunNode fun && fun.sig().isa(TypeFunPtr.MAIN) )
+ if( use instanceof FunNode fun && fun.sig().isa(CodeGen.CODE._main) )
{ assert C==null; C = fun; }
return C;
}
diff --git a/src/main/java/com/seaofnodes/simple/node/StopNode.java b/src/main/java/com/seaofnodes/simple/node/StopNode.java
index a15125a1..01199e9b 100644
--- a/src/main/java/com/seaofnodes/simple/node/StopNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/StopNode.java
@@ -14,6 +14,7 @@ public class StopNode extends CFGNode {
_src = src;
_type = compute();
}
+ public StopNode(StopNode stop) { super(stop); _src = stop._src; }
@Override
public String label() {
diff --git a/src/main/java/com/seaofnodes/simple/node/StoreNode.java b/src/main/java/com/seaofnodes/simple/node/StoreNode.java
index de17bbad..86c759bd 100644
--- a/src/main/java/com/seaofnodes/simple/node/StoreNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/StoreNode.java
@@ -2,10 +2,7 @@ package com.seaofnodes.simple.node;
import com.seaofnodes.print.ExprPrinter;
-import com.seaofnodes.simple.IterPeeps;
-import com.seaofnodes.simple.Parser;
-import com.seaofnodes.simple.Utils;
-import com.seaofnodes.simple.IterPeeps;
+import com.seaofnodes.simple.*;
import com.seaofnodes.simple.type.*;
/**
@@ -55,7 +52,7 @@ public class StoreNode extends MemOpNode {
@Override
public Node idealize() {
if( mem() instanceof MemMergeNode merge ) {
- setDef(1,IterPeeps.add(merge.alias(_alias)));
+ setDef(1,CodeGen.CODE.add(merge.alias(_alias)));
return this;
}
@@ -74,33 +71,20 @@ public class StoreNode extends MemOpNode {
return this;
}
- // Simple store-after-new on same address. Should pick up
- // an init-store being stomped by a first user store.
- if( mem() instanceof ProjNode st && st .in(0) instanceof NewNode nnn &&
- ptr() instanceof ProjNode ptr && ptr.in(0) == nnn &&
- ptr()._type instanceof TypeMemPtr tmp && // No bother if weird dead pointers
- // Cannot fold a store of a single element over the array body initializer value
- !(tmp._obj.isAry() && tmp._obj._fields[1]._alias==_alias) &&
- // Very sad strong cutout: val has to be legal to hoist to a New
- // input, which means it cannot depend on the New. Many, many
- // things are legal here but difficult to check without doing a
- // full dominator check. Example failure:
- // "struct C { C? c; }; C self = new C { c=self; }"
- val()._type.isHighOrConst() &&
- // Must have exactly one use of "this" or you get weird
- // non-serializable memory effects in the worse case.
- checkOnlyUse(st) &&
- // Folding away a broken store
- err()==null ) {
- nnn.setDef(nnn.findAlias(_alias),val());
- // Must *retype* the NewNode, this is not monotonic in isolation
- // but is monotonic counting from this Store to the New.
- nnn ._type = nnn .compute();
- mem()._type = mem().compute();
- IterPeeps.addAll(nnn._outputs);
- return mem();
+ // Value is automatically truncated by narrow store
+ if( val() instanceof AndNode and && and.in(2)._type.isConstant() ) {
+ int log = _declaredType.log_size();
+ if( log<3 ) { // And-mask vs narrow store
+ long mask = ((TypeInteger)and.in(2)._type).value();
+ long bits = (1L<<(8<<log))-1;
+ // Mask does not mask any of the stored bits
+ if( (bits&mask)==bits )
+ // So and-mask is already covered by the store
+ { setDef(4,and.in(1)); return this; }
+ }
}
+
return null;
}
@@ -162,9 +146,10 @@ public class StoreNode extends MemOpNode {
if( ptr()._type == Type.TOP )
return null; // Dead store
TypeMemPtr tmp = (TypeMemPtr)ptr()._type;
- if( tmp._ro || (tmp._obj.field(_name)._final && !"[]".equals(_name)) )
+ if( (tmp._ro || tmp._obj.field(_name)._final) && !_init )
return Parser.error("Cannot modify final field '"+_name+"'",_loc);
Type t = val()._type;
- return _init || t.isa(_declaredType) ? null : Parser.error("Cannot store "+t+" into field "+_declaredType+" "+_name,_loc);
+ //return _init || t.isa(_declaredType) ? null : Parser.error("Cannot store "+t+" into field "+_declaredType+" "+_name,_loc);
+ return null;
}
}
diff --git a/src/main/java/com/seaofnodes/simple/node/XCtrlNode.java b/src/main/java/com/seaofnodes/simple/node/XCtrlNode.java
index 3eae8548..891291d0 100644
--- a/src/main/java/com/seaofnodes/simple/node/XCtrlNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/XCtrlNode.java
@@ -2,11 +2,12 @@ package com.seaofnodes.simple.node;
import com.seaofnodes.print.ExprPrinter;
+import com.seaofnodes.simple.CodeGen;
import com.seaofnodes.simple.Parser;
import com.seaofnodes.simple.type.Type;
public class XCtrlNode extends CFGNode {
- public XCtrlNode() { super(Parser.START); }
+ public XCtrlNode() { super(new Node[]{CodeGen.CODE._start}); }
@Override public String label() { return "Xctrl"; }
@Override protected ExprPrinter<Node> _print1(ExprPrinter<Node> p) { return p.p("Xctrl"); }
@Override public boolean isConst() { return true; }
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddFRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddFRISC.java
new file mode 100644
index 00000000..bc959977
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddFRISC.java
@@ -0,0 +1,30 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+public class AddFRISC extends MachConcreteNode implements MachNode{
+ AddFRISC(Node addf) {super(addf);}
+
+ // Register mask allowed on input i.
+ @Override public RegMask regmap(int i) { assert i==1 || i==2; return riscv.FMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return riscv.FMASK; }
+ // 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();
+ }
+
+ // Default on double precision for now(64 bits)
+ // General form: "fadd.d rd = src1 + src2
+ @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 "addf"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddIRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddIRISC.java
new file mode 100644
index 00000000..af4bf08d
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddIRISC.java
@@ -0,0 +1,39 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+public class AddIRISC extends MachConcreteNode implements MachNode {
+ final TypeInteger _ti;
+ AddIRISC( Node add, TypeInteger ti ) {
+ super(add);
+ _inputs.pop();
+ _ti = ti;
+ }
+
+ // Register mask allowed on input i.
+ @Override public RegMask regmap(int i) {
+ // assert i== i;
+ return riscv.RMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return riscv.RMASK; }
+ // Output is same register as input#1
+ @Override public int twoAddress() { return 0; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form: "addi rd = rs1 + 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 _ti.value() == 1 ? "inc" : (_ti.value() == -1 ? "dec" : "addi");
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddRISC.java
new file mode 100644
index 00000000..482b7f11
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddRISC.java
@@ -0,0 +1,36 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+
+public class AddRISC extends MachConcreteNode implements MachNode {
+
+ AddRISC( Node add) {super(add); }
+
+ // Register mask allowed on input i.
+ @Override public RegMask regmap(int i) {
+ assert i== 1 || i == 2 || i == 3;
+ return riscv.RMASK;
+ }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return riscv.RMASK; }
+ // Output is same register as input#1
+ @Override public int twoAddress() { return 0; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form: "rd = rs1 + rs2"
+ @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 "add";}
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndIRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndIRISC.java
new file mode 100644
index 00000000..29c2a686
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndIRISC.java
@@ -0,0 +1,40 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+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 AndIRISC extends MachConcreteNode implements MachNode{
+ final TypeInteger _ti;
+ AndIRISC(AndNode and, TypeInteger ti) {
+ super(and);
+ _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 || i == 2; return riscv.RMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return riscv.RMASK; }
+
+ // Output is same register as input#1
+ @Override public int twoAddress() { return 0; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form
+ // General form: "andi rd = rs1 & 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 "andi"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndRISC.java
new file mode 100644
index 00000000..e697de36
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndRISC.java
@@ -0,0 +1,41 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.node.cpus.x86_64_v2.x86_64_v2;
+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 AndRISC extends MachConcreteNode implements MachNode{
+ AndRISC(Node and) {
+ super(and);
+ }
+
+ // Register mask allowed on input i.
+ // This is the normal calling convention
+ @Override public RegMask regmap(int i) {
+ assert i==1 || i==2;
+ return riscv.RMASK;
+ }
+
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return riscv.RMASK; }
+
+ // Output is same register as input#1
+ @Override public int twoAddress() { return 0; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form
+ // General form: #rd = rs1 & rs2
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1)));
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CBranchRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CBranchRISC.java
new file mode 100644
index 00000000..d7e23c6d
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CBranchRISC.java
@@ -0,0 +1,49 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+
+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;
+// Conditional branch such as: BEQ
+public class CBranchRISC extends IfNode implements MachNode{
+ final String _bop;
+ // label is obtained implicitly
+ CBranchRISC( IfNode iff, String bop ) {
+ super(iff);
+ _bop = bop;
+ }
+
+ @Override public String label() { return op(); }
+
+ @Override public void postSelect() {
+ Node set = in(1);
+ Node cmp = set.in(1);
+ // Bypass an expected Set and just reference the cmp directly
+ if( set instanceof SetRISC)
+ _inputs.set(1,cmp);
+ else
+ throw Utils.TODO();
+ }
+
+ @Override public RegMask regmap(int i) { assert i==1; return riscv.RMASK; }
+ @Override public RegMask outregmap() { return null; }
+
+ // 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) {
+ String src = code.reg(in(1));
+ if( src!="FLAGS" ) sb.p(src);
+ }
+
+ @Override public String op() { return "b"+_bop; }
+
+ @Override public String comment() {
+ return "L"+cproj(1)._nid+", L"+cproj(0)._nid;
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRISC.java
new file mode 100644
index 00000000..7f062104
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRISC.java
@@ -0,0 +1,37 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.TypeFunPtr;
+import java.io.ByteArrayOutputStream;
+
+public class CallRISC extends CallNode implements MachNode{
+ final TypeFunPtr _tfp;
+ final String _name;
+ CallRISC( CallNode call, TypeFunPtr tfp ) {
+ super(call);
+ _inputs.pop(); // Pop constant target
+ assert tfp.isConstant();
+ _tfp = tfp;
+ _name = CodeGen.CODE.link(tfp)._name;
+ }
+
+ @Override public String label() { return op(); }
+ @Override public RegMask regmap(int i) {
+ return riscv.callInMaskInt(i); // Normal argument
+ }
+ @Override public RegMask outregmap() { return riscv.RET_MASK; }
+
+ @Override public String name() { return _name; }
+
+ // 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(_name);
+ }
+
+ @Override public String op() { return "call"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRRISC.java
new file mode 100644
index 00000000..02850582
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRRISC.java
@@ -0,0 +1,31 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+
+import java.io.ByteArrayOutputStream;
+
+public class CallRRISC extends CallNode implements MachNode{
+ CallRRISC( CallNode call ) { super(call); }
+
+ @Override public String label() { return op(); }
+ @Override public RegMask regmap(int i) {
+ // Todo: float or int?
+ return i==_inputs._len
+ ? riscv.RMASK // Function call target
+ : riscv.callInMaskInt(i); // Normal argument
+ }
+ @Override public RegMask outregmap() { return riscv.RET_MASK; }
+
+ // 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(fptr()));
+ }
+
+ @Override public String op() { return "callr"; }
+
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CmpIRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CmpIRISC.java
new file mode 100644
index 00000000..39fa3a29
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CmpIRISC.java
@@ -0,0 +1,44 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+
+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.(RFLAGS)
+public class CmpIRISC extends MachConcreteNode implements MachNode{
+ final int _imm;
+ final String _bop;
+ CmpIRISC( BoolNode bool, TypeInteger ti ) {
+ super(bool);
+ _inputs.pop(); // Toss ideal ConstantNode away, embedded into machine op
+ _bop = bool.op(); // One of <,<=,==
+ _imm = (int)ti.value();
+ assert _imm == ti.value();
+ }
+ CmpIRISC( Node cmp, double ignore ) {
+ super(cmp);
+ _bop = "==";
+ _imm = 0;
+ }
+
+ @Override public RegMask regmap(int i) { assert i==1; return riscv.RMASK; }
+ @Override public RegMask outregmap() { return riscv.FLAGS_MASK; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form: "cmp rs1, 1"
+ @Override public void asm(CodeGen code, SB sb) {
+ String dst = code.reg(this);
+ if( dst!="FLAGS" ) sb.p(dst).p(" = ");
+ sb.p(code.reg(in(1)));
+ if( _imm != 0 ) sb.p(", #").p(_imm);
+ }
+
+ @Override public String op() { return _imm==0 ? "test" : "cmp"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CmpRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CmpRISC.java
new file mode 100644
index 00000000..3bd4008e
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CmpRISC.java
@@ -0,0 +1,28 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeFunPtr;
+import java.io.ByteArrayOutputStream;
+
+// Compare. Sets flags.(RFLAGS)
+public class CmpRISC extends MachConcreteNode implements MachNode{
+ CmpRISC( Node cmp ) { super(cmp); }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+
+ // General form: "cmp rs1, rs2"
+ @Override public void asm(CodeGen code, SB sb) {
+ String dst = code.reg(this);
+ if( dst!="FLAGS" ) sb.p(dst).p(" = ");
+ sb.p(code.reg(in(1))).p(", ").p(code.reg(in(2)));
+ }
+
+ @Override public RegMask regmap(int i) { assert i==1 || i==2; return riscv.RMASK; }
+ @Override public RegMask outregmap() { return riscv.FLAGS_MASK; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivFRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivFRISC.java
new file mode 100644
index 00000000..deb2628d
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivFRISC.java
@@ -0,0 +1,30 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import java.io.ByteArrayOutputStream;
+
+public class DivFRISC extends MachConcreteNode implements MachNode{
+ DivFRISC(Node divf) {super(divf);}
+
+ // Register mask allowed on input i.
+ @Override public RegMask regmap(int i) { assert i==1 || i==2; return riscv.FMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return riscv.FMASK; }
+ // 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();
+ }
+
+ // Default on double precision for now(64 bits)
+ // General form: "fdiv.d rd = src1 / src2
+ @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 "divf"; }
+
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivRISC.java
new file mode 100644
index 00000000..98a8406f
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivRISC.java
@@ -0,0 +1,33 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.node.cpus.x86_64_v2.x86_64_v2;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+public class DivRISC extends MachConcreteNode implements MachNode{
+ DivRISC(Node div) {super(div);}
+
+ // Register mask allowed on input i.
+ @Override public RegMask regmap(int i) {
+ assert i==1 || i==2;
+ return riscv.FMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return x86_64_v2.RMASK; }
+ // 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();
+ }
+
+ // Signed division
+ // General form: "rd = rs1 / rs"
+ @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 "div"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/F8ToIRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/F8ToIRISC.java
new file mode 100644
index 00000000..261a1dcd
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/F8ToIRISC.java
@@ -0,0 +1,27 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.node.cpus.x86_64_v2.x86_64_v2;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+public class F8ToIRISC extends MachConcreteNode implements MachNode {
+ F8ToIRISC(Node f8toi) {super(f8toi);}
+
+ // Register mask allowed on input i.
+ @Override public RegMask regmap(int i) { assert i==1; return riscv.FMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return riscv.RMASK; }
+
+ // 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)).p(" = ").p("(int)").p(code.reg(in(1)));
+ }
+
+ @Override public String op() { return "f8toi"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FltRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FltRISC.java
new file mode 100644
index 00000000..e1637eec
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FltRISC.java
@@ -0,0 +1,39 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+
+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.ConstantNode;
+import com.seaofnodes.simple.node.MachNode;
+import com.seaofnodes.simple.node.cpus.x86_64_v2.x86_64_v2;
+
+import java.io.ByteArrayOutputStream;
+
+public class FltRISC extends ConstantNode implements MachNode{
+ FltRISC(ConstantNode con) {
+ super(con);
+ }
+ // Register mask allowed on input i. 0 for no register.
+ @Override public RegMask regmap(int i) { return null; }
+ // General int registers
+ @Override public RegMask outregmap() { return riscv.FMASK; }
+
+ // 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) {
+ _con.print(sb.p(code.reg(this)).p(" #"));
+ }
+
+ @Override public String op() {
+ return "flw"; // Some fancier encoding
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FunRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FunRISC.java
new file mode 100644
index 00000000..66e32e29
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FunRISC.java
@@ -0,0 +1,22 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.simple.RegMask;
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.node.FunNode;
+import com.seaofnodes.simple.node.MachNode;
+import com.seaofnodes.simple.node.cpus.x86_64_v2.FunX86;
+
+import java.io.ByteArrayOutputStream;
+
+public class FunRISC extends FunNode implements MachNode{
+ FunRISC(FunNode fun) {super(fun);}
+ // Register mask allowed on input i. 0 for no register.
+ @Override public RegMask regmap(int i) { return null; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return null; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/I2F8RISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/I2F8RISC.java
new file mode 100644
index 00000000..df005e86
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/I2F8RISC.java
@@ -0,0 +1,29 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.node.cpus.x86_64_v2.x86_64_v2;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+// fcvt.d.w
+public class I2F8RISC extends MachConcreteNode implements MachNode {
+ I2F8RISC(Node i2f8) {super(i2f8);}
+
+ // Register mask allowed on input i.
+ @Override public RegMask regmap(int i) { assert i==1; return riscv.RMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return riscv.FMASK; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form: "i2f8 (flt)int_value"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p("(flt)").p(code.reg(in(1)));
+ }
+
+ @Override public String op() { return "i2f8"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/IntRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/IntRISC.java
new file mode 100644
index 00000000..bf170b22
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/IntRISC.java
@@ -0,0 +1,43 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+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.TypeInteger;
+
+import java.io.ByteArrayOutputStream;
+// Integer constants
+public class IntRISC extends ConstantNode implements MachNode {
+ IntRISC(ConstantNode con) {
+ super(con);
+ }
+
+ // Register mask allowed on input i. 0 for no register.
+ @Override public RegMask regmap(int i) { return null; }
+ // General int registers
+ @Override public RegMask outregmap() { return riscv.RMASK; }
+
+ // 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 || _con == TypeInteger.ZERO )
+ sb.p(reg).p(",").p(reg);
+ else
+ _con.print(sb.p(reg).p(" #"));
+ }
+
+ @Override public String op() {
+ if( _con == Type.NIL || _con == TypeInteger.ZERO )
+ return "xor";
+ return "ldi"; // Some fancier encoding
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/LoadRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/LoadRISC.java
new file mode 100644
index 00000000..846e313c
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/LoadRISC.java
@@ -0,0 +1,34 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+public class LoadRISC extends MemOpRISC{
+ LoadRISC(LoadNode ld) {
+ super(ld, ld);
+ }
+
+ @Override public RegMask regmap(int i) {
+ // wider mask, encode it differently, immediate handled with hard split(in RA)
+ return riscv.RMASK.or(riscv.FMASK);
+ }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return riscv.RMASK; }
+
+
+ // 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) {
+ }
+
+ @Override public String op() {
+ return "lw" +_sz;
+ }
+}
+
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MemOpRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MemOpRISC.java
new file mode 100644
index 00000000..3bf1cf09
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MemOpRISC.java
@@ -0,0 +1,39 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.print.ExprPrinter;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.node.cpus.x86_64_v2.x86_64_v2;
+import com.seaofnodes.simple.type.*;
+import java.io.ByteArrayOutputStream;
+import java.lang.StringBuilder;
+
+
+public class MemOpRISC extends MemOpNode implements MachNode{
+ final char _sz = (char)('0'+(1<<_declaredType.log_size()));
+ MemOpRISC(Node op, MemOpNode mop) {
+ super(op, mop);
+ }
+ @Override protected ExprPrinter<Node> _printMach(ExprPrinter<Node> p) { return p.p(".").p(_name); }
+
+ @Override public boolean isMem() { return _type instanceof TypeMem; }
+
+ @Override public String label() { return op(); }
+ @Override public Type compute() { throw Utils.TODO(); }
+ @Override public Node idealize() { throw Utils.TODO(); }
+
+ // Register mask allowed on input i.
+ @Override public RegMask regmap(int i) {
+ throw Utils.TODO();
+ }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { throw Utils.TODO(); }
+
+ @Override public int encoding(ByteArrayOutputStream bytes) { throw Utils.TODO(); }
+
+
+ SB asm_address(CodeGen code, SB sb) {
+ return sb;
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulFRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulFRISC.java
new file mode 100644
index 00000000..43197b50
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulFRISC.java
@@ -0,0 +1,30 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import java.io.ByteArrayOutputStream;
+
+public class MulFRISC extends MachConcreteNode implements MachNode{
+ MulFRISC(Node mulf) {super(mulf);}
+
+ // Register mask allowed on input i.
+ @Override public RegMask regmap(int i) { assert i==1 || i==2; return riscv.FMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return riscv.FMASK; }
+ // 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();
+ }
+
+ // Default on double precision for now(64 bits)
+ // General form: "fmul.d rd = src1 * src2
+ @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 "mulf"; }
+
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulIRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulIRISC.java
new file mode 100644
index 00000000..8735068e
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulIRISC.java
@@ -0,0 +1,35 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+public class MulIRISC extends MachConcreteNode implements MachNode {
+ final TypeInteger _ti;
+ MulIRISC(Node mul, TypeInteger ti) {super(mul); _inputs.pop(); _ti = ti;}
+
+ // Register mask allowed on input i.
+ // This is the normal calling convention
+ @Override public RegMask regmap(int i) {
+ return riscv.RMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return riscv.RMASK; }
+
+
+ // Output is same register as input#1
+ @Override public int twoAddress() { return 0; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+ // General form
+ // General form: "muli 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 "muli"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulRISC.java
new file mode 100644
index 00000000..8713486e
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulRISC.java
@@ -0,0 +1,33 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.node.cpus.x86_64_v2.x86_64_v2;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+
+// mulh signed multiply instruction(no-imm form)
+public class MulRISC extends MachConcreteNode implements MachNode{
+ MulRISC(Node mul) {super(mul);}
+
+ // Register mask allowed on input i.
+ @Override public RegMask regmap(int i) { assert i==1 || i==2; return riscv.RMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return riscv.RMASK; }
+ // 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: "rd = rs1 * rs2"
+ @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 "mul"; }
+
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NewRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NewRISC.java
new file mode 100644
index 00000000..2b8ec4f2
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NewRISC.java
@@ -0,0 +1,44 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.node.cpus.x86_64_v2.x86_64_v2;
+
+import java.io.ByteArrayOutputStream;
+
+
+public class NewRISC extends NewNode implements MachNode{
+
+ // A pre-zeroed chunk of memory.
+ NewRISC( NewNode nnn ) { super(nnn); }
+
+ // Register mask allowed on input i, the size
+ @Override public RegMask regmap(int i) {
+ // Size
+ if( i==2 ) return riscv.RET_MASK;
+ // Memory has no register
+ return null;
+ }
+
+ @Override public RegMask outregmap() { throw Utils.TODO(); }
+
+ // Register mask allowed as a result. Pointer result in standard calling
+ // convention.
+ @Override public RegMask outregmap(int i) {
+ if( i == 0 ) return riscv.RET_MASK;
+ // Memory has no register
+ return null;
+ }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form: "alloc #bytes"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(size()));
+ }
+
+ @Override public String op() { return "alloc"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrIRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrIRISC.java
new file mode 100644
index 00000000..f3f79ed1
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrIRISC.java
@@ -0,0 +1,43 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+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.node.cpus.x86_64_v2.x86_64_v2;
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeInteger;
+
+import java.io.ByteArrayOutputStream;
+
+public class OrIRISC extends MachConcreteNode implements MachNode{
+ final TypeInteger _ti;
+ OrIRISC(Node or, TypeInteger ti) {super(or); _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 || i==2;
+ return riscv.RMASK;
+ }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return riscv.RMASK; }
+ // Output is same register as input#1
+ @Override public int twoAddress() { return 0;}
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form
+ // General form: "rd = rs1 | 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 "ori"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrRISC.java
new file mode 100644
index 00000000..8f1e9305
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrRISC.java
@@ -0,0 +1,43 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.simple.node.MachConcreteNode;
+import com.seaofnodes.simple.node.MachNode;
+
+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.Node;
+import com.seaofnodes.simple.node.cpus.x86_64_v2.x86_64_v2;
+import java.io.ByteArrayOutputStream;
+
+public class OrRISC extends MachConcreteNode implements MachNode {
+ OrRISC(Node or) {
+ super(or);
+ }
+
+ // Register mask allowed on input i.
+ // This is the normal calling convention
+ @Override public RegMask regmap(int i) {
+ assert i==1 || i==2;
+ return riscv.RMASK;
+ }
+
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return riscv.RMASK; }
+
+ // Output is same register as input#1
+ @Override public int twoAddress() { return 0; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form
+ // General form: # rd = rs1 | rs2
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1)));
+ }
+
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ParmRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ParmRISC.java
new file mode 100644
index 00000000..2e20f96f
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ParmRISC.java
@@ -0,0 +1,25 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.simple.RegMask;
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.node.ParmNode;
+import com.seaofnodes.simple.node.MachNode;
+import java.io.ByteArrayOutputStream;
+
+public class ParmRISC extends ParmNode implements MachNode{
+ final RegMask _rmask;
+ ParmRISC(ParmNode parm) {
+ super(parm);
+ // Assume int
+ _rmask = riscv.callInMaskInt(_idx);
+ }
+
+ // Register mask allowed on input i. 0 for no register.
+ @Override public RegMask regmap(int i) { return null; }
+ // Register mask allowed as a result. Calling convention register
+ @Override public RegMask outregmap() { return _rmask; }
+
+ // Encoding is appended into the byte array. Returns size
+ @Override public int encoding(ByteArrayOutputStream bytes) { return 0; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ProjRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ProjRISC.java
new file mode 100644
index 00000000..a1312c4c
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ProjRISC.java
@@ -0,0 +1,23 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+
+import com.seaofnodes.simple.RegMask;
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.node.ProjNode;
+import com.seaofnodes.simple.node.MachNode;
+import java.io.ByteArrayOutputStream;
+
+public class ProjRISC extends ProjNode implements MachNode{
+ ProjRISC(ProjNode p) {super(p);}
+
+ // Register mask allowed on input i. 0 for no register.
+ @Override public RegMask regmap(int i) { return null; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return null; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/RetRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/RetRISC.java
new file mode 100644
index 00000000..c92e6989
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/RetRISC.java
@@ -0,0 +1,48 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.FunNode;
+import com.seaofnodes.simple.node.ReturnNode;
+import com.seaofnodes.simple.node.MachNode;
+
+import java.io.ByteArrayOutputStream;
+
+public class RetRISC extends ReturnNode implements MachNode{
+ RetRISC(ReturnNode ret, FunNode fun) { super(ret, fun); fun.setRet(this); }
+ // Todo: postSelect
+ // Register mask allowed on input i.
+ // This is the normal calling convention
+ @Override public RegMask regmap(int i) {
+ return switch( i ) {
+ case 0 -> null;
+ case 1 -> null;
+ case 2 -> riscv.RET_MASK;
+ case 3 -> null; // RPC is always on stack
+ default -> throw Utils.TODO();
+ };
+ }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return null; }
+
+ // 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)));
+ }
+
+ // Correct Nodes outside the normal edges
+ @Override public void postSelect() {
+ FunNode fun = (FunNode)rpc().in(0);
+ _fun = fun;
+ fun.setRet(this);
+ }
+
+ @Override public String op() { return "ret"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetRISC.java
new file mode 100644
index 00000000..1d8c9de8
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetRISC.java
@@ -0,0 +1,35 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+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;
+
+// corresponds to slt,sltu,slti,sltiu, seqz
+// RISCV doesn't have rflags.
+public class SetRISC extends MachConcreteNode implements MachNode{
+ final String _bop; // One of <,<=,==
+ SetRISC( 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 riscv.FLAGS_MASK; }
+ @Override public RegMask outregmap() { return riscv.RMASK; }
+
+ // 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/riscv/SllIRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllIRISC.java
new file mode 100644
index 00000000..8927c086
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllIRISC.java
@@ -0,0 +1,40 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.node.cpus.x86_64_v2.x86_64_v2;
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+import java.util.BitSet;
+
+public class SllIRISC extends MachConcreteNode implements MachNode{
+ final TypeInteger _ti;
+ SllIRISC(Node sll, TypeInteger ti) {super(sll); _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 riscv.RMASK; }
+
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return riscv.RMASK; }
+
+ // Output is same register as input#1
+ @Override public int twoAddress() { return 0; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form
+ // General form: "slli rd, rs1, 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 "slli"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllRISC.java
new file mode 100644
index 00000000..69538433
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllRISC.java
@@ -0,0 +1,36 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+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 SllRISC extends MachConcreteNode implements MachNode{
+ SllRISC(Node sll) {super(sll);}
+
+ // Register mask allowed on input i.
+ // This is the normal calling convention
+ @Override public RegMask regmap(int i) {
+ //assert i==1;
+ return riscv.RMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return riscv.RMASK; }
+
+ // 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: "sll rd, rs1, rs2"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1)));
+ }
+
+ @Override public String op() { return "sll"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraIRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraIRISC.java
new file mode 100644
index 00000000..654dd2ae
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraIRISC.java
@@ -0,0 +1,43 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.node.cpus.x86_64_v2.x86_64_v2;
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+import java.util.BitSet;
+import java.lang.StringBuilder;
+
+
+// Right Shift Arithmetic(immediate)
+public class SraIRISC extends MachConcreteNode implements MachNode{
+ final TypeInteger _ti;
+ SraIRISC(Node sra, TypeInteger ti) {super(sra); _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 riscv.RMASK; }
+
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return riscv.RMASK; }
+
+ // Output is same register as input#1
+ @Override public int twoAddress() { return 0; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form
+ // General form: "srai rd, rs1, 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 "srai"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraRISC.java
new file mode 100644
index 00000000..2d50ceaa
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraRISC.java
@@ -0,0 +1,42 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+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;
+
+// Right Shift Arithmetic
+public class SraRISC extends MachConcreteNode implements MachNode {
+
+ SraRISC(Node sra) {super(sra);}
+
+ // Register mask allowed on input i.
+ // This is the normal calling convention
+ @Override public RegMask regmap(int i) {
+ // assert i==1;
+ return riscv.RMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return riscv.RMASK; }
+
+ // 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: "sra rd, rs1, rs2"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1)));
+ }
+
+ @Override public String op() { return "sra"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlIRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlIRISC.java
new file mode 100644
index 00000000..f7ad6c25
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlIRISC.java
@@ -0,0 +1,43 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.node.cpus.x86_64_v2.x86_64_v2;
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+import java.util.BitSet;
+import java.lang.StringBuilder;
+
+
+// Right Shift Logical Immediate
+public class SrlIRISC extends MachConcreteNode implements MachNode {
+ final TypeInteger _ti;
+ SrlIRISC(Node srl, TypeInteger ti) {super(srl); _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 riscv.RMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return riscv.RMASK; }
+
+ // 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: "srli 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 "srli"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlRISC.java
new file mode 100644
index 00000000..25d62688
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlRISC.java
@@ -0,0 +1,38 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+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;
+
+// Right Shift Logical
+public class SrlRISC extends MachConcreteNode implements MachNode {
+ SrlRISC(Node srli) {super(srli);}
+
+ // Register mask allowed on input i.
+ // This is the normal calling convention
+ @Override public RegMask regmap(int i) {
+ // assert i==1;
+ return riscv.RMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return riscv.RMASK; }
+
+ // 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: "srl rd, rs1, rs2"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1)));
+ }
+
+ @Override public String op() { return "srl"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/StoreRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/StoreRISC.java
new file mode 100644
index 00000000..1bd40d0c
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/StoreRISC.java
@@ -0,0 +1,40 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+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.node.StoreNode;
+
+import java.io.ByteArrayOutputStream;
+import java.util.BitSet;
+
+
+// sw rs2,offset(rs1)
+public class StoreRISC extends MemOpRISC {
+ StoreRISC( StoreNode st) {
+ super(st, st);
+ }
+
+ @Override public RegMask regmap(int i) {
+ return i>=2 && i<=4 ? riscv.RMASK : null;
+ }
+
+
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return null; }
+
+ // 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) {
+
+ }
+
+ @Override public String op() { return "st"+_sz; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubFRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubFRISC.java
new file mode 100644
index 00000000..e6a0b8e4
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubFRISC.java
@@ -0,0 +1,30 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+public class SubFRISC extends MachConcreteNode implements MachNode{
+ SubFRISC(Node subf) {super(subf);}
+
+ // Register mask allowed on input i.
+ @Override public RegMask regmap(int i) { assert i==1 || i==2; return riscv.FMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return riscv.FMASK; }
+ // Output is same register as input#1
+ @Override public int twoAddress() { return 0; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // Default on double precision for now(64 bits)
+ // General form: "fsub.d rd = src1 - src2
+ @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/riscv/SubRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubRISC.java
new file mode 100644
index 00000000..6fb0e0ef
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubRISC.java
@@ -0,0 +1,29 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+public class SubRISC extends MachConcreteNode implements MachNode {
+ SubRISC( Node sub ) { super(sub); }
+
+ // Register mask allowed on input i.
+ @Override public RegMask regmap(int i) { assert i==1 || i==2; return riscv.RMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return riscv.RMASK; }
+ // Output is same register as input#1
+ @Override public int twoAddress() { return 0; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form: "sub # rd = rs1 - rs2"
+ @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/riscv/TFPRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/TFPRISC.java
new file mode 100644
index 00000000..b508541a
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/TFPRISC.java
@@ -0,0 +1,43 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.ConstantNode;
+import com.seaofnodes.simple.node.MachNode;
+import com.seaofnodes.simple.node.cpus.x86_64_v2.TFPX86;
+import com.seaofnodes.simple.node.cpus.x86_64_v2.x86_64_v2;
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeFunPtr;
+
+import java.io.ByteArrayOutputStream;
+
+public class TFPRISC extends ConstantNode implements MachNode{
+ TFPRISC(ConstantNode con) {super(con);}
+ // Register mask allowed on input i. 0 for no register.
+ @Override public RegMask regmap(int i) { return null; }
+ // General int registers
+ @Override public RegMask outregmap() { return x86_64_v2.RMASK; }
+
+ // 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/riscv/XorIRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorIRISC.java
new file mode 100644
index 00000000..3d53ee64
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorIRISC.java
@@ -0,0 +1,45 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+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.node.cpus.x86_64_v2.x86_64_v2;
+import com.seaofnodes.simple.type.TypeInteger;
+
+import java.io.ByteArrayOutputStream;
+
+public class XorIRISC extends MachConcreteNode implements MachNode{
+ final TypeInteger _ti;
+ XorIRISC(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 || i==2;
+
+ return riscv.RMASK; }
+
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return riscv.RMASK; }
+
+ // 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: "rd = rs1 ^ 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/riscv/XorRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorRISC.java
new file mode 100644
index 00000000..2f34529e
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorRISC.java
@@ -0,0 +1,43 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+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 XorRISC extends MachConcreteNode implements MachNode {
+
+ XorRISC(Node or) {
+ super(or);
+ }
+
+ // Register mask allowed on input i.
+ // This is the normal calling convention
+ @Override public RegMask regmap(int i) {
+ assert i==1 || i==2;
+ return riscv.RMASK;
+ }
+
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return riscv.RMASK; }
+
+ // Output is same register as input#1
+ @Override public int twoAddress() { return 0; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form
+ // General form: # # rd = rs1 ^ rs2
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1)));
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java
new file mode 100644
index 00000000..ad6e1c0f
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java
@@ -0,0 +1,332 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.simple.Machine;
+import com.seaofnodes.simple.RegMask;
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.node.cpus.x86_64_v2.*;
+import com.seaofnodes.simple.type.*;
+
+public class riscv extends Machine{
+ @Override public String name() {return "riscv";}
+
+ // Using ABI names instead of register names
+ public static int ZERO = 0, RA = 1, SP = 2, GP = 3, TP = 4, T0 = 5, T1 = 6, T2 = 7;
+ public static int S0 = 8, S1 = 9, A0 = 10, A1 = 11, A2 = 12, A3 = 13, A4 = 14, A5 = 15;
+ public static int A6 = 16, A7 = 17, S2 = 18, S3 = 19, S4 = 20, S5 = 21, S6 = 22, S7 = 23;
+ public static int S8 = 24, S9 = 25, S10 = 26, S11 = 27, T3 = 28, T4 = 29, T5 = 30, T6 = 31;
+
+ // FP registers
+ public static int F0 = 32, F1 = 33, F2 = 34, F3 = 35, F4 = 36, F5 = 37, F6 = 38, F7 = 39;
+ public static int FS0 = 40, FS1 = 41, FA0 = 42, FA1 = 43, FA2 = 44, FA3 = 45, FA4 = 46, FA5 = 47;
+ public static int FA6 = 48, FA7 = 49, FS2 = 50, FS3 = 51, FS4 = 52, FS5 = 53, FS6 = 54, FS7 = 55;
+ public static int FS8 = 56, FS9 = 57, FS10 = 58, FS11 = 59, FT8 = 60, FT9 = 61, FT10 = 62, FT11 = 63;
+
+ public static int FLAGS = 64;
+
+ // General purpose register mask: pointers and ints, not floats
+ public static RegMask RMASK = new RegMask(0b11111111111111111111111111111111);
+ // Float mask from(ft0–ft11)
+ public static RegMask FMASK = new RegMask(0b11111111111111111111111111111111);
+
+ // Return single int/ptr register
+ public static RegMask RET_MASK = new RegMask(A0);
+
+ // Arguments masks
+ public static RegMask A0_MASK = new RegMask(1L<<A0);
+ public static RegMask A1_MASK = new RegMask(1L<<A1);
+ public static RegMask A2_MASK = new RegMask(1L<<A2);
+ public static RegMask A3_MASK = new RegMask(1L<<A3);
+ public static RegMask A4_MASK = new RegMask(1L<<A4);
+ public static RegMask A5_MASK = new RegMask(1L<<A5);
+ public static RegMask A6_MASK = new RegMask(1L<<A6);
+ public static RegMask A7_MASK = new RegMask(1L<<A7);
+
+ public static RegMask FLAGS_MASK = new RegMask(1L << FLAGS);
+
+ // Float arguments masks
+ public static RegMask FA0_MASK = new RegMask(1L<<FA0);
+ public static RegMask FA1_MASK = new RegMask(1L<<FA1);
+ public static RegMask FA2_MASK = new RegMask(1L<<FA2);
+ public static RegMask FA3_MASK = new RegMask(1L<<FA3);
+ public static RegMask FA4_MASK = new RegMask(1L<<FA4);
+ public static RegMask FA5_MASK = new RegMask(1L<<FA5);
+ public static RegMask FA6_MASK = new RegMask(1L<<FA6);
+ public static RegMask FA7_MASK = new RegMask(1L<<FA7);
+
+ // Int arguments calling conv
+ static RegMask[] CALLINMASK_RISCV_INT = new RegMask[] {
+ null,
+ null,
+ A0_MASK,
+ A1_MASK,
+ A2_MASK,
+ A3_MASK,
+ A4_MASK,
+ A5_MASK,
+ A6_MASK,
+ A7_MASK
+ };
+
+ static int[] CALLINARG_RISCV_INT = new int[] {
+ 0, // Control, no register
+ 0, // Memory, no register
+ A0,
+ A1,
+ A2,
+ A3,
+ A4,
+ A5,
+ A6,
+ A7
+ };
+
+ static int callInArgInt(int idx) {
+ return CALLINARG_RISCV_INT[idx];
+ }
+
+ static RegMask callInMaskInt(int idx) {
+ return CALLINMASK_RISCV_INT[idx];
+ }
+
+ // Float arguments
+ static RegMask[] CALLINMASK_RISCV_FLOAT = new RegMask[] {
+ null,
+ null,
+ FA0_MASK,
+ FA1_MASK,
+ FA2_MASK,
+ FA3_MASK,
+ FA4_MASK,
+ FA5_MASK,
+ FA6_MASK,
+ FA7_MASK
+ };
+
+
+ static int[] CALLINARG_RISCV_FLOAT = new int[] {
+ 0, // Control, no register
+ 0, // Memory, no register
+ FA0,
+ FA1,
+ FA2,
+ FA3,
+ FA4,
+ FA5,
+ FA6,
+ FA7
+ };
+
+
+ static int callInArgFloat(int idx) {
+ return CALLINARG_RISCV_FLOAT[idx];
+ }
+
+ static RegMask callInMaskFloat(int idx) {
+ return CALLINMASK_RISCV_FLOAT[idx];
+ }
+ // caller saved(riscv)
+ //public static final long RISCV_CALLER_SAVED= TBD
+ // callee saved(riscv)
+ public static final long RISCV_CALLEE_SAVED =
+ (1L << FS0) | (1L << FS1) | (1L << FS2) | (1L << FS3) | (1L << FS4)
+ | (1L << FS5) | (1L << FS6) | (1L << FS7) | (1L << FS8) | (1L << FS9) | (1L << FS10);
+
+
+ // Calling conv metadata
+ public int GPR_COUNT_CONV_RISCV = 7; // A0, A1, A2, A3, A4, A5, A6, A7
+ public int FLOAT_COUNT_CONV_RISCV = 7; // FA0, FA1, FA2, FA3, FA4, FA5, FA6, FA7
+
+ public static final String[] REGS = new String[] {
+ "zero", "ra" , "sp" , "gp" , "tp" , "t0" , "t1" , "t2" ,
+ "s0" , "s1" , "a0" , "a1" , "a2" , "a3" , "a4" , "a5" ,
+ "a6" , "a7" , "s2" , "s3" , "s4" , "s5" , "s6" , "s7" ,
+ "s8" , "s9" , "s10" , "s11" , "t3" , "t4" , "t5" , "t6" ,
+ "f0" , "f1" , "f2" , "f3" , "f4" , "f5" , "f6" , "f7" ,
+ "fs0" , "fs1" , "fa0" , "fa1" , "fa2" , "fa3" , "fa4" , "fa5" ,
+ "fa6" , "fa7" , "fs2" , "fs3" , "fs4" , "fs5" , "fs6" , "fs7" ,
+ "fs8" , "fs9" , "fs10", "fs11", "ft8" , "ft9" , "ft10", "ft11",
+ "flags"
+ };
+
+ // General purpose register mask:
+ @Override public String reg( int reg ) { return REGS[reg]; }
+ // Return a MachNode unconditional branch
+ @Override public CFGNode jump() {
+ throw Utils.TODO();
+ }
+
+ // Create a split op; any register to any register, including stack slots
+ @Override public Node split() {
+ throw Utils.TODO();
+ }
+
+ // Break an infinite loop
+ @Override public IfNode never( CFGNode ctrl ) {
+ throw Utils.TODO();
+ }
+
+ @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 DivFRISC(divf);
+ case DivNode div -> new DivRISC(div);
+ case FunNode fun -> new FunRISC(fun);
+ case IfNode iff -> jmp(iff);
+ case LoadNode ld -> ld(ld);
+ case MemMergeNode mem -> new MemMergeNode(mem);
+ case MulFNode mulf -> new MulFRISC(mulf);
+ case MulNode mul -> mul(mul);
+ case NewNode nnn -> new NewRISC(nnn);
+ case OrNode or -> or(or);
+ case ParmNode parm -> new ParmRISC(parm);
+ case BulkMemPhiNode phi -> new BulkMemPhiNode(phi);
+ case MemPhiNode phi -> new MemPhiNode(phi);
+ case PhiNode phi -> new PhiNode(phi);
+ case ProjNode prj -> prj(prj);
+ case ReadOnlyNode read -> new ReadOnlyNode(read);
+ case ReturnNode ret -> new RetRISC(ret, ret.fun());
+ case SarNode sar -> sra(sar);
+ case ShlNode shl -> sll(shl);
+ case ShrNode shr -> srl(shr);
+ case StartNode start -> new StartNode(start);
+ case StopNode stop -> new StopNode(stop);
+ case StoreNode st -> st(st);
+ case SubFNode subf -> new SubFRISC(subf);
+ case SubNode sub -> sub(sub);
+ case ToFloatNode tfn -> i2f8(tfn);
+ case ToIntegerNode cvt -> new F8ToIRISC(cvt);
+ case XorNode xor -> xor(xor);
+
+ case LoopNode loop -> new LoopNode(loop);
+ case RegionNode region -> new RegionNode(region);
+ default -> throw Utils.TODO();
+ };
+ }
+
+ private Node addf(AddFNode addf) {
+ return new AddFRISC(addf);
+ }
+
+ private Node add(AddNode add) {
+ Node rhs = add.in(2);
+ if( rhs instanceof ConstantNode off && off._con instanceof TypeInteger toff ) {
+ return new AddIRISC(add, toff);
+ }
+ return new AddRISC(add);
+ }
+
+ private Node and(AndNode and) {
+ if( and.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti )
+ return new AndIRISC(and, ti);
+ return new AndRISC(and);
+ }
+
+ private Node cmp(BoolNode bool) {
+ Node cmp = _cmp(bool);
+ return new SetRISC(cmp, bool.op());
+ }
+
+ private Node call(CallNode call) {
+ if( call.fptr() instanceof ConstantNode con && con._con instanceof TypeFunPtr tfp )
+ return new CallRISC(call, tfp);
+ return new CallRRISC(call);
+ }
+
+ private Node _cmp(BoolNode bool) {
+ Node rhs = bool.in(2);
+
+ return rhs instanceof ConstantNode con && con._con instanceof TypeInteger ti
+ ? new CmpIRISC(bool, ti)
+ : new CmpRISC(bool);
+ }
+
+ private Node con( ConstantNode con ) {
+ return switch( con._con ) {
+ case TypeInteger ti -> new IntRISC(con);
+ case TypeFloat tf -> new IntRISC(con);
+ case TypeFunPtr tfp -> new TFPRISC(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 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 CBranchRISC(iff, ((BoolNode)iff.in(1)).op());
+ }
+
+ private Node or(OrNode or) {
+ if(or.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti)
+ return new OrIRISC(or, ti);
+
+ return new OrRISC(or);
+ }
+
+ private Node xor(XorNode xor) {
+ if(xor.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti)
+ return new XorIRISC(xor, ti);
+ return new XorRISC(xor);
+ }
+
+ private Node sra(SarNode sar) {
+ if( sar.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti)
+ return new SraIRISC(sar, ti);
+ return new SraRISC(sar);
+ }
+
+ private Node srl(ShrNode shr) {
+ if( shr.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti)
+ return new SrlIRISC(shr, ti);
+ return new SrlRISC(shr);
+ }
+
+ private Node sll(ShlNode sll) {
+ if( sll.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti)
+ return new SllIRISC(sll, ti);
+ return new SllRISC(sll);
+ }
+
+ private Node sub(SubNode sub) {
+ return sub.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti
+ ? new AddIRISC(sub, TypeInteger.constant(-ti.value()))
+ : new SubRISC(sub);
+ }
+
+ private Node i2f8(ToFloatNode tfn) {
+ assert tfn.in(1)._type instanceof TypeInteger ti;
+ return new I2F8RISC(tfn);
+ }
+
+ private Node prj(ProjNode prj) {
+ return new ProjRISC(prj);
+ }
+
+ private Node ld(LoadNode ld) {
+ return new LoadRISC(ld);
+ }
+
+ private Node st(StoreNode st) {
+ return new StoreRISC(st);
+ }
+
+ private Node mul(MulNode mul) {
+ return mul.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti
+ ? new MulIRISC(mul, ti)
+ : new MulRISC(mul);
+ }
+}
+
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFMemX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFMemX86.java
new file mode 100644
index 00000000..4bb24fa3
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFMemX86.java
@@ -0,0 +1,33 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.simple.CodeGen;
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.RegMask;
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.node.*;
+
+public class AddFMemX86 extends MemOpX86 {
+ AddFMemX86( AddFNode add, LoadNode ld , Node base, Node idx, int off, int scale, Node val ) {
+ super(add,ld, base, idx, off, scale, 0, val );
+ }
+
+ @Override public RegMask regmap(int i) {
+ if( i==1 ) return null; // Memory
+ if( i==2 ) return x86_64_v2.RMASK; // base
+ if( i==3 ) return x86_64_v2.RMASK; // index
+ if( i==4 ) return x86_64_v2.XMASK; // value
+ throw Utils.TODO();
+ }
+
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return x86_64_v2.XMASK; }
+
+
+ // General form: "add dst = src + [base + idx<<2 + 12]"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ");
+ sb.p(val()==null ? "#"+_imm : code.reg(val())).p(" + ");
+ asm_address(code,sb);
+ }
+ @Override public String op() { return "addf"+_sz; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFX86.java
new file mode 100644
index 00000000..a3f5c7b3
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFX86.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 AddFX86 extends MachConcreteNode implements MachNode {
+ AddFX86( Node addf ) { super(addf); }
+
+ // 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: "addf 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 "addf"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddIX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddIX86.java
new file mode 100644
index 00000000..6a332ff5
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddIX86.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.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+public class AddIX86 extends MachConcreteNode implements MachNode {
+ final TypeInteger _ti;
+ AddIX86( Node add, TypeInteger ti ) {
+ super(add);
+ _inputs.pop();
+ _ti = ti;
+ }
+
+ // Register mask allowed on input i.
+ @Override public RegMask regmap(int i) {
+ // assert i== i;
+ 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: "addi 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 _ti.value() == 1 ? "inc" : (_ti.value() == -1 ? "dec" : "addi");
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddMemX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddMemX86.java
new file mode 100644
index 00000000..7d45d757
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddMemX86.java
@@ -0,0 +1,25 @@
+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.*;
+
+public class AddMemX86 extends MemOpX86 {
+ AddMemX86( AddNode add, LoadNode ld , Node base, Node idx, int off, int scale, int imm, Node val ) {
+ super(add,ld, base, idx, off, scale, imm, val );
+ }
+
+ @Override public String op() { return "add"+_sz; }
+
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
+
+
+ // General form: "add dst = src + [base + idx<<2 + 12]"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ");
+ sb.p(val()==null ? "#"+_imm : code.reg(val())).p(" + ");
+ asm_address(code,sb);
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddX86.java
new file mode 100644
index 00000000..61aa5a9e
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddX86.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 AddX86 extends MachConcreteNode implements MachNode {
+ AddX86( Node add ) { super(add); }
+
+ // 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: "add 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 "add"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AndIX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AndIX86.java
new file mode 100644
index 00000000..f2ee1c66
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AndIX86.java
@@ -0,0 +1,42 @@
+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 AndIX86 extends MachConcreteNode implements MachNode {
+ final TypeInteger _ti;
+ AndIX86(Node and, TypeInteger ti) {
+ super(and);
+ _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: "andi 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 "andi"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AndX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AndX86.java
new file mode 100644
index 00000000..45cf2b22
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AndX86.java
@@ -0,0 +1,33 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import java.io.ByteArrayOutputStream;
+
+public class AndX86 extends MachConcreteNode implements MachNode{
+ AndX86(Node and) {
+ super(and);
+ }
+ // 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; }
+
+ @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: "and dst & #imm"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1)));
+ }
+
+ @Override public String op() { return "and"; }
+}
+
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallEndX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallEndX86.java
new file mode 100644
index 00000000..55ab90c9
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallEndX86.java
@@ -0,0 +1,37 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.CodeGen;
+import com.seaofnodes.simple.RegMask;
+import com.seaofnodes.simple.node.CallEndNode;
+import com.seaofnodes.simple.node.MachNode;
+import com.seaofnodes.simple.type.TypeFloat;
+import com.seaofnodes.simple.type.TypeFunPtr;
+import java.io.ByteArrayOutputStream;
+
+public class CallEndX86 extends CallEndNode implements MachNode {
+ final TypeFunPtr _tfp;
+ CallEndX86( CallEndNode cend ) {
+ super(cend);
+ _tfp = (TypeFunPtr)cend.call().fptr()._type;
+ }
+
+ @Override public String label() { return op(); }
+ @Override public RegMask regmap(int i) { return null; }
+ @Override public RegMask outregmap() { throw Utils.TODO(); }
+ @Override public RegMask outregmap(int idx) {
+ if( idx != 2 ) return null;
+ return _tfp._ret instanceof TypeFloat ? x86_64_v2.RET_FMASK : x86_64_v2.RET_MASK;
+ }
+
+ // 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) { }
+
+ @Override public String op() { return "cend"; }
+
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallRX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallRX86.java
new file mode 100644
index 00000000..99260a27
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallRX86.java
@@ -0,0 +1,31 @@
+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.TypeFunPtr;
+import java.io.ByteArrayOutputStream;
+
+public class CallRX86 extends CallNode implements MachNode {
+ CallRX86( CallNode call ) { super(call); }
+
+ @Override public String label() { return op(); }
+ @Override public RegMask regmap(int i) {
+ return i==_inputs._len
+ ? x86_64_v2.WMASK // Function call target
+ : x86_64_v2.callInMask(tfp(),i); // Normal argument
+ }
+ @Override public RegMask outregmap() { return x86_64_v2.RET_MASK; }
+
+ // 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(fptr()));
+ }
+
+ @Override public String op() { return "callr"; }
+
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallX86.java
new file mode 100644
index 00000000..b3e3bbe8
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallX86.java
@@ -0,0 +1,41 @@
+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.TypeFunPtr;
+import java.io.ByteArrayOutputStream;
+import java.util.BitSet;
+
+public class CallX86 extends CallNode implements MachNode {
+ final TypeFunPtr _tfp;
+ final String _name;
+ CallX86( CallNode call, TypeFunPtr tfp ) {
+ super(call);
+ _inputs.pop(); // Pop constant target
+ assert tfp.isConstant();
+ _tfp = tfp;
+ _name = CodeGen.CODE.link(tfp)._name;
+ }
+
+ @Override public String label() { return op(); }
+ @Override public RegMask regmap(int i) {
+ return x86_64_v2.callInMask(tfp(),i); // Normal argument
+ }
+ @Override public RegMask outregmap() { return x86_64_v2.RET_MASK; }
+
+ @Override public String name() { return _name; }
+ @Override public TypeFunPtr tfp() { return _tfp; }
+
+ // 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(_name);
+ }
+
+ @Override public String op() { return "call"; }
+
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpIX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpIX86.java
new file mode 100644
index 00000000..ea526f33
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpIX86.java
@@ -0,0 +1,43 @@
+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.(RFLAGS)
+public class CmpIX86 extends MachConcreteNode implements MachNode {
+ final int _imm;
+ final String _bop;
+ CmpIX86( BoolNode bool, TypeInteger ti ) {
+ super(bool);
+ _inputs.pop(); // Toss ideal ConstantNode away, embedded into machine op
+ _bop = bool.op(); // One of <,<=,==
+ _imm = (int)ti.value();
+ assert _imm == ti.value();
+ }
+ CmpIX86( Node cmp, double ignore ) {
+ super(cmp);
+ _bop = "==";
+ _imm = 0;
+ }
+
+ @Override public RegMask regmap(int i) { assert i==1; return x86_64_v2.RMASK; }
+ @Override public RegMask outregmap() { return x86_64_v2.FLAGS_MASK; }
+
+ // 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) {
+ String dst = code.reg(this);
+ if( dst!="FLAGS" ) sb.p(dst).p(" = ");
+ sb.p(code.reg(in(1)));
+ if( _imm != 0 ) sb.p(", #").p(_imm);
+ }
+
+ @Override public String op() { return _imm==0 ? "test" : "cmp"; }
+
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpMemX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpMemX86.java
new file mode 100644
index 00000000..873ca9df
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpMemX86.java
@@ -0,0 +1,33 @@
+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.*;
+
+public class CmpMemX86 extends MemOpX86 {
+ final boolean _invert; // Op switched LHS, RHS
+ CmpMemX86( BoolNode bool, LoadNode ld , Node base, Node idx, int off, int scale, int imm, Node val, boolean invert ) {
+ super(bool,ld, base, idx, off, scale, imm, val );
+ _invert = invert;
+ }
+
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return x86_64_v2.FLAGS_MASK; }
+
+ // General form: "cmp dst = src, [base + idx<<2 + 12]"
+ @Override public void asm(CodeGen code, SB sb) {
+ String dst = code.reg(this);
+ if( dst!="FLAGS" ) sb.p(dst).p(" = ");
+ if( _invert ) asm_address(code,sb).p(",");
+ if( val()==null ) {
+ if( _imm!=0 ) sb.p("#").p(_imm);
+ } else {
+ sb.p(code.reg(val()));
+ }
+ if( !_invert ) asm_address(code,sb.p(", "));
+ }
+
+ @Override public String op() { return ((val()==null && _imm==0) ? "test" : "cmp") + _sz; }
+
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpX86.java
new file mode 100644
index 00000000..d8ebb12d
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpX86.java
@@ -0,0 +1,28 @@
+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;
+
+// Compare. Sets flags.(RFLAGS)
+public class CmpX86 extends MachConcreteNode implements MachNode {
+ CmpX86( Node cmp ) { super(cmp); }
+
+ @Override public RegMask regmap(int i) { assert i==1 || i==2; return x86_64_v2.RMASK; }
+ @Override public RegMask outregmap() { return x86_64_v2.FLAGS_MASK; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form: "cmp reg1, reg2"
+ @Override public void asm(CodeGen code, SB sb) {
+ String dst = code.reg(this);
+ if( dst!="FLAGS" ) sb.p(dst).p(" = ");
+ sb.p(code.reg(in(1))).p(", ").p(code.reg(in(2)));
+ }
+
+ @Override public String op() { return "cmp"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivFX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivFX86.java
new file mode 100644
index 00000000..d73bfd5b
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivFX86.java
@@ -0,0 +1,33 @@
+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 DivFX86 extends MachConcreteNode implements MachNode {
+ DivFX86( Node divf) { super(divf); }
+
+ // 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: "divf 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() {
+ // 8 bytes = 64 bits
+ return "divf";
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivX86.java
new file mode 100644
index 00000000..1dc20017
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivX86.java
@@ -0,0 +1,31 @@
+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 DivX86 extends MachConcreteNode implements MachNode {
+ DivX86( Node div ) { super(div); }
+
+ // 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: "div 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 "div"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/F8ToIX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/F8ToIX86.java
new file mode 100644
index 00000000..33a77d4c
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/F8ToIX86.java
@@ -0,0 +1,27 @@
+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 F8ToIX86 extends MachConcreteNode implements MachNode {
+ F8ToIX86(Node f8toi ) { super(f8toi); }
+
+ // Register mask allowed on input i.
+ @Override public RegMask regmap(int i) { assert i==1; return x86_64_v2.XMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @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)).p(" = ").p("(int)").p(code.reg(in(1)));
+ }
+
+ @Override public String op() { return "f8toi"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FltX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FltX86.java
new file mode 100644
index 00000000..5e1190da
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FltX86.java
@@ -0,0 +1,36 @@
+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.ConstantNode;
+import com.seaofnodes.simple.node.MachNode;
+
+import java.io.ByteArrayOutputStream;
+
+public class FltX86 extends ConstantNode implements MachNode {
+ FltX86(ConstantNode con ) { super(con); }
+
+ // Register mask allowed on input i. 0 for no register.
+ @Override public RegMask regmap(int i) { return null; }
+ // General int registers
+ @Override public RegMask outregmap() { return x86_64_v2.XMASK; }
+
+ // 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) {
+ _con.print(sb.p(code.reg(this)).p(" #"));
+ }
+
+ @Override public String op() {
+ return "fld"; // Some fancier encoding
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FunX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FunX86.java
new file mode 100644
index 00000000..8b0f016a
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FunX86.java
@@ -0,0 +1,21 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.simple.RegMask;
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.node.FunNode;
+import com.seaofnodes.simple.node.MachNode;
+import java.io.ByteArrayOutputStream;
+
+public class FunX86 extends FunNode implements MachNode {
+ FunX86( FunNode fun ) { super(fun); }
+
+ // Register mask allowed on input i. 0 for no register.
+ @Override public RegMask regmap(int i) { return null; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return null; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/I2f8X86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/I2f8X86.java
new file mode 100644
index 00000000..983f817d
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/I2f8X86.java
@@ -0,0 +1,28 @@
+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 I2f8X86 extends MachConcreteNode implements MachNode {
+ I2f8X86(Node i2f8 ) { super(i2f8); }
+
+ // Register mask allowed on input i.
+ @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.XMASK; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form: "i2f8 (flt)int_value"
+
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p("(flt)").p(code.reg(in(1)));
+ }
+
+ @Override public String op() { return "i2f8"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/IntX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/IntX86.java
new file mode 100644
index 00000000..566a7293
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/IntX86.java
@@ -0,0 +1,45 @@
+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.TypeInteger;
+
+import java.io.ByteArrayOutputStream;
+
+// Integer constants
+public class IntX86 extends ConstantNode implements MachNode {
+
+ IntX86( ConstantNode con ) {
+ super(con);
+ }
+
+ // Register mask allowed on input i. 0 for no register.
+ @Override public RegMask regmap(int i) { return null; }
+ // 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 || _con == TypeInteger.ZERO )
+ sb.p(reg).p(",").p(reg);
+ else
+ _con.print(sb.p(reg).p(" #"));
+ }
+
+ @Override public String op() {
+ if( _con == Type.NIL || _con == TypeInteger.ZERO )
+ return "xor";
+ return "ldi"; // Some fancier encoding
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/JmpX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/JmpX86.java
new file mode 100644
index 00000000..2262e847
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/JmpX86.java
@@ -0,0 +1,46 @@
+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;
+
+// Jump on flags, uses flags
+public class JmpX86 extends IfNode implements MachNode {
+ final String _bop;
+ JmpX86( IfNode iff, String bop ) {
+ super(iff);
+ _bop = bop;
+ }
+
+ @Override public String label() { return op(); }
+
+ @Override public void postSelect() {
+ Node set = in(1);
+ Node cmp = set.in(1);
+ // Bypass an expected Set and just reference the cmp directly
+ if( set instanceof SetX86 && (cmp instanceof CmpX86 || cmp instanceof CmpIX86 || cmp instanceof CmpMemX86) )
+ _inputs.set(1,cmp);
+ else
+ throw Utils.TODO();
+ }
+ @Override public RegMask regmap(int i) { assert i==1; return x86_64_v2.FLAGS_MASK; }
+ @Override public RegMask outregmap() { return null; }
+
+ // 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) {
+ String src = code.reg(in(1));
+ if( src!="FLAGS" ) sb.p(src);
+ }
+
+ @Override public String op() { return "j"+_bop; }
+
+ @Override public String comment() {
+ return "L"+cproj(1)._nid+", L"+cproj(0)._nid;
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LeaX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LeaX86.java
new file mode 100644
index 00000000..a49b8d7f
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LeaX86.java
@@ -0,0 +1,42 @@
+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 LeaX86 extends MachConcreteNode implements MachNode {
+ final int _scale;
+ final long _offset;
+ LeaX86( Node add, Node base, Node idx, int scale, long offset ) {
+ super(add);
+ assert scale==1 || scale==2 || scale==4 || scale==8;
+ _inputs.pop();
+ _inputs.pop();
+ _inputs.push(base);
+ _inputs.push(idx);
+ _scale = scale;
+ _offset = offset;
+ }
+
+ // 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; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form: "lea dst = base + 4*idx + 12"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ");
+ if( in(1) != null )
+ sb.p(code.reg(in(1))).p(" + ");
+ sb.p(code.reg(in(2))).p("*").p(_scale);
+ if( _offset!=0 ) sb.p(" + #").p(_offset);
+ }
+
+ @Override public String op() { return "lea"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LoadX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LoadX86.java
new file mode 100644
index 00000000..02a9a4fe
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LoadX86.java
@@ -0,0 +1,25 @@
+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.LoadNode;
+import com.seaofnodes.simple.node.Node;
+
+public class LoadX86 extends MemOpX86 {
+ LoadX86( LoadNode ld, Node base, Node idx, int off, int scale ) {
+ super(ld,ld, base, idx, off, scale, 0);
+ }
+
+ @Override public String op() { return "ld"+_sz; }
+
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
+
+
+ // General form: "ldN dst,[base + idx<<2 + 12]"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(",");
+ asm_address(code,sb);
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemAddX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemAddX86.java
new file mode 100644
index 00000000..a6cdbd05
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemAddX86.java
@@ -0,0 +1,28 @@
+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.*;
+
+public class MemAddX86 extends MemOpX86 {
+ MemAddX86( StoreNode st, Node base, Node idx, int off, int scale, int imm, Node val ) {
+ super(st, st, base, idx, off, scale, imm, val );
+ }
+
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return null; }
+
+
+ // General form: "add [base + idx<<2 + 12] += src"
+ @Override public void asm(CodeGen code, SB sb) {
+ asm_address(code,sb);
+ if( val()==null ) {
+ if( _imm != 1 && _imm != -1 ) sb.p(" += #").p(_imm);
+ } else sb.p(" += ").p(code.reg(val()));
+ }
+
+ @Override public String op() {
+ return (_imm == 1 ? "inc" : (_imm == -1 ? "dec" : "add")) + _sz;
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemOpX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemOpX86.java
new file mode 100644
index 00000000..5a79fb09
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemOpX86.java
@@ -0,0 +1,90 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.print.ExprPrinter;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.*;
+import java.io.ByteArrayOutputStream;
+import java.lang.StringBuilder;
+
+// Generic X86 memory operand base.
+// inputs:
+// ctrl - will appear for possibly RCE (i.e. array ops)
+// mem - memory dependence edge
+// base - Basic object pointer
+// idx/null - Scaled index offset, or null if none
+// val/null - Value to store, as part of a op-to-mem or op-from-mem. Null for loads, or if an immediate is being used
+// Constants:
+// offset - offset added to base. Can be zero.
+// scale - scale on index; only 0,1,2,4,8 allowed, 0 is only when index is null
+// imm - immediate value to store or op-to-mem, and only when val is null
+public abstract class MemOpX86 extends MemOpNode implements MachNode {
+ final int _off; // Limit 32 bits
+ final int _scale; // Limit 0,1,2,3
+ final int _imm; // Limit 32 bits
+ final char _sz = (char)('0'+(1<<_declaredType.log_size()));
+ MemOpX86( Node op, MemOpNode mop, Node base, Node idx, int off, int scale, int imm ) {
+ super(op,mop);
+ assert base._type instanceof TypeMemPtr && !(base instanceof AddNode);
+ assert (idx==null && scale==0) || (idx!=null && 0<= scale && scale<=3);
+
+ // Copy memory parts from eg the LoadNode over the opcode, e.g. an Add
+ if( op != mop ) {
+ _inputs.set(0,mop.in(0)); // Control from mem op
+ _inputs.set(1,mop.in(1)); // Memory from mem op
+ _inputs.set(2,base); // Base handed in
+ }
+
+ assert ptr() == base;
+ _inputs.setX(3,idx);
+ _off = off;
+ _scale = scale;
+ _imm = imm;
+ }
+
+ // Store-based flavors have a value edge
+ MemOpX86( Node op, MemOpNode mop, Node base, Node idx, int off, int scale, int imm, Node val ) {
+ this(op,mop,base,idx,off,scale,imm);
+ _inputs.setX(4,val);
+ }
+
+ Node idx() { return in(3); }
+ Node val() { return in(4); }
+
+ @Override protected ExprPrinter<Node> _printMach(ExprPrinter<Node> p) { return p.p(".").p(_name); }
+
+ @Override public boolean isMem() { return _type instanceof TypeMem; }
+
+ @Override public String label() { return op(); }
+ @Override public Type compute() { throw Utils.TODO(); }
+ @Override public Node idealize() { throw Utils.TODO(); }
+
+ // Register mask allowed on input i.
+ @Override public RegMask regmap(int i) {
+ if( i==1 ) return null; // Memory
+ if( i==2 ) return x86_64_v2.RMASK; // base
+ if( i==3 ) return x86_64_v2.RMASK; // index
+ if( i==4 ) return x86_64_v2.RMASK; // value
+ return null; // Trailing anti-dependence edges
+ }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { throw Utils.TODO(); }
+
+ @Override public int encoding(ByteArrayOutputStream bytes) { throw Utils.TODO(); }
+
+
+ // "[base + idx<<2 + 12]"
+ SB asm_address(CodeGen code, SB sb) {
+ sb.p("[").p(code.reg(ptr()));
+ if( idx() != null ) {
+ sb.p("+").p(code.reg(idx()));
+ if( _scale != 0 )
+ sb.p("<<").p(_scale);
+ }
+ if( _off != 0 )
+ sb.p("+").p(_off);
+ return sb.p("]");
+ }
+
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulFX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulFX86.java
new file mode 100644
index 00000000..aef813e1
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulFX86.java
@@ -0,0 +1,30 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.TypeFloat;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+public class MulFX86 extends MachConcreteNode implements MachNode {
+ MulFX86( Node mulf) { super(mulf); }
+
+ // 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: "mulf 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 "mulf"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulIX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulIX86.java
new file mode 100644
index 00000000..e9eb17fc
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulIX86.java
@@ -0,0 +1,36 @@
+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 MulIX86 extends MachConcreteNode implements MachNode {
+ final TypeInteger _ti;
+ MulIX86(Node mul, TypeInteger ti) {super(mul); _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: "muli 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 "muli"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulX86.java
new file mode 100644
index 00000000..15fd9496
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulX86.java
@@ -0,0 +1,30 @@
+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 MulX86 extends MachConcreteNode implements MachNode {
+
+ MulX86( Node mul ) { super(mul); }
+
+ // 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: "mul 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 "mul"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NewX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NewX86.java
new file mode 100644
index 00000000..c70e1c54
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NewX86.java
@@ -0,0 +1,40 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import java.io.ByteArrayOutputStream;
+
+public class NewX86 extends NewNode implements MachNode {
+
+ // A pre-zeroed chunk of memory.
+ NewX86( NewNode nnn ) { super(nnn); }
+
+ // Register mask allowed on input i, the size
+ @Override public RegMask regmap(int i) {
+ // Size
+ if( i==2 ) return x86_64_v2.RDI_MASK;
+ // Memory has no register
+ return null;
+ }
+ @Override public RegMask outregmap() { throw Utils.TODO(); }
+
+ // Register mask allowed as a result. Pointer result in standard calling
+ // convention.
+ @Override public RegMask outregmap(int i) {
+ if( i == 0 ) return x86_64_v2.RET_MASK;
+ // Memory has no register
+ return null;
+ }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form: "alloc #bytes"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(size()));
+ }
+
+ @Override public String op() { return "alloc"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/OrIX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/OrIX86.java
new file mode 100644
index 00000000..d3940838
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/OrIX86.java
@@ -0,0 +1,44 @@
+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 OrIX86 extends MachConcreteNode implements MachNode {
+
+ final TypeInteger _ti;
+ OrIX86(Node or, TypeInteger ti) {super(or); _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 ori | #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 "ori"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/OrX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/OrX86.java
new file mode 100644
index 00000000..7c25bdae
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/OrX86.java
@@ -0,0 +1,33 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import java.io.ByteArrayOutputStream;
+
+public class OrX86 extends MachConcreteNode implements MachNode{
+ OrX86(Node or) {
+ super(or);
+ }
+
+ // 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; }
+
+ @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: "and dst & #imm"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1)));
+ }
+
+ @Override public String op() { return "or"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ParmX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ParmX86.java
new file mode 100644
index 00000000..0fcc922d
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ParmX86.java
@@ -0,0 +1,23 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.simple.RegMask;
+import com.seaofnodes.simple.node.ParmNode;
+import com.seaofnodes.simple.node.MachNode;
+import java.io.ByteArrayOutputStream;
+
+public class ParmX86 extends ParmNode implements MachNode {
+ final RegMask _rmask;
+ ParmX86( ParmNode parm ) {
+ super(parm);
+ // default to int
+ _rmask = x86_64_v2.callInMask(fun().sig(),_idx);
+ }
+
+ // Register mask allowed on input i. 0 for no register.
+ @Override public RegMask regmap(int i) { return null; }
+ // Register mask allowed as a result. Calling convention register
+ @Override public RegMask outregmap() { return _rmask; }
+
+ // Encoding is appended into the byte array. Returns size
+ @Override public int encoding(ByteArrayOutputStream bytes) { return 0; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ProjX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ProjX86.java
new file mode 100644
index 00000000..77fd7d34
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ProjX86.java
@@ -0,0 +1,25 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.simple.RegMask;
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.node.ProjNode;
+import com.seaofnodes.simple.node.MachNode;
+import java.io.ByteArrayOutputStream;
+
+public class ProjX86 extends ProjNode implements MachNode {
+
+ ProjX86( ProjNode p ) { super(p); }
+
+ // Register mask allowed on input i. 0 for no register.
+ @Override public RegMask regmap(int i) { return null; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() {
+ return ((MachNode)in(0)).outregmap(_idx);
+ }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RetX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RetX86.java
new file mode 100644
index 00000000..21db209c
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RetX86.java
@@ -0,0 +1,48 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.FunNode;
+import com.seaofnodes.simple.node.ReturnNode;
+import com.seaofnodes.simple.node.MachNode;
+import java.io.ByteArrayOutputStream;
+
+// Return
+public class RetX86 extends ReturnNode implements MachNode {
+ RetX86( ReturnNode ret, FunNode fun ) { super(ret, fun); fun.setRet(this); }
+
+ // Correct Nodes outside the normal edges
+ @Override public void postSelect() {
+ FunNode fun = (FunNode)rpc().in(0);
+ _fun = fun;
+ fun.setRet(this);
+ }
+
+ // Register mask allowed on input i.
+ // This is the normal calling convention
+ @Override public RegMask regmap(int i) {
+ return switch( i ) {
+ case 0 -> null;
+ case 1 -> null;
+ case 2 -> x86_64_v2.RET_MASK;
+ case 3 -> null; // RPC is always on stack
+ default -> throw Utils.TODO();
+ };
+ }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return null; }
+
+ // 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/SarIX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SarIX86.java
new file mode 100644
index 00000000..f5337ee8
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SarIX86.java
@@ -0,0 +1,37 @@
+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;
+
+// Arithmetic Right Shift
+public class SarIX86 extends MachConcreteNode implements MachNode {
+ final TypeInteger _ti;
+ SarIX86(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/SarX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SarX86.java
new file mode 100644
index 00000000..cb0b18bf
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SarX86.java
@@ -0,0 +1,42 @@
+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{
+ SarX86(Node sar) {super(sar);}
+
+ // Register mask allowed on input i.
+ // This is the normal calling convention
+
+ // CL register must be used for input(2)
+ @Override public RegMask regmap(int i) {
+ if(i == 1) return x86_64_v2.WMASK;
+ if(i == 2) return x86_64_v2.RCX_MASK;
+ throw Utils.TODO();
+ }
+
+ @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: "sar dst >> src"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1)));
+ }
+
+ @Override public String op() { return "sar"; }
+
+}
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/ShlX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlX86.java
new file mode 100644
index 00000000..b95f9f46
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlX86.java
@@ -0,0 +1,39 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import java.io.ByteArrayOutputStream;
+
+public class ShlX86 extends MachConcreteNode implements MachNode {
+ ShlX86(Node shl) {
+ super(shl);
+ };
+
+ // Register mask allowed on input i.
+ // This is the normal calling convention
+
+ // CL register must be used for input(2)
+ @Override public RegMask regmap(int i) {
+ if(i == 1) return x86_64_v2.WMASK;
+ if(i == 2) return x86_64_v2.RCX_MASK;
+ throw Utils.TODO();
+ }
+
+ @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: "shl dst << src"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1)));
+ }
+
+ @Override public String op() { return "shl"; }
+}
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..80115ff2
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrIX86.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;
+
+// Logical Right Shift
+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/ShrX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrX86.java
new file mode 100644
index 00000000..43fc730b
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrX86.java
@@ -0,0 +1,38 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import java.io.ByteArrayOutputStream;
+
+public class ShrX86 extends MachConcreteNode implements MachNode {
+ ShrX86(Node shr) {
+ super(shr);
+ };
+ // Register mask allowed on input i.
+ // This is the normal calling convention
+
+ // CL register must be used for input(2)
+ @Override public RegMask regmap(int i) {
+ if(i == 1) return x86_64_v2.WMASK;
+ if(i == 2) return x86_64_v2.RCX_MASK;
+ throw Utils.TODO();
+ }
+
+ @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: "shr dst >>> src"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1)));
+ }
+
+ @Override public String op() { return "shr"; }
+}
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..76bd2afb
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/StoreX86.java
@@ -0,0 +1,31 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.print.ExprPrinter;
+
+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;
+
+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 protected ExprPrinter<Node> _printMach(ExprPrinter<Node> p) {
+ return p.p(".").p(_name).p("=").p(val()==null ? _imm : val()).p(";");
+ }
+
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return null; }
+
+ // 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..c7edfcc4
--- /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 null; }
+ // 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..d71a49dc
--- /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..e7e0b874
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java
@@ -0,0 +1,443 @@
+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<<FLAGS);
+
+ // Return single int/ptr register
+ public static RegMask RET_MASK = new RegMask(1<<RAX);
+ public static RegMask RET_FMASK = new RegMask(1<<XMM0);
+
+ public static RegMask RDI_MASK = new RegMask(1L<<RDI);
+ public static RegMask RCX_MASK = new RegMask(1L<<RCX);
+ public static RegMask RDX_MASK = new RegMask(1L<<RDX);
+ public static RegMask R08_MASK = new RegMask(1L<<R08);
+ public static RegMask R09_MASK = new RegMask(1L<<R09);
+ public static RegMask RSI_MASK = new RegMask(1L<<RSI);
+
+ public static RegMask XMM0_MASK = new RegMask(1L<<XMM0);
+ public static RegMask XMM1_MASK = new RegMask(1L<<XMM1);
+ public static RegMask XMM2_MASK = new RegMask(1L<<XMM2);
+ public static RegMask XMM3_MASK = new RegMask(1L<<XMM3);
+ public static RegMask XMM4_MASK = new RegMask(1L<<XMM4);
+ public static RegMask XMM5_MASK = new RegMask(1L<<XMM5);
+ public static RegMask XMM6_MASK = new RegMask(1L<<XMM6);
+ public static RegMask XMM7_MASK = new RegMask(1L<<XMM7);
+ public static RegMask[] XMMS = new RegMask[]{
+ XMM0_MASK, XMM1_MASK, XMM2_MASK, XMM3_MASK,
+ XMM4_MASK, XMM5_MASK, XMM6_MASK, XMM7_MASK,
+ };
+
+ // Calling conv metadata
+ public int GPR_COUNT_CONV_WIN64 = 4; // RCX, RDX, R9, R9
+ public int XMM_COUNT_CONV_WIN64 = 4; // XMM0L, XMM1L, XMM2L, XMM3L
+
+ public int GPR_COUNT_CONV_SYSTEM_V = 6; // RDI, RSI, RDX, RCX, R8, R9
+ public int XMM_COUNT_CONV_SYSTEM_V = 4; // XMM0, XMM1, XMM2, XMM3 ....
+ // Human-readable name for a register number, e.g. "RAX".
+ // Hard crash for bad register number, fix yer bugs!
+ public static final String[] REGS = new String[] {
+ "rax" , "rcx" , "rdx" , "rbx" , "rsp" , "rbp" , "rsi" , "rdi" ,
+ "r8" , "r9" , "r10" , "r11" , "r12" , "r13" , "r14" , "r15" ,
+ "xmm0", "xmm1", "xmm2" , "xmm3" , "xmm4" , "xmm5" , "xmm6" , "xmm7" ,
+ "xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15",
+ "flags",
+ };
+ @Override public String reg( int reg ) {
+ return reg < REGS.length ? REGS[reg] : "[rsp+"+(reg-REGS.length)*4+"]";
+ }
+
+ // Calling convention; returns a machine-specific register
+ // for incoming argument idx.
+ // index 0 for control, 1 for memory, real args start at index 2
+ static int callInArg( TypeFunPtr tfp, int idx ) {
+ int[] cargs = switch( CodeGen.CODE._callingConv ) {
+ case CodeGen.CallingConv.SystemV -> CALLINARG_SYSTEMV_INT;
+ case CodeGen.CallingConv.Win64 -> CALLINARG_WIN64_INT;
+ };
+ if( idx < 2 ) return -1;
+ if( idx >= cargs.length )
+ throw Utils.TODO(); // Pass on stack slot
+ if( tfp.arg(idx-2) instanceof TypeFloat )
+ return XMM0 + (idx-2);
+ return cargs[idx];
+ }
+
+ // WIN64(param passing)
+ static int[] CALLINARG_WIN64_INT = new int[] {
+ -1, // Control, no register
+ -1, // Memory, no register
+ RCX,
+ RDX,
+ R08,
+ R09,
+ };
+ static RegMask[] CALLINARG_WIN64_MASK = new RegMask[] {
+ null, // Control, no register
+ null, // Memory, no register
+ RCX_MASK,
+ RDX_MASK,
+ R08_MASK,
+ R09_MASK,
+ };
+
+ // 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;
+
+ static int[] CALLINARG_SYSTEMV_INT = new int[] {
+ -1, // Control, no register
+ -1, // Memory, no register
+ RDI,
+ RSI,
+ RDX,
+ RCX,
+ R08,
+ R09,
+ };
+ static RegMask[] CALLINARG_SYSTEMV_MASK = new RegMask[] {
+ null, // Control, no register
+ null, // Memory, no register
+ RDI_MASK,
+ RSI_MASK,
+ RDX_MASK,
+ RCX_MASK,
+ R08_MASK,
+ R09_MASK,
+ };
+ // 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( TypeFunPtr tfp, int idx ) {
+ RegMask[] cargs = switch( CodeGen.CODE._callingConv ) {
+ case CodeGen.CallingConv.SystemV -> CALLINARG_SYSTEMV_MASK;
+ case CodeGen.CallingConv.Win64 -> CALLINARG_WIN64_MASK;
+ };
+ if( idx < 2 ) return null;
+ if( idx >= cargs.length )
+ throw Utils.TODO(); // Pass on stack slot
+ if( tfp.arg(idx-2) instanceof TypeFloat )
+ return XMMS[idx-2];
+ return cargs[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 CallEndX86(cend);
+ 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 BulkMemPhiNode phi -> new BulkMemPhiNode(phi);
+ case MemPhiNode phi -> new MemPhiNode(phi);
+ 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 ToIntegerNode cvt -> new F8ToIX86(cvt);
+ 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 )
+ return new AddMemX86(add,address(ld),ld.ptr(),idx,off,scale, imm(lhs),val);
+
+ // 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);
+ return new AndX86(and);
+ }
+
+ 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);
+ return new OrX86(or);
+ }
+
+ 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 SarIX86(sar, ti);
+ return new SarX86(sar);
+ }
+
+ private Node shl( ShlNode shl ) {
+ if( shl.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti )
+ return new ShlIX86(shl, ti);
+ return new ShlX86(shl);
+ }
+
+ private Node shr(ShrNode shr) {
+ if( shr.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti )
+ return new ShrIX86(shr, ti);
+ return new ShrX86(shr);
+ }
+
+ 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 extends MemOpNode> 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;
+ }
+
+}
diff --git a/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java b/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
index 3e23968a..1992a0c0 100644
--- a/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
+++ b/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
@@ -6,7 +6,7 @@ import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.node.Node;
import com.seaofnodes.simple.type.TypeMem;
-/** Chapter 18's view of the IR; browser, transport and layout are shared. */
+/** Chapter 19's view of the IR; browser, transport and layout are shared. */
public class SimpleGraphAdapter extends GraphAdapter<Node> {
@Override protected String edgeName(Node n, int i) {
diff --git a/src/main/java/com/seaofnodes/simple/print/SimpleGraphObserver.java b/src/main/java/com/seaofnodes/simple/print/SimpleGraphObserver.java
index b2dd3107..4ec55f4d 100644
--- a/src/main/java/com/seaofnodes/simple/print/SimpleGraphObserver.java
+++ b/src/main/java/com/seaofnodes/simple/print/SimpleGraphObserver.java
@@ -7,7 +7,7 @@ import com.seaofnodes.simple.node.Node;
import java.util.ArrayList;
import java.io.IOException;
-/** Chapter 18's compilation context; capture and event bookkeeping are shared. */
+/** Chapter 19's compilation context; capture and event bookkeeping are shared. */
public class SimpleGraphObserver extends GraphCapture<Node> {
private CodeGen _code;
public SimpleGraphObserver() { super(new SimpleGraphAdapter()); }
@@ -20,16 +20,15 @@ public class SimpleGraphObserver extends GraphCapture<Node> {
_code.parse().opto().typeCheck();
}
@Override protected String phase() { return _code._phase.name(); }
- @Override protected int pos() { return _code.P != null ? _code.P.pos() : -1; }
+ @Override protected int pos() { return _code._phase == CodeGen.Phase.Parse ? _code.P.pos() : -1; }
@Override protected void detach() {
if( _code != null ) _code._obs = null;
_code = null;
}
- @Override protected Node scope() { return _code.P == null ? null : _code.P._scope; }
+ @Override protected Node scope() { return _code.P._scope; }
@Override protected void roots(ArrayList<Node> roots) {
roots.add(_code._stop);
- if( _code.P != null ) {
- roots.add(com.seaofnodes.simple.Parser.START);
+ if( _code._phase == CodeGen.Phase.Parse ) {
roots.addAll(_code.P._xScopes);
}
}
diff --git a/src/main/java/com/seaofnodes/simple/type/Type.java b/src/main/java/com/seaofnodes/simple/type/Type.java
index 34f54be8..1ce5dbd0 100644
--- a/src/main/java/com/seaofnodes/simple/type/Type.java
+++ b/src/main/java/com/seaofnodes/simple/type/Type.java
@@ -236,8 +236,4 @@ public class Type {
// This is used by error messages, and is a shorted print.
public String str() { return STRS[_type]; }
-
- public static void reset() {
- TypeFunPtr.reset();
- }
}
diff --git a/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java b/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java
index 61fc71cf..94352790 100644
--- a/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java
+++ b/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java
@@ -25,7 +25,6 @@ public class TypeFunPtr extends TypeNil {
// within the TypeTuple. Can be upgraded to a BitSet for larger classes of
// functions. Negative means "these 63 concrete bits plus infinite unknown more"
public final long _fidxs; // 63 unique functions per signature
- public String _name; // Optional debug function name; only for named single function
private TypeFunPtr(byte nil, TypeTuple sig, Type ret, long fidxs) {
super(TFUNPTR,nil);
@@ -35,30 +34,16 @@ public class TypeFunPtr extends TypeNil {
_fidxs = fidxs;
}
- static TypeFunPtr make( byte nil, TypeTuple sig, Type ret, long fidxs ) { return new TypeFunPtr(nil,sig,ret,fidxs).intern(); }
+ public static TypeFunPtr make( byte nil, TypeTuple sig, Type ret, long fidxs ) { return new TypeFunPtr(nil,sig,ret,fidxs).intern(); }
public static TypeFunPtr make( boolean nil, TypeTuple sig, Type ret ) { return make((byte)(nil ? 3 : 2),sig,ret,-1); }
- @Override TypeFunPtr makeFrom( byte nil ) { return nil ==_nil ? this : make( nil,_sig,_ret, _fidxs).setName(_name); }
- public TypeFunPtr makeFrom( Type ret ) { return ret ==_ret ? this : make( _nil,_sig, ret, _fidxs).setName(_name); }
- public TypeFunPtr makeFrom( int fidx ) { return make((byte)2, _sig,_ret,1L<<fidx ).setName(_name); }
-
- // Compute "function indices": FIDX
- private static final HashMap<TypeTuple,Integer> FIDXS = new HashMap<>();
- private static int nextFIDX(TypeTuple sig) {
- Integer i = FIDXS.get(sig);
- int fidx = i==null ? 0 : i;
- FIDXS.put(sig,fidx+1); // Track count per sig
- assert fidx<64; // TODO: need a larger FIDX space
- return fidx; // Return
- }
- public static TypeFunPtr makeFun( TypeTuple sig, Type ret ) {
- return make((byte)2,sig,ret,1L<<nextFIDX(sig));
- }
+ @Override TypeFunPtr makeFrom( byte nil ) { return nil ==_nil ? this : make( nil,_sig,_ret, _fidxs); }
+ public TypeFunPtr makeFrom( Type ret ) { return ret ==_ret ? this : make( _nil,_sig, ret, _fidxs); }
+ public TypeFunPtr makeFrom( int fidx ) { return make((byte)2, _sig,_ret,1L<<fidx ); }
public static TypeFunPtr BOT = make((byte)3,TypeTuple.BOT,Type.BOTTOM,-1);
public static TypeFunPtr TEST = make((byte)2,TypeTuple.TEST,TypeInteger.BOT,1);
public static TypeFunPtr TEST0 = make((byte)3,TypeTuple.TEST,TypeInteger.BOT,3);
- public static TypeFunPtr MAIN = makeFun(TypeTuple.MAIN,Type.BOTTOM).setName("main");
- public static void gather(ArrayList<Type> ts) { ts.add(TEST); ts.add(TEST0); ts.add(BOT); ts.add(MAIN); }
+ public static void gather(ArrayList<Type> ts) { ts.add(TEST); ts.add(TEST0); ts.add(BOT); }
@Override
Type xmeet(Type t) {
@@ -93,15 +78,6 @@ public class TypeFunPtr extends TypeNil {
return _sig == ptr._sig && _ret == ptr._ret && _fidxs == ptr._fidxs && super.eq(ptr);
}
- public TypeFunPtr setName(String name) {
- if( name==null ) return this;
- assert _fidxs > 0 && Long.bitCount(_fidxs) == 1;
- // Name can be set to different things, just for debug
- //assert _name==null || _name.equals(name);
- _name = name;
- return this;
- }
-
@Override public String str() { return print(new SB()).toString();
}
@Override public SB print(SB sb) { return _print(sb,false,true); }
@@ -112,32 +88,20 @@ public class TypeFunPtr extends TypeNil {
if( this==BOT ) return sb.p("FunBot");
if( _nil==0 && _sig==TypeTuple.TOP && _ret==Type.TOP && _fidxs==0 ) return sb.p("FunTop");
sb.p(x()).p("{ ");
- if( n && _name!=null ) sb.p(_name);
- else {
- if( _sig._types!=null )
- for( Type t : _sig._types )
- _print(sb,g,t).p(" ");
- _print(sb.p(g ? "&rarr; " : "-> "),g,_ret).p(" #");
- if( isHigh() ) sb.p("~");
- long fidxs = isHigh() ? ~_fidxs : _fidxs;
- String fidx = fidxs==0 ? ""
- : Long.bitCount(fidxs) == 1 ? ""+Long.numberOfTrailingZeros(fidxs)
- : fidxs == -1 ? "ALL"
- : "b"+Long.toBinaryString(fidxs); // Just some function bits
- sb.p(fidx);
- }
- return sb.p("}").p(q());
+ if( _sig._types!=null )
+ for( Type t : _sig._types )
+ _print(sb,g,t).p(" ");
+ _print(sb.p(g ? "&rarr; " : "-> "),g,_ret).p(" #");
+ if( isHigh() ) sb.p("~");
+ long fidxs = isHigh() ? ~_fidxs : _fidxs;
+ String fidx = fidxs==0 ? ""
+ : Long.bitCount(fidxs) == 1 ? ""+Long.numberOfTrailingZeros(fidxs)
+ : fidxs == -1 ? "ALL"
+ : "b"+Long.toBinaryString(fidxs); // Just some function bits
+ return sb.p(fidx).p("}").p(q());
}
// Usage: for( long fidxs=fidxs(); fidxs!=0; fidxs=nextFIDX(fidxs) { int fidxs = Long.numberOfTrailingZeros(fidxs); ... }
public static long nextFIDX(long fidxs) { return fidxs & (fidxs-1); }
- public static void reset() {
- FIDXS.clear();
- FIDXS.put(MAIN._sig,1);
- for( Type t : INTERN.keySet() )
- if( t instanceof TypeFunPtr tfp && tfp!=MAIN)
- tfp._name = null;
- }
-
}
diff --git a/src/main/java/com/seaofnodes/simple/type/TypeStruct.java b/src/main/java/com/seaofnodes/simple/type/TypeStruct.java
index fff6a7db..89c79a1f 100644
--- a/src/main/java/com/seaofnodes/simple/type/TypeStruct.java
+++ b/src/main/java/com/seaofnodes/simple/type/TypeStruct.java
@@ -50,6 +50,7 @@ public class TypeStruct extends Type {
// Array
public static TypeStruct makeAry(TypeInteger len, int lenAlias, Type body, int bodyAlias) {
assert body instanceof TypeInteger || body instanceof TypeFloat || (body instanceof TypeNil tn && tn.nullable());
+ assert len.isa(TypeInteger.U32);
return make("[" + body.str() + "]",
Field.make("#" ,len , lenAlias,true ),
Field.make("[]",body,bodyAlias,false));
@@ -61,7 +62,7 @@ public class TypeStruct extends Type {
public static final TypeStruct S1 = make("S1", Field.make("a", TypeInteger.BOT, -1, false), Field.make("s2",TypeMemPtr.make((byte)2,S2F),-2, false) );
private static final TypeStruct S2 = make("S2", Field.make("b", TypeFloat .F64, -3, false), Field.make("s1",TypeMemPtr.make((byte)2,S1F),-4, false) );
- private static final TypeStruct ARY = makeAry(TypeInteger.BOT,-1,TypeInteger.BOT,-2);
+ private static final TypeStruct ARY = makeAry(TypeInteger.U32,-1,TypeInteger.BOT,-2);
public static void gather(ArrayList<Type> ts) { ts.add(TEST); ts.add(BOT); ts.add(S1); ts.add(S2); ts.add(ARY); }
@@ -225,6 +226,11 @@ public class TypeStruct extends Type {
private int[] offsets() { // Field byte offsets
// Compute a layout for a collection of fields
assert _fields != null; // No forward refs
+
+ // Array layout is different: len,[pad],body...
+ if( isAry() )
+ return _offs = new int[]{ 0, _fields[1]._type.log_size() < 3 ? 4 : 8 };
+
// Compute a layout
int[] cnts = new int[4]; // Count of fields at log field size
for( Field f : _fields )
diff --git a/src/test/java/com/seaofnodes/simple/BrainFuckTest.java b/src/test/java/com/seaofnodes/simple/BrainFuckTest.java
index 37ecde1c..8f09cb3d 100644
--- a/src/test/java/com/seaofnodes/simple/BrainFuckTest.java
+++ b/src/test/java/com/seaofnodes/simple/BrainFuckTest.java
@@ -63,7 +63,7 @@ for( int pc = 0; pc < program#; pc++ ) {
}
return output;
""");
- code.parse().opto().typeCheck().GCM();
+ code.parse().opto().typeCheck()/*.instSelect("x86_64_v2", "SystemV")*/.GCM().localSched();
assertEquals("Hello World!\n", Eval2.eval(code, 0, 10000));
}
}
diff --git a/src/test/java/com/seaofnodes/simple/Chapter03Test.java b/src/test/java/com/seaofnodes/simple/Chapter03Test.java
index 409e23ed..8212d246 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter03Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter03Test.java
@@ -46,7 +46,7 @@ public class Chapter03Test {
@Test
public void testRedeclareVar() {
try {
- new Parser("int a=1; int b=2; int c=0; int b=3; c=a+b;").parse();
+ new CodeGen("int a=1; int b=2; int c=0; int b=3; c=a+b;").parse();
fail();
} catch( RuntimeException e ) {
assertEquals("Redefining name 'b'",e.getMessage());
diff --git a/src/test/java/com/seaofnodes/simple/Chapter04Test.java b/src/test/java/com/seaofnodes/simple/Chapter04Test.java
index d2260e1f..934e794f 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter04Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter04Test.java
@@ -34,14 +34,14 @@ public class Chapter04Test {
public void testAddAddMul() {
CodeGen code = new CodeGen("return arg+0+arg;");
code.parse();
- assertEquals("return (arg*2);", code.print());
+ assertEquals("return (arg<<1);", code.print());
}
@Test
public void testPeephole3() {
CodeGen code = new CodeGen("return 1+arg+2+arg+3; ");
code.parse();
- assertEquals("return ((arg*2)+6);", code.print());
+ assertEquals("return ((arg<<1)+6);", code.print());
}
@Test
@@ -90,8 +90,7 @@ public class Chapter04Test {
// Adding functions means `main` does not constant fold the incoming arg value
@Ignore @Test
public void testConstantArg() {
- CodeGen code = new CodeGen("return arg; ", TypeInteger.constant(2));
- code.parse();
+ CodeGen code = new CodeGen("return arg; ", TypeInteger.constant(2), 123L).parse();
assertEquals("return 2;", code.expr().in(1).toString());
}
@@ -139,12 +138,8 @@ public class Chapter04Test {
@Test
public void testBug3() {
- try {
- new Parser("inta=1; return a;").parse();
- fail();
- } catch( RuntimeException e ) {
- assertEquals("Undefined name 'inta'",e.getMessage());
- }
+ try { new CodeGen("inta=1; return a;").parse(); fail(); }
+ catch( RuntimeException e ) { assertEquals("Undefined name 'inta'",e.getMessage()); }
}
@Test
@@ -156,8 +151,8 @@ public class Chapter04Test {
@Test
public void testBug5() {
- Parser parser = new Parser("return arg--2;");
- StopNode ret = parser.parse();
- assertEquals("return (arg--2);", ret.print());
+ CodeGen code = new CodeGen("return arg--2;");
+ code.parse();
+ assertEquals("return (arg--2);", code.print());
}
}
diff --git a/src/test/java/com/seaofnodes/simple/Chapter05Test.java b/src/test/java/com/seaofnodes/simple/Chapter05Test.java
index ff60c695..7a9414a2 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter05Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter05Test.java
@@ -64,7 +64,7 @@ else
b=a+1;
return a+b;""");
code.parse().opto();
- assertEquals("return ((arg*2)+Phi(Region,2,3));", code.print());
+ assertEquals("return ((arg<<1)+Phi(Region,2,3));", code.print());
}
@Test
@@ -79,7 +79,7 @@ else
a=b+1;
return a+b;""");
code.parse().opto();
- assertEquals("return ((Phi(Region,(arg*2),arg)+arg)+Phi(Region,4,5));", code.print());
+ assertEquals("return ((Phi(Region,(arg<<1),arg)+arg)+Phi(Region,4,5));", code.print());
}
@Test
@@ -150,79 +150,51 @@ return a;
@Test
public void testTrue() {
- StopNode stop = new Parser("return true;").parse();
- assertEquals("return 1;",stop.ret().print());
+ CodeGen code = new CodeGen("return true;").parse();
+ assertEquals("return 1;",code.print());
}
@Test
public void testHalfDef() {
- try {
- new CodeGen("if( arg==1 ) int b=2; return b;").parse();
- fail();
- } catch( RuntimeException e ) {
- assertEquals("Cannot define a 'b' on one arm of an if",e.getMessage());
- }
+ try { new CodeGen("if( arg==1 ) int b=2; return b;").parse(); fail(); }
+ catch( RuntimeException e ) { assertEquals("Cannot define a 'b' on one arm of an if",e.getMessage()); }
}
@Test
public void testHalfDef2() {
- try {
- new Parser("if( arg==1 ) { int b=2; } else { int b=3; } return b;").parse();
- fail();
- } catch( RuntimeException e ) {
- assertEquals("Undefined name 'b'",e.getMessage());
- }
+ try { new CodeGen("if( arg==1 ) { int b=2; } else { int b=3; } return b;").parse(); fail(); }
+ catch( RuntimeException e ) { assertEquals("Undefined name 'b'",e.getMessage()); }
}
@Test
public void testRegress1() {
- try {
- new CodeGen("if(arg==2) int a=1; else int b=2; return a;").parse();
- fail();
- } catch( RuntimeException e ) {
- assertEquals("Cannot define a 'a' on one arm of an if",e.getMessage());
- }
+ try { new CodeGen("if(arg==2) int a=1; else int b=2; return a;").parse(); fail(); }
+ catch( RuntimeException e ) { assertEquals("Cannot define a 'a' on one arm of an if",e.getMessage()); }
}
@Test
public void testBadNum() {
- try {
- new Parser("return 1-;").parse();
- fail();
- } catch( RuntimeException e ) {
- assertEquals("Syntax error, expected an identifier or expression: ;",e.getMessage());
- }
+ try { new CodeGen("return 1-;").parse(); fail(); }
+ catch( RuntimeException e ) { assertEquals("Syntax error, expected an identifier or expression: ;",e.getMessage()); }
}
@Test
public void testKeyword1() {
- try {
- new Parser("int true=0; return true;").parse();
- fail();
- } catch( RuntimeException e ) {
- assertEquals("Expected an identifier, found 'true'",e.getMessage());
- }
+ try { new CodeGen("int true=0; return true;").parse(); fail(); }
+ catch( RuntimeException e ) { assertEquals("Expected an identifier, found 'true'",e.getMessage()); }
}
@Test
public void testKeyword2() {
- try {
- new Parser("int else=arg; if(else) else=2; else else=1; return else;").parse();
- fail();
- } catch( RuntimeException e ) {
- assertEquals("Expected an identifier, found 'else'",e.getMessage());
- }
+ try { new CodeGen("int else=arg; if(else) else=2; else else=1; return else;").parse(); fail(); }
+ catch( RuntimeException e ) { assertEquals("Expected an identifier, found 'else'",e.getMessage()); }
}
@Test
public void testKeyword3() {
- try {
- new CodeGen("int a=1; ififif(arg);a=2;return a;").parse();
- fail();
- } catch( RuntimeException e ) {
- assertEquals("Undefined name 'ififif'",e.getMessage());
- }
+ try { new CodeGen("int a=1; ififif(arg);a=2;return a;").parse(); fail(); }
+ catch( RuntimeException e ) { assertEquals("Undefined name 'ififif'",e.getMessage()); }
}
}
diff --git a/src/test/java/com/seaofnodes/simple/Chapter06Test.java b/src/test/java/com/seaofnodes/simple/Chapter06Test.java
index 2107cdb0..117531f0 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter06Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter06Test.java
@@ -103,8 +103,7 @@ else if( arg==3 )
else
a=5;
return a;
-""", TypeInteger.constant(2));
- code.parse().opto();
+""", TypeInteger.constant(2), 123L).parse().opto();
assertEquals("return 5;", code.print());
}
@@ -124,8 +123,7 @@ else if( arg==3 )
else
a=5;
return a;
-""", TypeInteger.constant(1));
- code.parse().opto();
+""", TypeInteger.constant(1), 123L).parse().opto();
assertEquals("return 3;", code.print());
}
@@ -165,7 +163,7 @@ else if( arg==3 )
else
a=5;
return a;
-""", TypeInteger.constant(1));
+""", TypeInteger.constant(1), 123L);
code.parse().opto();
assertEquals("return 3;", code.print());
}
@@ -186,7 +184,7 @@ else if( arg==3 )
else
a=5;
return a;
-""", TypeInteger.constant(3));
+""", TypeInteger.constant(3), 123L);
code.parse().opto();
assertEquals("return 4;", code.print());
}
@@ -210,7 +208,8 @@ return a+b;
@Test
public void testDemo1True() {
- CodeGen code = new CodeGen("""
+ CodeGen code = new CodeGen(
+"""
arg=1;
int a = 0;
int b = 1;
@@ -220,14 +219,15 @@ if( arg ) {
else b = 3;
}
return a+b;
-""", TypeInteger.constant(1));
+""", TypeInteger.constant(1), 123L);
code.parse().opto();
assertEquals("return 4;", code.print());
}
@Test
public void testDemo1False() {
- CodeGen code = new CodeGen("""
+ CodeGen code = new CodeGen(
+"""
arg=0;
int a = 0;
int b = 1;
@@ -237,7 +237,7 @@ if( arg ) {
else b = 3;
}
return a+b;
-""", TypeInteger.constant(0));
+""", TypeInteger.constant(0), 123L);
code.parse().opto();
assertEquals("return 1;", code.print());
}
@@ -263,7 +263,8 @@ return a+b+c;
@Test
public void testDemo2True() {
- CodeGen code = new CodeGen("""
+ CodeGen code = new CodeGen(
+"""
arg=1;
int a = 0;
int b = 1;
@@ -275,14 +276,15 @@ if( arg ) {
else b = 3;
}
return a+b+c;
-""", TypeInteger.constant(1));
+""", TypeInteger.constant(1), 123L);
code.parse().opto();
assertEquals("return 6;", code.print());
}
@Test
public void testDemo2arg2() {
- CodeGen code = new CodeGen("""
+ CodeGen code = new CodeGen(
+"""
arg=2;
int a = 0;
int b = 1;
@@ -294,7 +296,7 @@ if( arg ) {
else b = 3;
}
return a+b+c;
-""", TypeInteger.constant(2));
+""", TypeInteger.constant(2), 123L);
code.parse().opto();
assertEquals("return 5;", code.print());
}
diff --git a/src/test/java/com/seaofnodes/simple/Chapter08Test.java b/src/test/java/com/seaofnodes/simple/Chapter08Test.java
index 03a2c50a..350df18b 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter08Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter08Test.java
@@ -1,8 +1,6 @@
package com.seaofnodes.simple;
-import com.seaofnodes.simple.evaluator.Evaluator;
import com.seaofnodes.simple.node.*;
-import com.seaofnodes.simple.type.TypeInteger;
import org.junit.Assert;
import org.junit.Test;
@@ -147,7 +145,7 @@ return arg;
@Test
public void testBreakOutsideLoop() {
try {
- new Parser("""
+ new CodeGen("""
if(arg <= 10) {
break;
arg = arg + 1;
diff --git a/src/test/java/com/seaofnodes/simple/Chapter09Test.java b/src/test/java/com/seaofnodes/simple/Chapter09Test.java
index b210f7e9..10f4a78b 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter09Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter09Test.java
@@ -41,7 +41,7 @@ else {
return x;
""");
code.parse().opto();
- assertEquals("return Phi(Region,(arg*2),(Mul+1));", code.print());
+ assertEquals("return Phi(Region,(arg<<1),(Shl+1));", code.print());
Assert.assertEquals( "2", Eval2.eval(code, 1));
Assert.assertEquals("23", Eval2.eval(code, 11));
}
@@ -255,7 +255,7 @@ while(0==1) while(v0)
v0=1+v0;
""");
code.parse().opto();
- assertEquals("return ~int;", code.print());
+ assertEquals("return 0;", code.print());
}
@Test
diff --git a/src/test/java/com/seaofnodes/simple/Chapter11Test.java b/src/test/java/com/seaofnodes/simple/Chapter11Test.java
index 9fd392d2..14819869 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter11Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter11Test.java
@@ -48,17 +48,11 @@ public class Chapter11Test {
assertEquals("S{x=1,y=2,z=3}",Eval2.eval(code,4));
}
- @Test public void testLateSliceWorklist() throws ReflectiveOperationException {
+ @Test public void testLateSliceWorklist() {
// Seed 97 exposed a store selecting a bulk predecessor without queuing
// it. Keep both the worklist assertion and the final heap check.
for (int seed : new int[]{0,97,123,456}) {
- var code = new CodeGen(Chapter10Test.NESTED_MEMORY).parse();
- var field = IterPeeps.class.getDeclaredField("WORK");
- field.setAccessible(true);
- Object work = field.get(null);
- field = work.getClass().getDeclaredField("_R");
- field.setAccessible(true);
- ((java.util.Random)field.get(work)).setSeed(seed);
+ var code = new CodeGen(Chapter10Test.NESTED_MEMORY,com.seaofnodes.simple.type.TypeInteger.BOT,seed).parse();
code.opto();
assertEquals("S{x=9,y=25,z=17}",Eval2.eval(code,4));
}
diff --git a/src/test/java/com/seaofnodes/simple/Chapter12Test.java b/src/test/java/com/seaofnodes/simple/Chapter12Test.java
index a15132d4..90aaf631 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter12Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter12Test.java
@@ -183,7 +183,7 @@ return new S1.s=new S2;
struct I {int i;};
struct P { !I? !pi; };
!P !p1 = new P;
-!P p2 = new P;
+!P !p2 = new P;
p2.pi = new I;
p2.pi.i = 2;
if (arg) p1 = new P;
diff --git a/src/test/java/com/seaofnodes/simple/Chapter15Test.java b/src/test/java/com/seaofnodes/simple/Chapter15Test.java
index 97f695bf..c38dfb00 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter15Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter15Test.java
@@ -142,7 +142,7 @@ struct S { int x; flt y; };
!S !s = new S; s.x=99; s.y = 3.14;
// Double-d array of Ss. Fill in one row.
-!S?[]?[] iss = new !S?[]?[2];
+!S?[]?[] !iss = new !S?[]?[2];
iss[0] = new !S?[7];
iss[0][2] = s;
@@ -151,10 +151,12 @@ flt rez = 1.2;
if( iss[arg] )
if( iss[arg][2] )
rez = iss[arg][2].y;
-return rez;
-""");
- try { code.parse().opto().typeCheck(); fail(); }
- catch( Exception e ) { assertEquals("Might be null accessing '[]'",e.getMessage()); }
+return rez;""");
+ code.parse().opto().typeCheck();
+ assertEquals("return Phi(Region,Phi(Region,.y,1.2),1.2);", code.print());
+ assertEquals("3.14", Eval2.eval(code, 0));
+ try { Eval2.eval(code, 10); fail(); } // Fails AIOOBE in Eval2, no range checks
+ catch( Exception e ) { assertEquals("Index 10 out of bounds for length 2",e.getMessage()); }
}
@Test
diff --git a/src/test/java/com/seaofnodes/simple/Chapter17bTest.java b/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
index e83b43af..19cc25d9 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
@@ -39,7 +39,7 @@ return arg++;
return arg+++arg++;
""");
code.parse().opto();
- assertEquals("return ((arg*2)+1);", code.print());
+ assertEquals("return ((arg<<1)+1);", code.print());
assertEquals("1", Eval2.eval(code, 0));
}
@@ -50,7 +50,7 @@ return arg+++arg++;
return -arg---arg--;
""");
code.parse().opto();
- assertEquals("return (-((arg*2)+-1));", code.print());
+ assertEquals("return (-((arg<<1)+-1));", code.print());
assertEquals("1", Eval2.eval(code, 0));
}
@@ -344,7 +344,7 @@ var b = arg ? new Bar : null;
return b ? b.x++ + b.x++ : -1;
""");
code.parse().opto();
- assertEquals("return Phi(Region,((.x*2)+1),-1);", code.print());
+ assertEquals("return Phi(Region,((.x<<1)+1),-1);", code.print());
assertEquals("-1", Eval2.eval(code, 0));
assertEquals("1", Eval2.eval(code, 1));
}
@@ -357,7 +357,7 @@ var b = arg ? new Bar;
return b ? b.x++ + b.x++ : -1;
""");
code.parse().opto();
- assertEquals("return Phi(Region,((.x*2)+1),-1);", code.print());
+ assertEquals("return Phi(Region,((.x<<1)+1),-1);", code.print());
assertEquals("-1", Eval2.eval(code, 0));
assertEquals("1", Eval2.eval(code, 1));
}
diff --git a/src/test/java/com/seaofnodes/simple/Chapter18Test.java b/src/test/java/com/seaofnodes/simple/Chapter18Test.java
index 7f2b92ac..f965edd4 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter18Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter18Test.java
@@ -9,30 +9,6 @@ import static org.junit.Assert.fail;
import org.junit.Ignore;
public class Chapter18Test {
- @Test public void testFloatArrays() {
- for( String type : new String[]{"flt","f64","f32"} ) {
- CodeGen zero = new CodeGen("var a = new "+type+"[arg]; return a[arg-1];")
- .parse().opto().typeCheck();
- assertEquals("0.0",Eval2.eval(zero,3));
- new CodeGen(type+" x = arg; var a = new "+type+"[arg]; a[0] = x; return a[0];")
- .parse().opto().typeCheck();
- CodeGen loop = new CodeGen("var a = new "+type+"[arg]; "+
- "for(int i=0; i<arg; i++) a[i] += i+0.25; return a[0]+a[arg-1];")
- .parse().opto().typeCheck();
- assertEquals("0.5",Eval2.eval(loop,1));
- assertEquals("3.5",Eval2.eval(loop,4));
-
- CodeGen rounded = new CodeGen("var a = new "+type+"[arg]; "+
- "for(int i=0; i<arg; i++) a[i] = 16777216.0+i; return a[arg-1];")
- .parse().opto().typeCheck();
- assertEquals(type.equals("f32") ? "1.6777216E7" : "1.6777217E7",Eval2.eval(rounded,2));
- }
- CodeGen rounded = new CodeGen("var a = new f32[arg]; "+
- "for(int i=0; i<arg; i++) a[i] = 16777217.0; return a[arg-1];")
- .parse().opto().typeCheck();
- assertEquals("1.6777216E7",Eval2.eval(rounded,2));
- }
-
@Test public void testNeverExitLoopDepth() {
String[] sources = {
"if(arg) while(1) {} return 7;",
@@ -204,7 +180,7 @@ return sq;
""");
code.parse().opto();
assertEquals("Stop[ return { sq}; return (Parm_x(sq,int)*x); ]", code._stop.toString());
- assertEquals("{ sq}", Eval2.eval(code, 3));
+ assertEquals("{ int -> int #1}", Eval2.eval(code, 3));
}
@Test
@@ -216,7 +192,7 @@ var sq = { int x ->
};
return sq(arg)+sq(3);
""");
- code.parse().opto();
+ code.parse().opto().typeCheck().GCM().localSched();
assertEquals("Stop[ return (sq( 3)+sq( arg)); return (Parm_x(sq,int,3,arg)*x); ]", code._stop.toString());
assertEquals("13", Eval2.eval(code, 2));
}
@@ -255,7 +231,7 @@ var fcn = arg ? { int x -> x*x; } : { int x -> x+x; };
return fcn(3);
""");
code.parse().opto();
- assertEquals("Stop[ return Phi(Region,{ int -> int #1},{ int -> int #2})( 3); return (Parm_x($fun,int,3)*x); return (Parm_x($fun,int,3)*2); ]", code._stop.toString());
+ assertEquals("Stop[ return Phi(Region,{ int -> int #1},{ int -> int #2})( 3); return (Parm_x($fun,int,3)*x); return (Parm_x($fun,int,3)<<1); ]", code._stop.toString());
assertEquals("6", Eval2.eval(code, 0));
assertEquals("9", Eval2.eval(code, 1));
}
@@ -264,7 +240,7 @@ return fcn(3);
@Test
public void testFcn5() {
CodeGen code = new CodeGen("val fact = { int x -> x <= 1 ? 1 : x*fact(x-1); }; return fact(arg);");
- code.parse().opto();
+ code.parse().opto().typeCheck();
assertEquals("Stop[ return fact( arg); return Phi(Region,1,(Parm_x(fact,int,arg,(x-1))*fact( Sub))); ]", code._stop.toString());
assertEquals( "1", Eval2.eval(code, 0));
assertEquals( "1", Eval2.eval(code, 1));
@@ -319,7 +295,6 @@ for(;;) {
assertEquals("3", Eval2.eval(code, 0));
}
-
@Test
public void testFcn9() {
CodeGen code = new CodeGen(
@@ -336,6 +311,31 @@ for(;;) {
assertEquals(null, Eval2.eval(code, 0));
}
+
+ @Test
+ public void testFcn10() {
+ CodeGen code = new CodeGen(
+"""
+struct Person {
+ int age;
+};
+
+val fcn = { !Person?[] !ps, int x ->
+ val tmp = ps[x];
+ if( ps[x] )
+ ps[x].age++;
+};
+
+var ps = new !Person?[2];
+ps[0] = new Person;
+ps[1] = new Person;
+fcn(ps,1);
+""");
+ code.parse().opto().typeCheck().GCM().localSched();
+ assertEquals("return 0;", code._stop.toString());
+ assertEquals("0", Eval2.eval(code, 0));
+ }
+
// Function break
@Test
public void testErr1() {
diff --git a/src/test/java/com/seaofnodes/simple/Chapter19Test.java b/src/test/java/com/seaofnodes/simple/Chapter19Test.java
new file mode 100644
index 00000000..1abda81f
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/Chapter19Test.java
@@ -0,0 +1,529 @@
+package com.seaofnodes.simple;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.*;
+
+
+import org.junit.Test;
+import static org.junit.Assert.*;
+import static org.junit.Assert.fail;
+import org.junit.Ignore;
+
+public class Chapter19Test {
+ @Test public void testPrintingFunctionLookup() {
+ var code = new CodeGen("return 0;").parse();
+ var tfp = TypeFunPtr.make((byte)2,TypeTuple.make(TypeInteger.constant(987654)),TypeInteger.constant(123456),1L<<30);
+ var con = new ConstantNode(tfp);
+ var call = new CallNode(null,code._start,con,con);
+ // Diagnostic lookup may construct/intern the return-erased linker key.
+ con.toString();
+ org.junit.Assert.assertNull(call.name());
+
+ var fun = new FunNode(null,tfp);
+ fun._name = "printerTarget";
+ fun.addDef(code._start);
+ code.link(fun);
+ var otherReturn = tfp.makeFrom(TypeInteger.constant(654321));
+ org.junit.Assert.assertSame(fun,code.link(otherReturn));
+ org.junit.Assert.assertTrue(con.toString().contains("printerTarget"));
+ assertEquals("printerTarget",call.name());
+ }
+
+
+ @Ignore
+ @Test
+ public void testJig() {
+ CodeGen code = new CodeGen("""
+return 0;
+""");
+ code.parse().opto().typeCheck();
+ assertEquals("return 0;", code._stop.toString());
+ assertEquals("0", Eval2.eval(code, 2));
+ }
+
+
+ @Test
+ public void testString() {
+ CodeGen code = new CodeGen(
+"""
+struct String {
+ u8[] !cs;
+ int _hashCode;
+};
+
+val equals = { !String !self, !String s ->
+ if( self == s ) return true;
+ if( self.cs# != s.cs# ) return false;
+ for( int i=0; i< self.cs#; i++ )
+ if( self.cs[i] != s.cs[i] )
+ return false;
+ return true;
+};
+
+val hashCode = { !String self ->
+ self._hashCode
+ ? self._hashCode
+ : (self._hashCode = _hashCodeString(self));
+};
+
+val _hashCodeString = { !String self ->
+ int hash=0;
+ if( self.cs ) {
+ for( int i=0; i< self.cs#; i++ )
+ hash = hash*31 + self.cs[i];
+ }
+ if( !hash ) hash = 123456789;
+ return hash;
+};
+
+!String !s = new String { cs = new u8[17]; };
+s.cs[0] = 67; // C
+s.cs[1] = 108; // l
+hashCode(s);""");
+ code.parse().opto().typeCheck().GCM().localSched();
+ assertEquals("Stop[ return Phi(Region,123456789,Phi(Loop,0,(.[]+((Phi_hash<<5)-Phi_hash)))); return Phi(Region,1,0,0,1); ]", code._stop.toString());
+ assertEquals("-2449306563677080489", Eval2.eval(code, 2));
+ }
+
+ @Test
+ public void testBasic0() {
+ CodeGen code = new CodeGen("return 0;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return 0;", code._stop.toString());
+ }
+
+ @Test
+ public void testBasic1() {
+ CodeGen code = new CodeGen("return arg+1;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return (inc,arg);", code._stop.toString());
+ }
+
+ @Test
+ public void testBasic2() {
+ CodeGen code = new CodeGen("return -17;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return -17;", code._stop.toString());
+ }
+
+
+ @Test
+ public void testBasic3() {
+ CodeGen code = new CodeGen("return arg==1;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return (set==,(cmp,arg));", code._stop.toString());
+ }
+
+ @Test
+ public void testBasic4() {
+ CodeGen code = new CodeGen("return arg<<1;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return (shli,arg);", code._stop.toString());
+ }
+
+ @Test
+ public void testBasic5() {
+ CodeGen code = new CodeGen("return arg >> 1;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return (sari,arg);", code._stop.toString());
+ }
+
+ @Test
+ public void testBasic6() {
+ CodeGen code = new CodeGen("return arg >>> 1;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return (shri,arg);", code._stop.toString());
+ }
+
+ @Test
+ public void testBasic7() {
+ CodeGen code = new CodeGen("return arg / 2;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return (div,arg,2);", code._stop.toString());
+ }
+
+ @Test
+ public void testBasic8() {
+ CodeGen code = new CodeGen("return arg * 6;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return (muli,arg);", code._stop.toString());
+ }
+
+ @Test
+ public void testBasic9() {
+ CodeGen code = new CodeGen("return arg & 2;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return (andi,arg);", code._stop.toString());
+ }
+
+ @Test
+ public void testBasic10() {
+ CodeGen code = new CodeGen("return arg | 2;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return (ori,arg);", code._stop.toString());
+ }
+
+ @Test
+ public void testBasic11() {
+ CodeGen code = new CodeGen("return arg ^ 2;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return (xori,arg);", code._stop.toString());
+ }
+
+ @Test
+ public void testBasic12() {
+ CodeGen code = new CodeGen("return arg + 2.0;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return (addf,(i2f8,arg),2.0f);", code._stop.toString());
+ }
+
+ @Test
+ public void testBasic13() {
+ CodeGen code = new CodeGen("return arg - 2.0;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return (subf,(i2f8,arg),2.0f);", code._stop.toString());
+ }
+
+ @Test
+ public void testBasic14() {
+ CodeGen code = new CodeGen("return arg * 2.0;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return (mulf,(i2f8,arg),2.0f);", code._stop.toString());
+ }
+
+ @Test
+ public void testBasic15() {
+ CodeGen code = new CodeGen("return arg / 2.0;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return (divf,(i2f8,arg),2.0f);", code._stop.toString());
+ }
+
+ @Test
+ public void testBasic16() {
+ CodeGen code = new CodeGen(
+"""
+int arg1 = arg + 1;
+return arg1 / arg;""");
+ code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return (div,(inc,arg),arg);", code._stop.toString());
+ }
+
+ @Test
+ public void testBasic17() {
+ CodeGen code = new CodeGen(
+"""
+int arg1 = arg + 1;
+return arg1 * arg;
+""");
+ code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return (mul,(inc,arg),arg);", code._stop.toString());
+ }
+
+ @Test
+ public void testBasic18() {
+ CodeGen code = new CodeGen(
+ """
+ int arg1 = arg + 1;
+ return arg1 << arg;
+ """);
+ code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return (shl,(inc,arg),arg);", code._stop.toString());
+ }
+
+ @Test
+ public void testBasic19() {
+ CodeGen code = new CodeGen(
+ """
+ int arg1 = arg + 1;
+ return arg1 >> arg;
+ """);
+ code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return (sar,(inc,arg),arg);", code._stop.toString());
+ }
+
+ @Test
+ public void testBasic20() {
+ CodeGen code = new CodeGen(
+ """
+ int arg1 = arg + 1;
+ return arg1 >>> arg;
+ """);
+ code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return (shr,(inc,arg),arg);", code._stop.toString());
+ }
+
+ @Test
+ public void testToFloat() {
+ CodeGen code = new CodeGen("""
+ int a = arg;
+ return a + 2.0;
+ """
+ ).parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return (addf,(i2f8,arg),2.0f);", code._stop.toString());
+ }
+
+ @Test
+ public void testIfStmt() {
+ CodeGen code = new CodeGen(
+"""
+int a = 1;
+if (arg == 1)
+ a = arg+2;
+else {
+ a = arg-3;
+}
+return a;""");
+ code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return Phi(Region,(addi,arg),(addi,arg));", code.print());
+ }
+
+ @Test
+ public void testIfMerge2() {
+ CodeGen code = new CodeGen(
+"""
+int a=arg+1;
+int b=arg+2;
+if( arg==1 )
+ b=b+a;
+else
+ a=b+1;
+return a+b;""");
+ code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return (add,(add,Phi(Region,(shli,arg),arg),arg),Phi(Region,4,5));", code.print());
+ }
+
+ @Test
+ public void testLoop() {
+ CodeGen code = new CodeGen(
+"""
+int sum=0;
+for( int i=0; i<arg; i++ )
+ sum += i;
+return sum;""");
+ code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return Phi(Loop,0,(add,Phi_sum,Phi(Loop,0,(inc,Phi_i))));", code.print());
+ }
+
+ @Test
+ public void testAlloc1() {
+ CodeGen code = new CodeGen(
+"""
+struct S { int a; !S? !c; };
+return new S;""");
+ code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return S;", code.print());
+ }
+
+ @Test
+ public void testLea1() {
+ CodeGen code = new CodeGen("int x = arg/3; return arg+x+7;");
+ code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return (lea,arg,(div,arg,3));", code.print());
+ }
+
+ @Test
+ public void testLea2() {
+ CodeGen code = new CodeGen("int x = arg/3; return arg+x*4+7;");
+ code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return (lea,arg,(div,arg,3));", code.print());
+ }
+
+ @Test
+ public void testLea3() {
+ CodeGen code = new CodeGen("int x = arg/3; return x*4+arg;");
+ code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return (lea,arg,(div,arg,3));", code.print());
+ }
+
+ @Test
+ public void testAlloc2() {
+ CodeGen code = new CodeGen("int[] !xs = new int[3]; xs[arg]=1; return xs[arg&1];");
+ code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return .[];", code.print());
+ }
+
+ @Test
+ public void testAlloc3() {
+ CodeGen code = new CodeGen("int[] !xs = new int[3]; xs[arg]=1; return xs[arg&1]+3;");
+ code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return .[];", code.print());
+ }
+
+ @Test
+ public void testArray1() {
+ CodeGen code = new CodeGen(
+"""
+int[] !ary = new int[arg];
+// Fill [0,1,2,3,4,...]
+for( int i=0; i<ary#; i++ )
+ ary[i] = i;
+// Fill [0,1,3,6,10,...]
+for( int i=0; i<ary#-1; i++ )
+ ary[i+1] += ary[i];
+return ary[1] * 1000 + ary[3]; // 1 * 1000 + 6
+""");
+ code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return .[];", code.print());
+ }
+
+ @Test
+ public void testArray2() {
+ CodeGen code = new CodeGen(
+"""
+flt[] !A = new flt[arg], !B = new flt[arg];
+// Fill [0,1,2,3,4,...]
+for( int i=0; i<A#; i++ )
+ A[i] = i;
+for( int i=0; i<A#; i++ )
+ B[i] += A[i];
+""");
+ code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return 0;", code.print());
+ }
+
+ @Test
+ public void testArray3() {
+ CodeGen code = new CodeGen(
+"""
+byte[] !A = new byte[arg];
+for( int i=0; i<A#; i++ )
+ A[i]++;
+return A[1];
+""");
+ code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return .[];", code.print());
+ }
+
+ @Test
+ public void testNewton() {
+ CodeGen code = new CodeGen(
+"""
+// Newtons approximation to the square root
+val sqrt = { flt x ->
+ flt guess = x;
+ while( 1 ) {
+ flt next = (x/guess + guess)/2;
+ if( next == guess ) return guess;
+ guess = next;
+ }
+};
+flt farg = arg;
+return sqrt(farg);
+""");
+ code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return Phi(Loop,(i2f8,arg),(divf,(addf,(divf,i2f8,Phi_guess),Phi_guess),2.0f));", code.print());
+ };
+
+
+ @Test
+ public void sieveOfEratosthenes() {
+ CodeGen code = new CodeGen(
+"""
+var ary = new bool[arg], primes = new int[arg];
+var nprimes=0, p=0;
+// Find primes while p^2 < arg
+for( p=2; p*p < arg; p++ ) {
+ // skip marked non-primes
+ while( ary[p] ) p++;
+ // p is now a prime
+ primes[nprimes++] = p;
+ // Mark out the rest non-primes
+ for( int i = p + p; i < ary#; i += p )
+ ary[i] = true;
+}
+// Now just collect the remaining primes, no more marking
+for( ; p < arg; p++ )
+ if( !ary[p] )
+ primes[nprimes++] = p;
+// Copy/shrink the result array
+var !rez = new int[nprimes];
+for( int j=0; j<nprimes; j++ )
+ rez[j] = primes[j];
+return rez;
+""");
+ code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return [int];", code.print());
+ //assertEquals("int[ 2,3,5,7,11,13,17,19]",Eval2.eval(code, 20));
+ }
+
+
+ @Test
+ public void testFcn1() {
+ CodeGen code = new CodeGen(
+"""
+val fcn = arg ? { int x -> x*x; } : { int x -> x+x; };
+return fcn(2)*10 + fcn(3);
+""");
+ code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("Stop[ return (add,Phi(Region,{ int -> int #1},{ int -> int #2})( 3),(muli,Phi_( 2))); return (mul,Parm_x($fun,int,3,2),x); return (shli,Parm_x($fun,int,3,2)); ]", code.print());
+ }
+
+ @Test
+ public void testFcn2() {
+ CodeGen code = new CodeGen(
+"""
+val sq = { int x -> x*x; };
+return sq(arg) + sq(3);
+""");
+ code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("Stop[ return (add,sq(),sq()); return (mul,Parm_x(sq,int,3,arg),x); ]", code.print());
+ }
+
+ @Test public void testSelectedMemory() {
+ for( String cpu : new String[]{"x86_64_v2","riscv"} )
+ for( String src : new String[]{Chapter10Test.NESTED_MEMORY,Chapter16Test.CONSTRUCTOR_MEMORY,
+ Chapter18Test.CALL_MEMORY,Chapter18Test.RECURSIVE_MEMORY} ) {
+ var code = new CodeGen(src).parse().opto().typeCheck();
+ var aliases = memoryPhis(code._stop);
+ code.instSelect(cpu,"SystemV");
+ assertEquals(aliases,memoryPhis(code._stop));
+ code.GCM().localSched();
+ code._stop.walk(n -> {
+ if( !(n instanceof CFGNode) && !(n instanceof ProjNode) )
+ assertTrue(n.in(0) instanceof CFGNode);
+ if( n instanceof NewNode nn ) {
+ assertEquals(2,((TypeTuple)nn._type)._types.length);
+ assertTrue(nn.mem() instanceof MemMergeNode);
+ assertNull(nn.mem().in(1)); // Partial allocation input.
+ MachNode mach = (MachNode)nn;
+ assertNull(mach.regmap(1));
+ assertNotNull(mach.regmap(2));
+ assertNotNull(mach.outregmap(0));
+ assertNull(mach.outregmap(1));
+ }
+ if( n instanceof CallNode call ) {
+ assertEquals(4,call.nIns()); // ctrl, memory, two args; direct target is embedded.
+ }
+ return null;
+ });
+ }
+ }
+
+ private static java.util.ArrayList<String> memoryPhis(Node stop) {
+ var phis = new java.util.ArrayList<String>();
+ // Count only definitions reachable from Stop, as instruction selection does.
+ // A dead Phi cycle can still appear through the reverse use edges.
+ var seen = new java.util.BitSet();
+ var work = new java.util.ArrayList<Node>();
+ work.add(stop);
+ for( int i=0; i<work.size(); i++ ) {
+ Node n = work.get(i);
+ if( n==null || seen.get(n._nid) ) continue;
+ seen.set(n._nid);
+ for( Node def : n._inputs ) work.add(def);
+ if( n instanceof MemPhiNode phi ) phis.add("alias:"+phi._alias);
+ if( n instanceof BulkMemPhiNode phi ) phis.add("bulk:"+phi._aliases);
+ assertFalse(n instanceof PhiNode && n.isMem() &&
+ !(n instanceof MemPhiNode) && !(n instanceof BulkMemPhiNode) && !(n instanceof ParmNode));
+ }
+ java.util.Collections.sort(phis);
+ return phis;
+ }
+
+ @Test public void testFoldedReadBeforeWrites() {
+ var code = new CodeGen("""
+ struct S { int x; };
+ !S !a = new S { x=11; }; !S !b = new S { x=22; };
+ !S !p = a; if( arg ) p = b;
+ int before = p.x + arg;
+ a.x=33; b.x=44;
+ return before;
+ """).parse().opto().typeCheck().instSelect("x86_64_v2","SystemV").GCM().localSched();
+ var read = (MemOpNode)code._stop.walk(n ->
+ n instanceof com.seaofnodes.simple.node.cpus.x86_64_v2.AddMemX86 ? n : null);
+ assertNotNull(read);
+ int writes = 0;
+ for( Node use : read.antiDeps() )
+ if( use instanceof MemOpNode st && st.isMem() ) {
+ assertSame(read.cfg0(),st.cfg0());
+ assertTrue(st._inputs.find(read)>=0);
+ assertTrue(read.cfg0()._outputs.find(read)<st.cfg0()._outputs.find(st));
+ assertNull(((MachNode)st).regmap(st._inputs.find(read)));
+ writes++;
+ }
+ assertEquals(1,writes); // The next Store follows this first clobber through memory.
+ }
+}
diff --git a/src/test/java/com/seaofnodes/simple/Eval2.java b/src/test/java/com/seaofnodes/simple/Eval2.java
index 3f337e86..46251cef 100644
--- a/src/test/java/com/seaofnodes/simple/Eval2.java
+++ b/src/test/java/com/seaofnodes/simple/Eval2.java
@@ -179,7 +179,7 @@ public abstract class Eval2 {
int path = r._inputs.find( prior );
// Parameters read from prior frame, Phis from local frame
Frame frame = (r instanceof FunNode ? F._prior : F);
- boolean isMain = r instanceof FunNode fun && fun.sig().isa(TypeFunPtr.MAIN);
+ boolean isMain = r instanceof FunNode fun && fun.sig().isa(CodeGen.CODE._main);
// Parallel assign Phis. First parallel read and cache
int i;
@@ -349,7 +349,7 @@ public abstract class Eval2 {
int num = type._fields.length;
Object[] ptr = new Object[num];
for( int i=0; i<num; i++ )
- ptr[i] = val(alloc.in(3+i));
+ ptr[i] = con(type._fields[i]._type.makeZero());
return ptr;
}
@@ -365,6 +365,8 @@ public abstract class Eval2 {
}
private static Object store( StoreNode st ) {
+ if( st._name.equals("#") )
+ return "$mem"; // Eval stores the length in the java object[], no need to set it now
TypeMemPtr tmp = (TypeMemPtr)st.ptr()._type;
Object[] fs = (Object[])val(st.ptr());
int idx = tmp._obj.isAry()
diff --git a/src/test/java/com/seaofnodes/simple/FuzzerWrap.java b/src/test/java/com/seaofnodes/simple/FuzzerWrap.java
index 143828e6..b022bec9 100644
--- a/src/test/java/com/seaofnodes/simple/FuzzerWrap.java
+++ b/src/test/java/com/seaofnodes/simple/FuzzerWrap.java
@@ -7,10 +7,9 @@ import org.junit.Test;
import static org.junit.Assert.assertTrue;
/**
- * Normal tests run only this chapter's fixed regression seeds. Open failures
- * and exploratory fuzzing are opt-in: run their methods directly in the IDE,
- * or temporarily remove the relevant Ignore annotation when investigating.
- * Seeds belong to this chapter's generator; do not copy them between chapters.
+ * Normal tests run chapter-local regression seeds. Open failures and exploratory
+ * fuzzing are opt-in (run directly in the IDE or temporarily remove Ignore).
+ * Seeds depend on this chapter's generator; do not copy them between chapters.
*/
public class FuzzerWrap {
diff --git a/src/test/java/com/seaofnodes/simple/Main.java b/src/test/java/com/seaofnodes/simple/Main.java
new file mode 100644
index 00000000..523bd306
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/Main.java
@@ -0,0 +1,17 @@
+package com.seaofnodes.simple;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+public class Main {
+ // Compile and run a simple program
+ public static void main( String[] args ) throws Exception {
+ // First arg is file, 2nd+ args are program args
+ String src = Files.readString(Path.of(args[0]));
+ int arg = Integer.valueOf(args[1]);
+ CodeGen code = new CodeGen(src);
+ code.parse().opto().typeCheck().GCM().localSched();
+ System.out.println(code._stop);
+ System.out.println(Eval2.eval(code,arg,100000));
+ }
+}
diff --git a/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java b/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java
index 7dbc551e..cb98971b 100644
--- a/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java
+++ b/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java
@@ -199,14 +199,17 @@ public class Evaluator {
assert elem instanceof TypeMemPtr;
}
// Length value
- body[0] = vall(alloc.in(3));
+ body[0] = n;
} else {
body = new Object[num = type._fields.length];
- for (int i=0; i<num; i++)
- body[i] = val(alloc.in(3+i));
+ for( int i=0; i<num; i++ )
+ body[i] = switch( alloc._ptr._obj._fields[i]._type ) {
+ case TypeInteger ti -> 0L;
+ case TypeFloat tf -> 0D;
+ default -> null;
+ };
}
return new Object[]{new Obj(type,body),null};
-
}
private Object load(LoadNode load) {
@@ -341,7 +344,7 @@ public class Evaluator {
assert block != null;
for (; i < block.nodes().length; i++) {
if (!(block.nodes()[i] instanceof PhiNode phi)) break;
- var val = region instanceof FunNode fun && "main".equals(fun.sig()._name) && ((ParmNode)phi)._idx==2 && exit==1
+ var val = region instanceof FunNode fun && "main".equals(fun._name) && ((ParmNode)phi)._idx==2 && exit==1
? parameter
: val(phi.in(exit));
phiCache.add(val);
diff --git a/src/test/java/com/seaofnodes/simple/evaluator/Scheduler.java b/src/test/java/com/seaofnodes/simple/evaluator/Scheduler.java
index 52d8f3e3..4bfa7628 100644
--- a/src/test/java/com/seaofnodes/simple/evaluator/Scheduler.java
+++ b/src/test/java/com/seaofnodes/simple/evaluator/Scheduler.java
@@ -508,7 +508,7 @@ public class Scheduler {
private static CFGNode findSingleCFGOut(Node node) {
if (node instanceof StartNode) {
for(var n:node._outputs)
- if (n instanceof FunNode fun && "main".equals(fun.sig()._name) )
+ if (n instanceof FunNode fun && "main".equals(fun._name) )
return fun;
return null;
}
diff --git a/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java b/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
index a5634e82..851380cf 100644
--- a/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
+++ b/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
@@ -126,17 +126,16 @@ public class Fuzzer {
var code = new CodeGen(sb.toString());
code.parse();
- int parse_peeps= Node.ITER_CNT;
- int parse_nops = Node.ITER_NOP_CNT;
+ int nids = code.UID();
+ if( nids <= max_nids ) return max_nids; // Smaller, looking for trends as we grow
+ int parse_peeps= code._iter_cnt;
+ int parse_nops = code._iter_nop_cnt;
double parse_nop_ratio = (double)parse_nops/parse_peeps;
- int nids = Node.UID();
- if( nids <= max_nids ) return max_nids;
-
double parse_peeps_per_node = (double)parse_peeps/nids;
code.opto();
- int iter_peeps= Node.ITER_CNT;
- int iter_nops = Node.ITER_NOP_CNT;
+ int iter_peeps= code._iter_cnt;
+ int iter_nops = code._iter_nop_cnt;
double iter_nop_ratio = (double)iter_nops/iter_peeps;
double iter_peeps_per_node = (double)iter_peeps/nids;
@@ -173,7 +172,7 @@ public class Fuzzer {
return exceptions.isEmpty();
}
- /** Run a fixed seed without reducing or swallowing failures. */
+ /** Run a fixed chapter-local seed without reducing or swallowing failures. */
public void fuzzPeepsRegression(long seed) {
var rand = new Random(seed);
var sb = new StringBuilder();
diff --git a/src/test/java/com/seaofnodes/simple/fuzzer/FuzzerUtils.java b/src/test/java/com/seaofnodes/simple/fuzzer/FuzzerUtils.java
index 4509c6fc..5c89a485 100644
--- a/src/test/java/com/seaofnodes/simple/fuzzer/FuzzerUtils.java
+++ b/src/test/java/com/seaofnodes/simple/fuzzer/FuzzerUtils.java
@@ -4,6 +4,7 @@ import com.seaofnodes.simple.CodeGen;
import com.seaofnodes.simple.IterPeeps;
import com.seaofnodes.simple.node.Node;
import com.seaofnodes.simple.node.StopNode;
+import com.seaofnodes.simple.type.TypeInteger;
import java.io.OutputStream;
import java.io.PrintStream;
import java.lang.invoke.MethodHandle;
@@ -47,33 +48,10 @@ class FuzzerUtils {
}
/**
- * Copy of Node.WVISIST
- * Used to clear as exceptions might happen in the walk and left this bitset not cleared
- */
- private static final BitSet NodeWalkVisit;
- private static final java.util.Random WorkRandom;
-
- /**
- * Write access to Iterate.MID_ASSERT
- * Exception could happen mid assert and left this at true. Used to reset to false prior to parsing.
- */
- private static final MethodHandle set_MID_ASSERT;
-
- /**
* Some problems might trigger debug output. Suppress this output with a null print stream during fuzzing.
*/
public static final PrintStream NULL_PRINT_STREAM = new PrintStream(OutputStream.nullOutputStream());
- static {
- try {
- NodeWalkVisit = getFieldValue(Node.class, "WVISIT");
- WorkRandom = getFieldValue(getFieldValue(IterPeeps.class, "WORK"), "_R");
- set_MID_ASSERT = MethodHandles.lookup().unreflectSetter(getField( IterPeeps.class, "MID_ASSERT"));
- } catch (Exception e) {
- throw new ExceptionInInitializerError(e);
- }
- }
-
/**
* Check that the messages are similar.
*/
@@ -122,17 +100,9 @@ class FuzzerUtils {
try {
System.setErr(NULL_PRINT_STREAM);
System.setOut(NULL_PRINT_STREAM);
- try {
- set_MID_ASSERT.invokeExact(false);
- } catch (Throwable e) {
- throw rethrow(e);
- }
- var code = new CodeGen(script);
- code.parse();
- WorkRandom.setSeed(workListSeed);
- return code.opto().typeCheck().GCM().localSched();
+ var code = new CodeGen(script, TypeInteger.BOT, workListSeed);
+ return code.parse().opto().typeCheck().GCM().localSched();
} finally {
- NodeWalkVisit.clear();
System.setErr(err);
System.setOut(out);
}
diff --git a/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java b/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java
index d061a656..167a5b24 100644
--- a/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java
+++ b/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java
@@ -3,12 +3,13 @@ package com.seaofnodes.simple.fuzzer;
import com.seaofnodes.simple.Parser;
import com.seaofnodes.simple.node.ScopeNode;
import com.seaofnodes.simple.type.TypeInteger;
-
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.function.Predicate;
+import static com.seaofnodes.simple.Parser.KEYWORDS;
+
/**
* Generate a pseudo random script.
@@ -34,18 +35,6 @@ public class ScriptGenerator {
* Valid characters for identifiers. The last 10 are not valid for the first character.
*/
private static final String VAR_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789";
- /**
- * List of keywords not valid for identifiers.
- */
- private static final HashSet<String> KEYWORDS;
-
- static {
- try {
- KEYWORDS = FuzzerUtils.getFieldValue(new Parser(""), "KEYWORDS");
- } catch (Throwable e) {
- throw new ExceptionInInitializerError(e);
- }
- }
/**
* Flag for a statement that it does not pass on control flow to the next statement.
@@ -108,7 +97,6 @@ public class ScriptGenerator {
private static final List<TypeInt> INTTYPES = new ArrayList<>();
static {
- new Parser("", TypeInteger.BOT);
for( var e : Parser.defaultTypes().entrySet() ) {
if( e.getValue() instanceof TypeInteger ) {
INTTYPES.add(new TypeInt(e.getKey()));
diff --git a/src/test/java/com/seaofnodes/simple/progs/fcn.smp b/src/test/java/com/seaofnodes/simple/progs/fcn.smp
new file mode 100644
index 00000000..43d398b3
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/progs/fcn.smp
@@ -0,0 +1,15 @@
+
+struct Person {
+ int age;
+};
+
+val fcn = { !Person?[] ps, int x ->
+ val tmp = ps[x];
+ if( ps[x] )
+ ps[x].age++;
+};
+
+!Person?[] !ps = new !Person?[2];
+ps[0] = new Person;
+ps[1] = new Person;
+fcn(ps,1);
diff --git a/src/test/java/com/seaofnodes/simple/progs/fcn2.smp b/src/test/java/com/seaofnodes/simple/progs/fcn2.smp
new file mode 100644
index 00000000..049132d9
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/progs/fcn2.smp
@@ -0,0 +1,5 @@
+
+val fcn = (arg&1)
+ ? { int x -> x*x; }
+ : { int x -> x+x; };
+fcn(arg);
diff --git a/src/test/java/com/seaofnodes/simple/progs/fcn3.smp b/src/test/java/com/seaofnodes/simple/progs/fcn3.smp
new file mode 100644
index 00000000..55b74455
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/progs/fcn3.smp
@@ -0,0 +1,42 @@
+
+//val fact = { int n -> n <= 1 ? 1 : fact(n-1)*n; }
+struct LL { !LL? !next; int payload; };
+
+
+struct String {
+
+ val _hashCodeString = { ->
+ int hash=0;
+ for( int i=0; i< cs#; i++ )
+ hash = hash*31 + cs[i];
+ if( !hash ) hash = 123456789;
+ return hash;
+ };
+
+ val equals = { !String s ->
+ if( self == s ) return true;
+ if( cs# != s.cs# ) return false;
+ for( int i=0; i< #; i++ )
+ if( [i] != s.cs[i] )
+ return false;
+ return true;
+ };
+
+ int _hashCode = hash();
+ u8[] cs;
+};
+
+
+
+val hashCode= { !String self ->
+ int hash = self._hashCode
+ ? self._hashCode
+ : self._hashCode = _hashCodeString(self);
+ return hash;
+};
+
+
+String s = new String { cs = new u8[17]; };
+s.cs[0] = 67; // C
+s.cs[1] = 108; // l
+hashCode(s);
diff --git a/src/test/java/com/seaofnodes/simple/progs/sieve.smp b/src/test/java/com/seaofnodes/simple/progs/sieve.smp
new file mode 100644
index 00000000..0788e6c5
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/progs/sieve.smp
@@ -0,0 +1,32 @@
+
+int[] !ary = new int[arg], !primes = new int[arg];
+int nprimes = 0, p=2;
+// Find primes while p^2 < arg
+while( p*p < arg ) {
+ // skip marked non-primes
+ while( ary[p]==1 ) p = p + 1;
+ // p is now a prime
+ primes[nprimes] = p; nprimes = nprimes + 1;
+ // Mark out the rest non-primes
+ int i = p + p;
+ while( i < ary# ) {
+ ary[i] = 1;
+ i = i + p;
+ }
+ p = p + 1;
+}
+// Now just collect the remaining primes, no more marking
+while( p < arg ) {
+ if( ary[p] == 0 ) {
+ primes[nprimes] = p; nprimes = nprimes + 1;
+ }
+ p = p + 1;
+}
+// Copy/shrink the result array
+int[] !rez = new int[nprimes];
+int j = 0;
+while( j < nprimes ) {
+ rez[j] = primes[j];
+ j = j + 1;
+}
+return rez;