From 4a106770d9ec216f00fdf11258887af3855ae42f Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Mon, 30 Dec 2024 23:48:50 +0000
Subject: [PATCH 001/119] Sparse Conditional Constant Propagation

---
 .../ezlang/compiler/BasicBlock.java           |   2 +
 .../ezlang/compiler/CompiledFunction.java     |   1 +
 .../ezlang/compiler/Instruction.java          |   1 +
 .../ezlang/compiler/Optimizer.java            |   1 +
 .../ezlang/compiler/SSAEdges.java             |  80 +++
 .../SparseConditionalConstantPropagation.java | 470 ++++++++++++++++++
 .../ezlang/compiler/TestSCCP.java             |  67 +++
 .../ezlang/interpreter/TestInterpreter.java   |  15 +
 8 files changed, 637 insertions(+)
 create mode 100644 optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SSAEdges.java
 create mode 100644 optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SparseConditionalConstantPropagation.java
 create mode 100644 optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSCCP.java

diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/BasicBlock.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/BasicBlock.java
index a7f9cf9..5401c04 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/BasicBlock.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/BasicBlock.java
@@ -96,6 +96,7 @@ public BasicBlock(int bid, BasicBlock... preds) {
     }
     public void add(Instruction instruction) {
         instructions.add(instruction);
+        instruction.block = this;
     }
     public void addSuccessor(BasicBlock successor) {
         successors.add(successor);
@@ -120,6 +121,7 @@ public void insertPhiFor(Register var) {
             inputs.add(var);
         Instruction.Phi phi = new Instruction.Phi(var, inputs);
         instructions.add(0, phi);
+        phi.block = this;
     }
     public List<Instruction.Phi> phis() {
         List<Instruction.Phi> list = new ArrayList<>();
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
index 3e02457..ff005bc 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
@@ -127,6 +127,7 @@ else if (virtualStack.size() > 1)
 
     public void code(Instruction instruction) {
         currentBlock.add(instruction);
+        instruction.block = currentBlock;
     }
 
     private void compileStatement(AST.Stmt statement) {
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java
index cd6166a..9a98f37 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java
@@ -29,6 +29,7 @@ public abstract class Instruction {
     public final int opcode;
     public Operand.RegisterOperand def;
     public Operand[] uses;
+    public BasicBlock block;
 
     public Instruction(int opcode, Operand... uses) {
         this.opcode = opcode;
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Optimizer.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Optimizer.java
index 73acce5..f886a4a 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Optimizer.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Optimizer.java
@@ -4,6 +4,7 @@ public class Optimizer {
 
     public void optimize(CompiledFunction function) {
         new EnterSSA(function);
+        new SparseConditionalConstantPropagation().constantPropagation(function);
         new ExitSSA(function);
         new ChaitinGraphColoringRegisterAllocator().assignRegisters(function, 64);
     }
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SSAEdges.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SSAEdges.java
new file mode 100644
index 0000000..6026f8a
--- /dev/null
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SSAEdges.java
@@ -0,0 +1,80 @@
+package com.compilerprogramming.ezlang.compiler;
+
+import com.compilerprogramming.ezlang.exceptions.CompilerException;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * SSA Edges are connections from the unique point where a variable is
+ * given a value to a use of that variable. SSA edges are essentially
+ * def-use chains in the SSA program.
+ */
+public class SSAEdges {
+
+    public static final class SSADef {
+        public final Register register;
+        public final Instruction instruction;
+        public final List<Instruction> useList;
+
+        public SSADef(Register register, Instruction instruction) {
+            this.register = register;
+            this.instruction = instruction;
+            this.useList = new ArrayList<>();
+        }
+    }
+
+    public static Map<Integer, SSADef> buildDefUseChains(CompiledFunction function) {
+
+        if (!function.isSSA) throw new CompilerException("Function must be in SSA form");
+
+        Map<Integer, SSADef> defUseChains = new HashMap<>();
+        recordDefs(function, defUseChains);
+        recordUses(function, defUseChains);
+
+        return defUseChains;
+    }
+
+    private static void recordDefs(CompiledFunction function, Map<Integer, SSADef> defUseChains) {
+        for (BasicBlock block : function.getBlocks()) {
+            for (Instruction instruction : block.instructions) {
+                if (instruction instanceof Instruction.Phi phi) {
+                    recordDef(defUseChains, phi.value(), instruction);
+                }
+                else if (instruction.definesVar()) {
+                    recordDef(defUseChains, instruction.def(), instruction);
+                }
+            }
+        }
+    }
+
+    private static void recordUses(CompiledFunction function, Map<Integer, SSADef> defUseChains) {
+        for (BasicBlock block : function.getBlocks()) {
+            for (Instruction instruction : block.instructions) {
+                if (instruction instanceof Instruction.Phi phi) {
+                    recordUses(defUseChains, phi.inputs, block, instruction);
+                }
+                else {
+                    List<Register> uses = instruction.uses();
+                    if (!uses.isEmpty())
+                        recordUses(defUseChains, uses.toArray(new Register[uses.size()]), block, instruction);
+                }
+            }
+        }
+    }
+
+    private static void recordUses(Map<Integer, SSADef> defUseChains, Register[] inputs, BasicBlock block, Instruction instruction) {
+        for (Register register : inputs) {
+            SSADef def = defUseChains.get(register.id);
+            def.useList.add(instruction);
+        }
+    }
+
+    private static void recordDef(Map<Integer, SSADef> defUseChains, Register value, Instruction instruction) {
+        if (defUseChains.containsKey(value.id))
+            throw new CompilerException("Register already defined, invalid multiple definition in SSA");
+        defUseChains.put(value.id, new SSADef(value, instruction));
+    }
+}
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SparseConditionalConstantPropagation.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SparseConditionalConstantPropagation.java
new file mode 100644
index 0000000..f56154e
--- /dev/null
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SparseConditionalConstantPropagation.java
@@ -0,0 +1,470 @@
+package com.compilerprogramming.ezlang.compiler;
+
+import com.compilerprogramming.ezlang.types.Type;
+
+import java.util.*;
+
+/**
+ * Implementation of Sparse Conditional Constant Propagation based on descriptions
+ * in:
+ *
+ * <ol>
+ *     <li>Constant Propagation with Conditional Branches. Wegman and Zadeck.</li>
+ *     <li>Modern Compiler Implementation in C</li>
+ *     <li></li>
+ * </ol>
+ *
+ *
+ */
+public class SparseConditionalConstantPropagation {
+
+    /**
+     * Contains a lattice for all possible definitions
+     */
+    ValueLattice valueLattice;
+    /**
+     * Executable status for each flow edge
+     */
+    Map<FlowEdge, Boolean> flowEdges;
+    WorkList<Instruction> instructionWorkList;
+    WorkList<BasicBlock> flowWorklist;
+    BitSet visited = new BitSet();
+    /**
+     * Def use chains for each register
+     * Called SSAEdge in the original paper.
+     */
+    Map<Integer, SSAEdges.SSADef> ssaEdges;
+    CompiledFunction function;
+
+    public SparseConditionalConstantPropagation constantPropagation(CompiledFunction function) {
+        init(function);
+        while (!flowWorklist.isEmpty() || !instructionWorkList.isEmpty()) {
+            while (!instructionWorkList.isEmpty()) {
+                Instruction i = instructionWorkList.pop();
+                visitInstruction(i);
+            }
+            while (!flowWorklist.isEmpty()) {
+                BasicBlock b = flowWorklist.pop();
+                visitBlock(b);
+            }
+        }
+        return this;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("Flow edges:\n");
+        for (var edge : flowEdges.keySet()) {
+            if (flowEdges.get(edge)) {
+                sb.append(edge).append("=Executable").append("\n");
+            }
+        }
+        sb.append("Lattices:\n");
+        for (var id: valueLattice.valueLattice.keySet()) {
+            var register = function.registerPool.getReg(id);
+            sb.append(register.name()).append("=").append(valueLattice.get(register)).append("\n");
+        }
+        return sb.toString();
+    }
+
+    private void visitBlock(BasicBlock b) {
+        for (var phi : b.phis()) {
+            visitInstruction(phi);
+        }
+        if (!visited.get(b.bid)) {
+            visited.set(b.bid);
+            for (var i : b.instructions) {
+                visitInstruction(i);
+            }
+        }
+    }
+
+    private void visitInstruction(Instruction instruction) {
+        BasicBlock block = instruction.block;
+        if (evalInstruction(instruction)) {
+            if (instruction instanceof Instruction.ConditionalBranch || instruction instanceof Instruction.Jump) {
+                for (BasicBlock s : block.successors) {
+                    if (isEdgeExecutable(block, s)) {
+                        flowWorklist.push(block);   // Is this correct ?
+                        flowWorklist.push(s);
+                    }
+                }
+            } else if (instruction.definesVar() || instruction instanceof Instruction.Phi) {
+                var def = instruction instanceof Instruction.Phi phi ? phi.value() : instruction.def();
+                // Push all uses (instructions) of the def into the worklist
+                SSAEdges.SSADef ssaDef = ssaEdges.get(def.id);
+                if (ssaDef != null) {
+                    for (Instruction use : ssaDef.useList) {
+                        instructionWorkList.push(use);
+                    }
+                }
+            }
+        }
+    }
+
+    private void init(CompiledFunction function) {
+        this.function = function;
+        ssaEdges = SSAEdges.buildDefUseChains(function);
+        valueLattice = new ValueLattice();
+        flowEdges = new HashMap<>();
+        for (BasicBlock block : function.getBlocks()) {
+            for (BasicBlock s : block.successors) {
+                flowEdges.put(new FlowEdge(block, s), false);
+            }
+        }
+        instructionWorkList = new WorkList<>();
+        flowWorklist = new WorkList<>();
+        flowWorklist.push(function.entry);
+        visited = new BitSet();
+    }
+
+    static final byte V_UNDEFINED = 1;  // TOP
+    static final byte V_CONSTANT = 2;
+    static final byte V_VARYING = 3;    // BOTTOM
+
+    // Associated with each register
+    static final class LatticeElement {
+        private byte kind;
+        private long value;
+
+        public LatticeElement(byte kind, long value) {
+            this.kind = kind;
+            this.value = value;
+        }
+
+        boolean meet(long value) {
+            byte oldKind = this.kind;
+            long oldValue = this.value;
+            if (kind == V_UNDEFINED) {
+                kind = V_CONSTANT;
+                this.value = value;
+            } else if (kind == V_CONSTANT && this.value != value) {
+                kind = V_VARYING;
+            }
+            return kind != oldKind || value != oldValue;
+        }
+
+        boolean meet(LatticeElement other) {
+            byte oldKind = this.kind;
+            long oldValue = this.value;
+            if (kind == V_UNDEFINED) {
+                kind = other.kind;
+                value = other.value;
+            } else if (kind == V_CONSTANT) {
+                if (other.kind == V_CONSTANT) {
+                    if (other.value != value) {
+                        kind = V_VARYING;
+                    }
+                } else if (other.kind == V_VARYING) {
+                    kind = V_VARYING;
+                }
+            }
+            return kind != oldKind || value != oldValue;
+        }
+
+        public boolean setKind(byte kind) {
+            byte oldKind = this.kind;
+            this.kind = kind;
+            return oldKind != kind;
+        }
+        @Override
+        public String toString() {
+            if (kind == V_UNDEFINED) {
+                return "undefined";
+            }
+            else if (kind == V_CONSTANT) {
+                return String.valueOf(value);
+            }
+            return "varying";
+        }
+    }
+
+    // A CFG edge
+    static final class FlowEdge {
+        BasicBlock source;
+        BasicBlock target;
+
+        public FlowEdge(BasicBlock source, BasicBlock target) {
+            this.target = target;
+            this.source = source;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            FlowEdge that = (FlowEdge) o;
+            return (source.bid == that.source.bid) && (target.bid == that.target.bid);
+        }
+
+        @Override
+        public int hashCode() {
+            return source.bid + target.bid;
+        }
+
+        @Override
+        public String toString() {
+            return "L"+source.bid+"->L"+target.bid;
+        }
+    }
+
+    private boolean evalInstruction(Instruction instruction) {
+        BasicBlock block = instruction.block;
+        boolean changed = false;
+        switch (instruction) {
+            case Instruction.Ret retInst -> {
+                // TODO is this correct?
+            }
+            case Instruction.Move moveInst -> {
+                if (moveInst.to() instanceof Operand.RegisterOperand toReg) {
+                    var cell = valueLattice.get(toReg.reg);
+                    if (moveInst.from() instanceof Operand.RegisterOperand fromReg) {
+                        changed = cell.meet(valueLattice.get(fromReg.reg));
+                    } else if (moveInst.from() instanceof Operand.ConstantOperand constantOperand) {
+                        changed = cell.meet(constantOperand.value);
+                    } else throw new IllegalStateException();
+                } else throw new IllegalStateException();
+            }
+            case Instruction.Jump jumpInst -> {
+                changed = markEdgeExecutable(block, jumpInst.jumpTo);
+            }
+            case Instruction.ConditionalBranch cbrInst -> {
+                if (cbrInst.condition() instanceof Operand.RegisterOperand registerOperand) {
+                    var cell = valueLattice.get(registerOperand.reg);
+                    if (cell.kind == V_CONSTANT) {
+                        if (cell.value != 0) {
+                            changed = markEdgeExecutable(block, cbrInst.trueBlock);
+                        } else {
+                            changed = markEdgeExecutable(block, cbrInst.falseBlock);
+                        }
+                    } else if (cell.kind == V_VARYING) {
+                        boolean changed0 = markEdgeExecutable(block, cbrInst.trueBlock);
+                        boolean changed1 = markEdgeExecutable(block, cbrInst.falseBlock);
+                        changed = changed0 || changed1;
+                    }
+                } else if (cbrInst.condition() instanceof Operand.ConstantOperand constantOperand) {
+                    if (constantOperand.value != 0) {
+                        changed = markEdgeExecutable(block, cbrInst.trueBlock);
+                    } else {
+                        changed = markEdgeExecutable(block, cbrInst.falseBlock);
+                    }
+                } else throw new IllegalStateException();
+            }
+            case Instruction.Call callInst -> {
+                // Copy args to new frame
+                // Copy return value in expected location
+                if (!(callInst.callee.returnType instanceof Type.TypeVoid)) {
+                    var cell = valueLattice.get(callInst.returnOperand().reg);
+                    changed = cell.setKind(V_VARYING);
+                }
+            }
+            case Instruction.Unary unaryInst -> {
+                Operand.RegisterOperand unaryOperand = (Operand.RegisterOperand) unaryInst.operand();
+                var cell = valueLattice.get(unaryInst.result().reg);
+                var input = valueLattice.get(unaryOperand.reg);
+                if (input.kind == V_CONSTANT) {
+                    changed = cell.meet(unaryInst.unop.equals("-") ? -input.value : (input.value == 0 ? 1 : 0));
+                } else {
+                    changed = cell.meet(input);
+                }
+            }
+            case Instruction.Binary binaryInst -> {
+                var cell = valueLattice.get(binaryInst.result().reg);
+                LatticeElement left, right;
+                if (binaryInst.left() instanceof Operand.ConstantOperand constant)
+                    left = new LatticeElement(V_CONSTANT, constant.value);
+                else if (binaryInst.left() instanceof Operand.RegisterOperand registerOperand)
+                    left = valueLattice.get(registerOperand.reg);
+                else throw new IllegalStateException();
+                if (binaryInst.right() instanceof Operand.ConstantOperand constant)
+                    right = new LatticeElement(V_CONSTANT, constant.value);
+                else if (binaryInst.right() instanceof Operand.RegisterOperand registerOperand)
+                    right = valueLattice.get(registerOperand.reg);
+                else throw new IllegalStateException();
+                switch (binaryInst.binOp) {
+                    case "+":
+                    case "-":
+                    case "*":
+                    case "/":
+                    case "%":
+                        changed = evalArith(cell, left, right, binaryInst.binOp);
+                        break;
+                    case "==":
+                    case "!=":
+                    case "<":
+                    case ">":
+                    case "<=":
+                    case ">=":
+                        changed = evalLogical(cell, left, right, binaryInst.binOp);
+                        break;
+                    default:
+                        throw new IllegalStateException();
+                }
+            }
+            case Instruction.NewArray newArrayInst -> {
+                var cell = valueLattice.get(newArrayInst.destOperand().reg);
+                changed = cell.setKind(V_VARYING);
+            }
+            case Instruction.NewStruct newStructInst -> {
+                var cell = valueLattice.get(newStructInst.destOperand().reg);
+                changed = cell.setKind(V_VARYING);
+            }
+            case Instruction.AStoreAppend arrayAppendInst -> {
+            }
+            case Instruction.ArrayStore arrayStoreInst -> {
+            }
+            case Instruction.ArrayLoad arrayLoadInst -> {
+                var cell = valueLattice.get(arrayLoadInst.destOperand().reg);
+                changed = cell.setKind(V_VARYING);
+            }
+            case Instruction.SetField setFieldInst -> {
+            }
+            case Instruction.GetField getFieldInst -> {
+                var cell = valueLattice.get(getFieldInst.destOperand().reg);
+                changed = cell.setKind(V_VARYING);
+            }
+            case Instruction.ArgInstruction argInst -> {
+                var cell = valueLattice.get(argInst.def());
+                changed = cell.setKind(V_VARYING);
+            }
+            case Instruction.Phi phiInst -> {
+                changed = visitPhi(block, phiInst);
+            }
+            default -> throw new IllegalStateException("Unexpected value: " + instruction);
+        }
+        return changed;
+    }
+
+    private boolean visitPhi(BasicBlock block, Instruction.Phi phiInst) {
+        LatticeElement oldValue = valueLattice.get(phiInst.value());
+        LatticeElement newValue = new LatticeElement(V_UNDEFINED, 0);
+        for (int j = 0; j < block.predecessors.size(); j++) {
+            BasicBlock pred = block.predecessors.get(j);
+            if (isEdgeExecutable(pred, block)) {
+                LatticeElement varValue = valueLattice.get(phiInst.input(j));
+                newValue.meet(varValue);
+            }
+        }
+        return oldValue.meet(newValue);
+    }
+
+    private boolean isEdgeExecutable(BasicBlock source, BasicBlock target) {
+        return flowEdges.get(new FlowEdge(source, target));
+    }
+    private boolean markEdgeExecutable(BasicBlock source, BasicBlock target) {
+        var edge = new FlowEdge(source, target);
+        var oldValue = flowEdges.get(edge);
+        assert oldValue != null;
+        if (!oldValue) {
+            flowEdges.put(edge, true);
+            return true;
+        }
+        return false;
+    }
+
+    private static boolean evalLogical(LatticeElement cell, LatticeElement left, LatticeElement right, String binOp) {
+        boolean changed = false;
+        if (left.kind == V_CONSTANT && right.kind == V_CONSTANT) {
+            long leftValue = left.value;
+            long rightValue = right.value;
+            long result;
+            switch (binOp) {
+                case "==":
+                    result = leftValue == rightValue ? 1 : 0;
+                    break;
+                case "!=":
+                    result = leftValue != rightValue ? 1 : 0;
+                    break;
+                case "<":
+                    result = leftValue < rightValue ? 1 : 0;
+                    break;
+                case ">":
+                    result = leftValue > rightValue ? 1 : 0;
+                    break;
+                case "<=":
+                    result = leftValue <= rightValue ? 1 : 0;
+                    break;
+                case ">=":
+                    result = leftValue >= rightValue ? 1 : 0;
+                    break;
+                default:
+                    throw new IllegalStateException();
+            }
+            changed = cell.meet(result);
+        } else if (left.kind == V_VARYING || right.kind == V_VARYING) {
+            changed = cell.setKind(V_VARYING);
+        }
+        return changed;
+    }
+
+    private static boolean evalArith(LatticeElement cell, LatticeElement left, LatticeElement right, String binOp) {
+        boolean changed = false;
+        if (left.kind == V_CONSTANT && right.kind == V_CONSTANT) {
+            long leftValue = left.value;
+            long rightValue = right.value;
+            long result;
+            switch (binOp) {
+                case "+":
+                    result = leftValue + rightValue;
+                    break;
+                case "-":
+                    result = leftValue - rightValue;
+                    break;
+                case "/":
+                    result = leftValue / rightValue;
+                    break;
+                case "*":
+                    result = leftValue * rightValue;
+                    break;
+                case "%":
+                    result = leftValue % rightValue;
+                    break;
+                default:
+                    throw new IllegalStateException();
+            }
+            changed = cell.meet(result);
+        } else if (binOp.equals("*") && ((left.kind == V_CONSTANT && left.value == 0) || (right.kind == V_CONSTANT && right.value == 0))) {
+            var result = new LatticeElement(V_CONSTANT, 0);
+            changed = cell.meet(result);
+        } else if (left.kind == V_VARYING || right.kind == V_VARYING) {
+            changed = cell.setKind(V_VARYING);
+        }
+        return changed;
+    }
+
+    static final class ValueLattice {
+        Map<Integer, LatticeElement> valueLattice = new HashMap<>();
+
+        LatticeElement get(Register reg) {
+            var cell = valueLattice.get(reg.id);
+            if (cell == null) {
+                cell = new LatticeElement(V_UNDEFINED, 0);
+                valueLattice.put(reg.id, cell);
+            }
+            return cell;
+        }
+    }
+
+    static final class WorkList<E> {
+        Set<E> members = new HashSet<>();
+        List<E> list = new ArrayList<>();
+
+        void push(E element) {
+            if (members.add(element)) {
+                list.add(element);
+            }
+        }
+
+        E pop() {
+            if (list.isEmpty()) return null;
+            var x = list.removeFirst();
+            members.remove(x);
+            return x;
+        }
+
+        boolean isEmpty() {
+            return list.isEmpty();
+        }
+    }
+}
diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSCCP.java b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSCCP.java
new file mode 100644
index 0000000..ec9ec15
--- /dev/null
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSCCP.java
@@ -0,0 +1,67 @@
+package com.compilerprogramming.ezlang.compiler;
+
+import com.compilerprogramming.ezlang.types.Symbol;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.BitSet;
+
+public class TestSCCP {
+
+    String compileSrc(String src) {
+        var compiler = new Compiler();
+        var typeDict = compiler.compileSrc(src);
+        StringBuilder sb = new StringBuilder();
+        for (Symbol s : typeDict.bindings.values()) {
+            if (s instanceof Symbol.FunctionTypeSymbol f) {
+                var functionBuilder = (CompiledFunction) f.code();
+                new EnterSSA(functionBuilder);
+                BasicBlock.toStr(sb, functionBuilder.entry, new BitSet(), false);
+                sb.append(new SparseConditionalConstantPropagation().constantPropagation(functionBuilder).toString());
+            }
+        }
+        return sb.toString();
+    }
+
+    @Test
+    public void test1() {
+        String src = """
+func foo()->Int {
+    var i = 1
+    if (i == 0)
+        i = 2
+    else
+        i = 3
+    return i
+}            
+""";
+        String actual = compileSrc(src);
+        String expected = """
+L0:
+    i_0 = 1
+    %t1_0 = i_0==0
+    if %t1_0 goto L2 else goto L3
+L2:
+    i_2 = 2
+    goto  L4
+L4:
+    i_3 = phi(i_2, i_1)
+    ret i_3
+    goto  L1
+L1:
+L3:
+    i_1 = 3
+    goto  L4
+Flow edges:
+L0->L3=Executable
+L4->L1=Executable
+L3->L4=Executable
+Lattices:
+i_0=1
+%t1_0=0
+i_1=3
+i_3=3
+""";
+        Assert.assertEquals(expected, actual);
+    }
+}
diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java b/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java
index fd87020..5d49505 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java
@@ -173,4 +173,19 @@ func foo() {
         Assert.assertTrue(value instanceof Value.IntegerValue integerValue
                 && integerValue.value == 89);
     }
+
+    @Test
+    public void testFunction10() {
+        String src = """
+                func foo()->Int {
+                    if (1)
+                        return 2;
+                    return 3;
+                }
+                """;
+        var value = compileAndRun(src, "foo", true);
+        Assert.assertNotNull(value);
+        Assert.assertTrue(value instanceof Value.IntegerValue integerValue
+                && integerValue.value == 2);
+    }
 }

From 87604c2dd90528f2cb18eaae17077fc9e3dbf5a4 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Wed, 1 Jan 2025 16:30:42 +0000
Subject: [PATCH 002/119] #20 ssa transform useless phis

---
 .../ezlang/compiler/TestSSATransform.java     | 26 +++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
index 52dffb6..8b46834 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
@@ -814,4 +814,30 @@ func foo(x: Int) {
         System.out.println(result);
     }
 
+    // http://users.csc.calpoly.edu/~akeen/courses/csc431/handouts/references/ssa_example.pdf
+    @Test
+    public void testSSAExample() {
+        // TODO
+        String src = """
+func foo(x: Int, y: Int)->Int {
+   var sum: Int
+   
+   if (x >= y)
+      return 0
+      
+   sum = 0;
+   while (x < y) {
+      if (x / 2 * 2 == x) {
+         sum = sum + 1
+      }
+      x = x + 1
+   }
+   return sum
+}
+                """;
+        String result = compileSrc(src);
+        System.out.println(result);
+
+    }
+
 }
\ No newline at end of file

From e8390913c98bc7e8472fc05dd652a954e56897ef Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Wed, 1 Jan 2025 16:34:13 +0000
Subject: [PATCH 003/119] SCCP cleanups

---
 optvm/README.md                               |  2 +
 .../ezlang/compiler/SSAEdges.java             | 28 ++++----
 .../SparseConditionalConstantPropagation.java | 65 ++++++++++++++-----
 3 files changed, 67 insertions(+), 28 deletions(-)

diff --git a/optvm/README.md b/optvm/README.md
index bae0927..218b4f7 100644
--- a/optvm/README.md
+++ b/optvm/README.md
@@ -35,6 +35,8 @@ a physical machine. Therefore, all our optimization passes will work on the inst
   liveness data per basic block - mainly live-out. Note that the interference graph builder starts here and computes instruction level liveness as necessary.
 * [EnterSSA](src/main/java/com/compilerprogramming/ezlang/compiler/EnterSSA.java) - Transforms into SSA, using algorithm by Preston Briggs.
 * [ExitSSA](src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA.java) - Exits SSA form, using algorithm by Preston Briggs.
+* [SparseConditionalConstantPropagation](src/main/java/com/compilerprogramming/ezlang/compiler/SparseConditionalConstantPropagation.java) - Conditional Constant Propagation on SSA form (SCCP)
+* [SSAEdges](src/main/java/com/compilerprogramming/ezlang/compiler/SSAEdges.java) - SSAEdges are def-use chains used by SCCP algorithm 
 * [LoopFinder](src/main/java/com/compilerprogramming/ezlang/compiler/LoopFinder.java) - Discovers loops. (Not used yet)
 * [LoopNest](src/main/java/com/compilerprogramming/ezlang/compiler/LoopNest.java) - Representation of loop nesting. (Not used yet)
 * [InterferenceGraph](src/main/java/com/compilerprogramming/ezlang/compiler/InterferenceGraph.java) - Representation of an Interference Graph
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SSAEdges.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SSAEdges.java
index 6026f8a..262a3ba 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SSAEdges.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SSAEdges.java
@@ -15,29 +15,33 @@
 public class SSAEdges {
 
     public static final class SSADef {
-        public final Register register;
+        /**
+         * Instruction where the definition occurs
+         */
         public final Instruction instruction;
+        /**
+         * Instructions that use the definition
+         */
         public final List<Instruction> useList;
 
-        public SSADef(Register register, Instruction instruction) {
-            this.register = register;
+        public SSADef(Instruction instruction) {
             this.instruction = instruction;
             this.useList = new ArrayList<>();
         }
     }
 
-    public static Map<Integer, SSADef> buildDefUseChains(CompiledFunction function) {
+    public static Map<Register, SSADef> buildDefUseChains(CompiledFunction function) {
 
         if (!function.isSSA) throw new CompilerException("Function must be in SSA form");
 
-        Map<Integer, SSADef> defUseChains = new HashMap<>();
+        Map<Register, SSADef> defUseChains = new HashMap<>();
         recordDefs(function, defUseChains);
         recordUses(function, defUseChains);
 
         return defUseChains;
     }
 
-    private static void recordDefs(CompiledFunction function, Map<Integer, SSADef> defUseChains) {
+    private static void recordDefs(CompiledFunction function, Map<Register, SSADef> defUseChains) {
         for (BasicBlock block : function.getBlocks()) {
             for (Instruction instruction : block.instructions) {
                 if (instruction instanceof Instruction.Phi phi) {
@@ -50,7 +54,7 @@ else if (instruction.definesVar()) {
         }
     }
 
-    private static void recordUses(CompiledFunction function, Map<Integer, SSADef> defUseChains) {
+    private static void recordUses(CompiledFunction function, Map<Register, SSADef> defUseChains) {
         for (BasicBlock block : function.getBlocks()) {
             for (Instruction instruction : block.instructions) {
                 if (instruction instanceof Instruction.Phi phi) {
@@ -65,16 +69,16 @@ private static void recordUses(CompiledFunction function, Map<Integer, SSADef> d
         }
     }
 
-    private static void recordUses(Map<Integer, SSADef> defUseChains, Register[] inputs, BasicBlock block, Instruction instruction) {
+    private static void recordUses(Map<Register, SSADef> defUseChains, Register[] inputs, BasicBlock block, Instruction instruction) {
         for (Register register : inputs) {
-            SSADef def = defUseChains.get(register.id);
+            SSADef def = defUseChains.get(register);
             def.useList.add(instruction);
         }
     }
 
-    private static void recordDef(Map<Integer, SSADef> defUseChains, Register value, Instruction instruction) {
-        if (defUseChains.containsKey(value.id))
+    private static void recordDef(Map<Register, SSADef> defUseChains, Register value, Instruction instruction) {
+        if (defUseChains.containsKey(value))
             throw new CompilerException("Register already defined, invalid multiple definition in SSA");
-        defUseChains.put(value.id, new SSADef(value, instruction));
+        defUseChains.put(value, new SSADef(instruction));
     }
 }
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SparseConditionalConstantPropagation.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SparseConditionalConstantPropagation.java
index f56154e..47a838b 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SparseConditionalConstantPropagation.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SparseConditionalConstantPropagation.java
@@ -1,5 +1,6 @@
 package com.compilerprogramming.ezlang.compiler;
 
+import com.compilerprogramming.ezlang.exceptions.CompilerException;
 import com.compilerprogramming.ezlang.types.Type;
 
 import java.util.*;
@@ -10,8 +11,8 @@
  *
  * <ol>
  *     <li>Constant Propagation with Conditional Branches. Wegman and Zadeck.</li>
- *     <li>Modern Compiler Implementation in C</li>
- *     <li></li>
+ *     <li>Modern Compiler Implementation in C, Andrew Appel, section 19.3</li>
+ *     <li>Building an Optimizing Compiler, Bob Morgan, section 8.3</li>
  * </ol>
  *
  *
@@ -19,21 +20,34 @@
 public class SparseConditionalConstantPropagation {
 
     /**
-     * Contains a lattice for all possible definitions
+     * Contains a lattice for each SSA definition
      */
     ValueLattice valueLattice;
     /**
-     * Executable status for each flow edge
+     * Executable status for each flow edge, initially all edges are
+     * marked non-executable except the start block
      */
     Map<FlowEdge, Boolean> flowEdges;
+    /**
+     * Worklist of ssaedges (the term used by SCCP paper)
+     */
     WorkList<Instruction> instructionWorkList;
+    /**
+     * As edges between basic blocks become executable, we
+     * add them the impacted blocks to the worklist for processing.
+     */
     WorkList<BasicBlock> flowWorklist;
+    /**
+     * We don't evaluate a block more than once (except for Phi instructions
+     * in the block). So we have to track which blocks have already been
+     * evaluated.
+     */
     BitSet visited = new BitSet();
     /**
      * Def use chains for each register
      * Called SSAEdge in the original paper.
      */
-    Map<Integer, SSAEdges.SSADef> ssaEdges;
+    Map<Register, SSAEdges.SSADef> ssaEdges;
     CompiledFunction function;
 
     public SparseConditionalConstantPropagation constantPropagation(CompiledFunction function) {
@@ -61,8 +75,7 @@ public String toString() {
             }
         }
         sb.append("Lattices:\n");
-        for (var id: valueLattice.valueLattice.keySet()) {
-            var register = function.registerPool.getReg(id);
+        for (var register: valueLattice.getRegisters()) {
             sb.append(register.name()).append("=").append(valueLattice.get(register)).append("\n");
         }
         return sb.toString();
@@ -86,14 +99,14 @@ private void visitInstruction(Instruction instruction) {
             if (instruction instanceof Instruction.ConditionalBranch || instruction instanceof Instruction.Jump) {
                 for (BasicBlock s : block.successors) {
                     if (isEdgeExecutable(block, s)) {
-                        flowWorklist.push(block);   // Is this correct ?
+                        flowWorklist.push(block);   // Push both this block and successor to worklist?
                         flowWorklist.push(s);
                     }
                 }
             } else if (instruction.definesVar() || instruction instanceof Instruction.Phi) {
                 var def = instruction instanceof Instruction.Phi phi ? phi.value() : instruction.def();
                 // Push all uses (instructions) of the def into the worklist
-                SSAEdges.SSADef ssaDef = ssaEdges.get(def.id);
+                SSAEdges.SSADef ssaDef = ssaEdges.get(def);
                 if (ssaDef != null) {
                     for (Instruction use : ssaDef.useList) {
                         instructionWorkList.push(use);
@@ -209,6 +222,14 @@ public String toString() {
         }
     }
 
+    /**
+     * This is based on the description of CP_Evaluate(I) in
+     * Building an Optimizing Compiler. It evaluates an instruction and
+     * if the instruction defines an SSA variable, then it updates the lattice
+     * value of that variable. If the lattice changes then this returns true,
+     * else false. For branches the change in executable status of an edge is
+     * used instead of the lattice value change.
+     */
     private boolean evalInstruction(Instruction instruction) {
         BasicBlock block = instruction.block;
         boolean changed = false;
@@ -252,8 +273,6 @@ private boolean evalInstruction(Instruction instruction) {
                 } else throw new IllegalStateException();
             }
             case Instruction.Call callInst -> {
-                // Copy args to new frame
-                // Copy return value in expected location
                 if (!(callInst.callee.returnType instanceof Type.TypeVoid)) {
                     var cell = valueLattice.get(callInst.returnOperand().reg);
                     changed = cell.setKind(V_VARYING);
@@ -341,6 +360,7 @@ private boolean visitPhi(BasicBlock block, Instruction.Phi phiInst) {
         LatticeElement newValue = new LatticeElement(V_UNDEFINED, 0);
         for (int j = 0; j < block.predecessors.size(); j++) {
             BasicBlock pred = block.predecessors.get(j);
+            // We ignore non-executable edges
             if (isEdgeExecutable(pred, block)) {
                 LatticeElement varValue = valueLattice.get(phiInst.input(j));
                 newValue.meet(varValue);
@@ -357,6 +377,7 @@ private boolean markEdgeExecutable(BasicBlock source, BasicBlock target) {
         var oldValue = flowEdges.get(edge);
         assert oldValue != null;
         if (!oldValue) {
+            // Mark edge as executable
             flowEdges.put(edge, true);
             return true;
         }
@@ -393,6 +414,8 @@ private static boolean evalLogical(LatticeElement cell, LatticeElement left, Lat
             }
             changed = cell.meet(result);
         } else if (left.kind == V_VARYING || right.kind == V_VARYING) {
+            // We could constrain the result here to the set [0-1]
+            // but we don't track ranges or sets of values
             changed = cell.setKind(V_VARYING);
         }
         return changed;
@@ -412,6 +435,7 @@ private static boolean evalArith(LatticeElement cell, LatticeElement left, Latti
                     result = leftValue - rightValue;
                     break;
                 case "/":
+                    if (rightValue == 0) throw new CompilerException("Division by zero");
                     result = leftValue / rightValue;
                     break;
                 case "*":
@@ -425,25 +449,34 @@ private static boolean evalArith(LatticeElement cell, LatticeElement left, Latti
             }
             changed = cell.meet(result);
         } else if (binOp.equals("*") && ((left.kind == V_CONSTANT && left.value == 0) || (right.kind == V_CONSTANT && right.value == 0))) {
-            var result = new LatticeElement(V_CONSTANT, 0);
-            changed = cell.meet(result);
+            // multiplication with 0 yields 0
+            changed = cell.meet(0);
         } else if (left.kind == V_VARYING || right.kind == V_VARYING) {
             changed = cell.setKind(V_VARYING);
         }
         return changed;
     }
 
+    /**
+     * Maintains a Lattice for each SSA variable - i.e register
+     * Initial value of lattice is TOP/Undefined
+     */
     static final class ValueLattice {
-        Map<Integer, LatticeElement> valueLattice = new HashMap<>();
+
+        private final Map<Register, LatticeElement> valueLattice = new HashMap<>();
 
         LatticeElement get(Register reg) {
-            var cell = valueLattice.get(reg.id);
+            var cell = valueLattice.get(reg);
             if (cell == null) {
+                // Initial value is UNDEFINED/TOP
                 cell = new LatticeElement(V_UNDEFINED, 0);
-                valueLattice.put(reg.id, cell);
+                valueLattice.put(reg, cell);
             }
             return cell;
         }
+        Set<Register> getRegisters() {
+            return valueLattice.keySet();
+        }
     }
 
     static final class WorkList<E> {

From a956288e19bf2e6ddc7eca04f1c2bffea815ed26 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Wed, 1 Jan 2025 20:52:49 +0000
Subject: [PATCH 004/119] Dot output

---
 .../ezlang/compiler/BasicBlock.java           | 22 ++++++++++++++++
 .../ezlang/compiler/CompiledFunction.java     | 25 +++++++++++++++----
 2 files changed, 42 insertions(+), 5 deletions(-)

diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/BasicBlock.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/BasicBlock.java
index 5401c04..845b2f6 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/BasicBlock.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/BasicBlock.java
@@ -164,6 +164,28 @@ public static StringBuilder toStr(StringBuilder sb, BasicBlock bb, BitSet visite
         }
         return sb;
     }
+    private String escapeHtmlChars(String s) {
+        if (s.contains(">"))
+            s = s.replaceAll(">", " &gt; ");
+        if (s.contains(">="))
+            s = s.replaceAll(">=", " &ge; ");
+        if (s.contains("<"))
+            s = s.replaceAll("<", " &lt; ");
+        if (s.contains("<="))
+            s = s.replaceAll("<=", " &le; ");
+        return s;
+    }
+    public StringBuilder toDot(StringBuilder sb, boolean verbose) {
+        sb.append("<TABLE BORDER=\"1\" CELLBORDER=\"0\">\n");
+        sb.append("<TR><TD><B>L").append(bid).append("</B></TD></TR>\n");
+        for (Instruction i: instructions) {
+            sb.append("<TR><TD>");
+            sb.append(escapeHtmlChars(i.toStr(new StringBuilder()).toString()));
+            sb.append("</TD></TR>\n");
+        }
+        sb.append("</TABLE>");
+        return sb;
+    }
     @Override
     public boolean equals(Object o) {
         if (this == o) return true;
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
index ff005bc..8cd4c23 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
@@ -14,7 +14,7 @@ public class CompiledFunction {
 
     public BasicBlock entry;
     public BasicBlock exit;
-    private int bid = 0;
+    private int BID = 0;
     public BasicBlock currentBlock;
     private BasicBlock currentBreakTarget;
     private BasicBlock currentContinueTarget;
@@ -41,7 +41,7 @@ public CompiledFunction(Symbol.FunctionTypeSymbol functionSymbol) {
         this.functionType = (Type.TypeFunction) functionSymbol.type;
         this.registerPool = new RegisterPool();
         setVirtualRegisters(funcDecl.scope);
-        this.bid = 0;
+        this.BID = 0;
         this.entry = this.currentBlock = createBlock();
         this.exit = createBlock();
         this.currentBreakTarget = null;
@@ -55,7 +55,7 @@ public CompiledFunction(Symbol.FunctionTypeSymbol functionSymbol) {
     public CompiledFunction(Type.TypeFunction functionType) {
         this.functionType = (Type.TypeFunction) functionType;
         this.registerPool = new RegisterPool();
-        this.bid = 0;
+        this.BID = 0;
         this.entry = this.currentBlock = createBlock();
         this.exit = createBlock();
         this.currentBreakTarget = null;
@@ -99,11 +99,11 @@ private void setVirtualRegisters(Scope scope) {
     }
 
     public BasicBlock createBlock() {
-        return new BasicBlock(bid++);
+        return new BasicBlock(BID++);
     }
 
     private BasicBlock createLoopHead() {
-        return new BasicBlock(bid++, true);
+        return new BasicBlock(BID++, true);
     }
 
     private void compileBlock(AST.BlockStmt block) {
@@ -574,4 +574,19 @@ public void livenessAnalysis() {
     public List<BasicBlock> getBlocks() {
         return BBHelper.findAllBlocks(entry);
     }
+
+    public StringBuilder toDot(StringBuilder sb, boolean verbose) {
+        sb.append("digraph CompiledFunction {\n");
+        List<BasicBlock> blocks = getBlocks();
+        for (BasicBlock block: blocks) {
+            sb.append("L").append(block.bid).append(" [shape=none, margin=0, label=<");
+            block.toDot(sb, verbose);
+            sb.append(">];\n");
+            for (BasicBlock s: block.successors) {
+                sb.append("L").append(block.bid).append(" -> ").append("L").append(s.bid).append("\n");
+            }
+        }
+        sb.append("}\n");
+        return sb;
+    }
 }

From bfba0002fb269007169623ecc70a3ceed4091312 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Wed, 1 Jan 2025 20:57:24 +0000
Subject: [PATCH 005/119] SCCP: don't visit instruction that's not in a visited
 block because block might not be executable Test case added

---
 .../SparseConditionalConstantPropagation.java | 40 +++++----
 .../ezlang/compiler/TestSCCP.java             | 85 +++++++++++++++++++
 2 files changed, 108 insertions(+), 17 deletions(-)

diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SparseConditionalConstantPropagation.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SparseConditionalConstantPropagation.java
index 47a838b..08c558a 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SparseConditionalConstantPropagation.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SparseConditionalConstantPropagation.java
@@ -65,22 +65,6 @@ public SparseConditionalConstantPropagation constantPropagation(CompiledFunction
         return this;
     }
 
-    @Override
-    public String toString() {
-        StringBuilder sb = new StringBuilder();
-        sb.append("Flow edges:\n");
-        for (var edge : flowEdges.keySet()) {
-            if (flowEdges.get(edge)) {
-                sb.append(edge).append("=Executable").append("\n");
-            }
-        }
-        sb.append("Lattices:\n");
-        for (var register: valueLattice.getRegisters()) {
-            sb.append(register.name()).append("=").append(valueLattice.get(register)).append("\n");
-        }
-        return sb.toString();
-    }
-
     private void visitBlock(BasicBlock b) {
         for (var phi : b.phis()) {
             visitInstruction(phi);
@@ -109,7 +93,10 @@ private void visitInstruction(Instruction instruction) {
                 SSAEdges.SSADef ssaDef = ssaEdges.get(def);
                 if (ssaDef != null) {
                     for (Instruction use : ssaDef.useList) {
-                        instructionWorkList.push(use);
+                        if (visited.get(use.block.bid))
+                            // Don't visit the instruction if block hasn't been
+                            // visited
+                            instructionWorkList.push(use);
                     }
                 }
             }
@@ -457,6 +444,25 @@ private static boolean evalArith(LatticeElement cell, LatticeElement left, Latti
         return changed;
     }
 
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("Flow edges:\n");
+        for (var edge : flowEdges.keySet()) {
+            if (flowEdges.get(edge)) {
+                sb.append(edge).append("=Executable").append("\n");
+            }
+            else {
+                sb.append(edge).append("=NOT Executable").append("\n");
+            }
+        }
+        sb.append("Lattices:\n");
+        for (var register: valueLattice.getRegisters()) {
+            sb.append(register.name()).append("=").append(valueLattice.get(register)).append("\n");
+        }
+        return sb.toString();
+    }
+
     /**
      * Maintains a Lattice for each SSA variable - i.e register
      * Initial value of lattice is TOP/Undefined
diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSCCP.java b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSCCP.java
index ec9ec15..c0d4cae 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSCCP.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSCCP.java
@@ -17,6 +17,7 @@ String compileSrc(String src) {
                 var functionBuilder = (CompiledFunction) f.code();
                 new EnterSSA(functionBuilder);
                 BasicBlock.toStr(sb, functionBuilder.entry, new BitSet(), false);
+                //functionBuilder.toDot(sb, false);
                 sb.append(new SparseConditionalConstantPropagation().constantPropagation(functionBuilder).toString());
             }
         }
@@ -53,14 +54,98 @@ func foo()->Int {
     i_1 = 3
     goto  L4
 Flow edges:
+L0->L2=NOT Executable
 L0->L3=Executable
 L4->L1=Executable
+L2->L4=NOT Executable
 L3->L4=Executable
 Lattices:
 i_0=1
 %t1_0=0
 i_1=3
 i_3=3
+""";
+        Assert.assertEquals(expected, actual);
+    }
+
+    // 19.4 in MCIC Appel
+    // Expected results are based on fig 19.13 page 456
+    @Test
+    public void test2() {
+        String src = """
+func foo()->Int {
+    var i = 1
+    var j = 1
+    var k = 0
+    while (k < 100) {
+        if (j < 20) {
+            j = i
+            k = k + 1
+        }
+        else {
+            j = k
+            k = k + 2
+        }
+    }
+    return j
+}
+""";
+        String actual = compileSrc(src);
+        String expected = """
+L0:
+    i_0 = 1
+    j_0 = 1
+    k_0 = 0
+    goto  L2
+L2:
+    k_1 = phi(k_0, k_4)
+    j_1 = phi(j_0, j_4)
+    %t3_0 = k_1<100
+    if %t3_0 goto L3 else goto L4
+L3:
+    %t4_0 = j_1<20
+    if %t4_0 goto L5 else goto L6
+L5:
+    j_3 = i_0
+    %t5_0 = k_1+1
+    k_3 = %t5_0
+    goto  L7
+L7:
+    k_4 = phi(k_3, k_2)
+    j_4 = phi(j_3, j_2)
+    goto  L2
+L6:
+    j_2 = k_1
+    %t6_0 = k_1+2
+    k_2 = %t6_0
+    goto  L7
+L4:
+    ret j_1
+    goto  L1
+L1:
+Flow edges:
+L0->L2=Executable
+L4->L1=Executable
+L2->L3=Executable
+L2->L4=Executable
+L3->L5=Executable
+L7->L2=Executable
+L3->L6=NOT Executable
+L5->L7=Executable
+L6->L7=NOT Executable
+Lattices:
+j_3=1
+%t5_0=varying
+k_3=varying
+k_4=varying
+j_4=1
+i_0=1
+j_0=1
+k_0=0
+k_1=varying
+j_1=1
+%t3_0=varying
+%t4_0=1
 """;
         Assert.assertEquals(expected, actual);
     }

From 1bdf3f6c87ad533acd8f7f50cfb53a299e92479f Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <dibyendu@redukti.com>
Date: Sun, 12 Jan 2025 10:36:53 +0000
Subject: [PATCH 006/119] Ensure that Phi inputs are not assumed to be
 registers only because SCCP pass will replace registers with constants.

---
 .../ezlang/compiler/EnterSSA.java             |  2 +-
 .../ezlang/compiler/ExitSSA.java              |  7 +++-
 .../ezlang/compiler/Instruction.java          | 42 ++++++++++++++-----
 .../ezlang/compiler/Liveness.java             |  2 +-
 .../ezlang/compiler/SSAEdges.java             |  2 +-
 .../SparseConditionalConstantPropagation.java |  2 +-
 6 files changed, 41 insertions(+), 16 deletions(-)

diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/EnterSSA.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/EnterSSA.java
index 54afbc3..557bf04 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/EnterSSA.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/EnterSSA.java
@@ -155,7 +155,7 @@ void search(BasicBlock block) {
         for (BasicBlock s: block.successors) {
             int j = block.whichPred(s);
             for (Instruction.Phi phi: s.phis()) {
-                Register oldReg = phi.input(j);
+                Register oldReg = phi.inputAsRegister(j);
                 phi.replaceInput(j, stacks[oldReg.nonSSAId()].top());
             }
         }
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA.java
index 684d11e..5dd20bb 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA.java
@@ -52,6 +52,11 @@ private void insertCopies(BasicBlock block) {
      * replace all uses u with stacks[i]
      */
     private void replaceUses(Instruction i) {
+        if (i instanceof Instruction.Phi)
+            // FIXME check this can never be valid
+            // tests 8/9 in TestInterpreter invoke on Phi but
+            // replacements are same as existing inputs
+            return;
         var oldUses = i.uses();
         Register[] newUses = new Register[oldUses.size()];
         for (int u = 0; u < oldUses.size(); u++) {
@@ -86,7 +91,7 @@ private void scheduleCopies(BasicBlock block, List<Integer> pushed) {
             int j = block.whichPred(s);
             for (Instruction.Phi phi: s.phis()) {
                 Register dst = phi.value();
-                Register src = phi.input(j);   // jth operand of phi node
+                Register src = phi.inputAsRegister(j);   // jth operand of phi node
                 copySet.add(new CopyItem(src, dst));
                 map.put(src.id, src);
                 map.put(dst.id, dst);
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java
index 9a98f37..cf98485 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java
@@ -27,8 +27,8 @@ public abstract class Instruction {
     static final int I_FIELD_SET = 16;
 
     public final int opcode;
-    public Operand.RegisterOperand def;
-    public Operand[] uses;
+    protected Operand.RegisterOperand def;
+    protected Operand[] uses;
     public BasicBlock block;
 
     public Instruction(int opcode, Operand... uses) {
@@ -340,21 +340,29 @@ public StringBuilder toStr(StringBuilder sb) {
     /**
      * Phi does not generate uses or defs directly, instead
      * they are treated as a special case.
-     * To avoid bugs we do not use the def or uses.
+     * To avoid bugs we disable the standard interfaces
      */
     public static class Phi extends Instruction {
-        public Register value;
-        public final Register[] inputs;
+        private Register value;
         public Phi(Register value, List<Register> inputs) {
             super(I_PHI);
             this.value = value;
-            this.inputs = inputs.toArray(new Register[inputs.size()]);
+            this.uses = new Operand[inputs.size()];
+            for (int i = 0; i < inputs.size(); i++) {
+                this.uses[i] = new Operand.RegisterOperand(inputs.get(i));
+            }
         }
         public void replaceInput(int i, Register newReg) {
-            inputs[i] = newReg;
+            uses[i].replaceRegister(newReg);
+        }
+        /**
+         * This will fail in input was replaced by a constant
+         */
+        public Register inputAsRegister(int i) {
+            return ((Operand.RegisterOperand) uses[i]).reg;
         }
-        public Register input(int i) {
-            return inputs[i];
+        public Register[] inputRegisters() {
+            return super.uses().toArray(new Register[super.uses().size()]);
         }
         @Override
         public Register def() {
@@ -368,6 +376,18 @@ public void replaceDef(Register newReg) {
         public boolean definesVar() {
             return false;
         }
+        @Override
+        public List<Register> uses() {
+            return Collections.emptyList();
+        }
+        @Override
+        public void replaceUses(Register[] newUses) {
+            throw new UnsupportedOperationException();
+        }
+        @Override
+        public boolean replaceUse(Register source, Register target) {
+            throw new UnsupportedOperationException();
+        }
         public Register value() {
             return value;
         }
@@ -377,9 +397,9 @@ public void replaceValue(Register newReg) {
         @Override
         public StringBuilder toStr(StringBuilder sb) {
             sb.append(value().name()).append(" = phi(");
-            for (int i = 0; i < inputs.length; i++) {
+            for (int i = 0; i < uses.length; i++) {
                 if (i > 0) sb.append(", ");
-                sb.append(inputs[i].name());
+                sb.append(uses[i].toString());
             }
             sb.append(")");
             return sb;
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Liveness.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Liveness.java
index 8d0b322..daadd1f 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Liveness.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Liveness.java
@@ -69,7 +69,7 @@ private void init(List<BasicBlock> blocks) {
                 if (instruction instanceof Instruction.Phi phi) {
                     for (int i = 0; i < block.predecessors.size(); i++) {
                         BasicBlock pred = block.predecessors.get(i);
-                        Register use = phi.input(i);
+                        Register use = phi.inputAsRegister(i);
                         // We can have a block referring it its own phis
                         // if there is loop back and there are cycles
                         // such as e.g. the swap copy problem
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SSAEdges.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SSAEdges.java
index 262a3ba..ed8b8ea 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SSAEdges.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SSAEdges.java
@@ -58,7 +58,7 @@ private static void recordUses(CompiledFunction function, Map<Register, SSADef>
         for (BasicBlock block : function.getBlocks()) {
             for (Instruction instruction : block.instructions) {
                 if (instruction instanceof Instruction.Phi phi) {
-                    recordUses(defUseChains, phi.inputs, block, instruction);
+                    recordUses(defUseChains, phi.inputRegisters(), block, instruction);
                 }
                 else {
                     List<Register> uses = instruction.uses();
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SparseConditionalConstantPropagation.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SparseConditionalConstantPropagation.java
index 08c558a..3b5dd9f 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SparseConditionalConstantPropagation.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SparseConditionalConstantPropagation.java
@@ -349,7 +349,7 @@ private boolean visitPhi(BasicBlock block, Instruction.Phi phiInst) {
             BasicBlock pred = block.predecessors.get(j);
             // We ignore non-executable edges
             if (isEdgeExecutable(pred, block)) {
-                LatticeElement varValue = valueLattice.get(phiInst.input(j));
+                LatticeElement varValue = valueLattice.get(phiInst.inputAsRegister(j));
                 newValue.meet(varValue);
             }
         }

From 5c063cbbd432ae5415fb778b10e99083c24d9160 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Sun, 12 Jan 2025 23:41:48 +0000
Subject: [PATCH 007/119] SCCP: wip apply SCCP changes - propagate constants to
 uses and delete defs no longer needed

---
 .../ezlang/compiler/BasicBlock.java           |  3 ++
 .../ezlang/compiler/Instruction.java          |  9 ++++-
 .../SparseConditionalConstantPropagation.java | 39 +++++++++++++++++++
 3 files changed, 50 insertions(+), 1 deletion(-)

diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/BasicBlock.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/BasicBlock.java
index 845b2f6..c7b120b 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/BasicBlock.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/BasicBlock.java
@@ -98,6 +98,9 @@ public void add(Instruction instruction) {
         instructions.add(instruction);
         instruction.block = this;
     }
+    public void deleteInstruction(Instruction instruction) {
+        instructions.remove(instruction);
+    }
     public void addSuccessor(BasicBlock successor) {
         successors.add(successor);
         successor.predecessors.add(this);
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java
index cf98485..12bbdac 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java
@@ -87,7 +87,14 @@ public boolean replaceUse(Register source, Register target) {
         }
         return replaced;
     }
-
+    public void replaceWithConstant(Register register, Operand.ConstantOperand constantOperand) {
+        for (int i = 0; i < uses.length; i++) {
+            Operand operand = uses[i];
+            if (operand != null && operand instanceof Operand.RegisterOperand registerOperand && registerOperand.reg.id == register.id) {
+                uses[i] = constantOperand;
+            }
+        }
+    }
     public static class NoOp extends Instruction {
         public NoOp() {
             super(I_NOOP);
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SparseConditionalConstantPropagation.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SparseConditionalConstantPropagation.java
index 3b5dd9f..1b43414 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SparseConditionalConstantPropagation.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SparseConditionalConstantPropagation.java
@@ -119,6 +119,45 @@ private void init(CompiledFunction function) {
         visited = new BitSet();
     }
 
+    private void apply() {
+        /*
+        The constant propagation algorithm does not change the flow graph - it computes
+        information about the flow graph. The compiler now uses this information to improve
+        the graph in the following ways:
+
+        * The instructions corresponding to temporaries that evaluate as constants are modified
+        to be load constant instructions.
+
+        • An edge that has not become executable is eliminated, and the conditional branching
+        instruction representing that edge is modified to be a simpler instruction.
+        The phi-nodes at the head of the edge are modified to have one less operand.
+
+        • Blocks that become unreachable are eliminated.
+
+         Bob Morgan. Building an Optimizing Compiler
+         */
+
+        replaceVarsWithConstants();
+    }
+
+    private void replaceVarsWithConstants() {
+        for (var register: valueLattice.getRegisters()) {
+            var latticeElement = valueLattice.get(register);
+            if (latticeElement.kind == V_CONSTANT) {
+                var constant = new Operand.ConstantOperand(latticeElement.value, register.type);
+                var defUseChain = this.ssaEdges.get(register);
+                // FIXME this only for live blocks
+                for (var usingInstruction: defUseChain.useList) {
+                    usingInstruction.replaceWithConstant(register, constant);
+                }
+                defUseChain.useList.clear();
+                var block = defUseChain.instruction.block;
+                block.deleteInstruction(defUseChain.instruction);
+                ssaEdges.remove(register);
+            }
+        }
+    }
+
     static final byte V_UNDEFINED = 1;  // TOP
     static final byte V_CONSTANT = 2;
     static final byte V_VARYING = 3;    // BOTTOM

From 2b1dba80e07b73b8d5a70991a7e113071e60324e Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <dibyendu@redukti.com>
Date: Sat, 18 Jan 2025 14:37:00 +0000
Subject: [PATCH 008/119] Add missing % to parser

---
 .../main/java/com/compilerprogramming/ezlang/parser/Parser.java  | 1 +
 1 file changed, 1 insertion(+)

diff --git a/parser/src/main/java/com/compilerprogramming/ezlang/parser/Parser.java b/parser/src/main/java/com/compilerprogramming/ezlang/parser/Parser.java
index b24c815..de65e19 100644
--- a/parser/src/main/java/com/compilerprogramming/ezlang/parser/Parser.java
+++ b/parser/src/main/java/com/compilerprogramming/ezlang/parser/Parser.java
@@ -298,6 +298,7 @@ private AST.Expr parseAddition(Lexer lexer) {
     private AST.Expr parseMultiplication(Lexer lexer) {
         var x = parseUnary(lexer);
         while (isToken(currentToken, "*")
+                || isToken(currentToken, "%")
                 || isToken(currentToken, "/")) {
             var tok = currentToken;
             nextToken(lexer);

From b838311e1202f9a4345ae371d3f9504bc76f545a Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <dibyendu@redukti.com>
Date: Sat, 18 Jan 2025 14:38:44 +0000
Subject: [PATCH 009/119] Refactor whichPred() to reverse caller/arg. Add
 whichSucc().

---
 .../ezlang/compiler/BasicBlock.java              | 16 +++++++++++++---
 .../ezlang/compiler/EnterSSA.java                |  2 +-
 .../ezlang/compiler/ExitSSA.java                 |  2 +-
 3 files changed, 15 insertions(+), 5 deletions(-)

diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/BasicBlock.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/BasicBlock.java
index c7b120b..04ad220 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/BasicBlock.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/BasicBlock.java
@@ -135,15 +135,25 @@ public List<Instruction.Phi> phis() {
         }
         return list;
     }
-    public int whichPred(BasicBlock s) {
+    public int whichPred(BasicBlock pred) {
         int i = 0;
-        for (BasicBlock p: s.predecessors) {
-            if (p == this)
+        for (BasicBlock p: predecessors) {
+            if (p == pred)
                 return i;
             i++;
         }
         throw new IllegalStateException();
     }
+    public int whichSucc(BasicBlock succ) {
+        int i = 0;
+        for (BasicBlock s: successors) {
+            if (s == succ)
+                return i;
+            i++;
+        }
+        throw new IllegalStateException();
+    }
+
     public static StringBuilder toStr(StringBuilder sb, BasicBlock bb, BitSet visited, boolean dumpLiveness)
     {
         if (visited.get(bb.bid))
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/EnterSSA.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/EnterSSA.java
index 557bf04..9365c82 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/EnterSSA.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/EnterSSA.java
@@ -153,7 +153,7 @@ void search(BasicBlock block) {
         }
         // Update phis in successor blocks
         for (BasicBlock s: block.successors) {
-            int j = block.whichPred(s);
+            int j = s.whichPred(block);
             for (Instruction.Phi phi: s.phis()) {
                 Register oldReg = phi.inputAsRegister(j);
                 phi.replaceInput(j, stacks[oldReg.nonSSAId()].top());
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA.java
index 5dd20bb..b78d9f0 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA.java
@@ -88,7 +88,7 @@ private void scheduleCopies(BasicBlock block, List<Integer> pushed) {
         Map<Integer, Register> map = new HashMap<>();
         BitSet usedByAnother = new BitSet(function.registerPool.numRegisters()*2);
         for (BasicBlock s: block.successors) {
-            int j = block.whichPred(s);
+            int j = s.whichPred(block);
             for (Instruction.Phi phi: s.phis()) {
                 Register dst = phi.value();
                 Register src = phi.inputAsRegister(j);   // jth operand of phi node

From 2c491eb0ee2d42230f05a5966bd4d8e99e0fd020 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <dibyendu@redukti.com>
Date: Sat, 18 Jan 2025 14:39:00 +0000
Subject: [PATCH 010/119] Add removeInput()

---
 .../compilerprogramming/ezlang/compiler/Instruction.java    | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java
index 12bbdac..cde20ac 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java
@@ -401,6 +401,12 @@ public Register value() {
         public void replaceValue(Register newReg) {
             this.value = newReg;
         }
+        public void removeInput(int i) {
+            var newUses = new Operand[uses.length - 1];
+            System.arraycopy(uses, 0, newUses, 0, i);
+            System.arraycopy(uses, i + 1, newUses, i, uses.length - i - 1);
+            this.uses = newUses;
+        }
         @Override
         public StringBuilder toStr(StringBuilder sb) {
             sb.append(value().name()).append(" = phi(");

From db2bbdfb49888eb5b314f44b2d8e3666def93544 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <dibyendu@redukti.com>
Date: Sat, 18 Jan 2025 18:01:53 +0000
Subject: [PATCH 011/119] Fix bug in ExitSSA

---
 .../ezlang/compiler/ExitSSA.java              |  9 ++--
 .../ezlang/compiler/TestSSATransform.java     | 42 +++++++++++++++++++
 2 files changed, 48 insertions(+), 3 deletions(-)

diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA.java
index b78d9f0..420f67f 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA.java
@@ -72,11 +72,13 @@ private void replaceUses(Instruction i) {
     static class CopyItem {
         final Register src;
         final Register dest;
+        final BasicBlock destBlock;
         boolean removed;
 
-        public CopyItem(Register src, Register dest) {
+        public CopyItem(Register src, Register dest, BasicBlock destBlock) {
             this.src = src;
             this.dest = dest;
+            this.destBlock = destBlock;
             this.removed = false;
         }
     }
@@ -92,7 +94,7 @@ private void scheduleCopies(BasicBlock block, List<Integer> pushed) {
             for (Instruction.Phi phi: s.phis()) {
                 Register dst = phi.value();
                 Register src = phi.inputAsRegister(j);   // jth operand of phi node
-                copySet.add(new CopyItem(src, dst));
+                copySet.add(new CopyItem(src, dst, s));
                 map.put(src.id, src);
                 map.put(dst.id, dst);
                 usedByAnother.set(src.id);
@@ -118,6 +120,7 @@ private void scheduleCopies(BasicBlock block, List<Integer> pushed) {
                 final CopyItem copyItem = workList.remove(0);
                 final Register src = copyItem.src;
                 final Register dest = copyItem.dest;
+                final BasicBlock destBlock = copyItem.destBlock;
                 /* Engineering a Compiler: We can avoid the lost copy
                    problem by checking the liveness of the target name
                    for each copy that we try to insert. When we discover
@@ -127,7 +130,7 @@ private void scheduleCopies(BasicBlock block, List<Integer> pushed) {
                  */
                 if (block.liveOut.get(dest.id)) {
                     /* Insert a copy from dest to a new temp t at phi node defining dest */
-                    final Register t = addMoveToTempAfterPhi(block, dest);
+                    final Register t = addMoveToTempAfterPhi(destBlock, dest);
                     stacks[dest.id].push(t);
                     pushed.add(dest.id);
                 }
diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
index 8b46834..9901dde 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
@@ -840,4 +840,46 @@ func foo(x: Int, y: Int)->Int {
 
     }
 
+    @Test
+    public void testContinue() {
+        String src = """
+func foo(x: Int)->Int {
+   var sum = 0
+   var i = 0
+   while (i < x) {
+      if (i % 2 == 0)
+        continue
+      if (i / 3 == 1)
+        continue
+      sum = sum + 1
+      i = i + 1
+   }
+   return sum
+}
+                """;
+        String result = compileSrc(src);
+        System.out.println(result);
+
+    }
+
+    @Ignore
+    @Test
+    public void testInfiniteLoop() {
+        String src = """
+func foo() {
+   var i = 0
+   while (1) {
+        if (1)
+            continue;
+        else 
+            continue;
+   }
+}
+                """;
+        String result = compileSrc(src);
+        System.out.println(result);
+
+    }
+
+
 }
\ No newline at end of file

From 26c2aea245f279a7912e05d10b7700c889a8dbfb Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <dibyendu@redukti.com>
Date: Sat, 18 Jan 2025 21:22:14 +0000
Subject: [PATCH 012/119] Apply SCCP changes after analysis - i.e propagate
 constants and remove unreachable branches/blocks

---
 .../ezlang/compiler/BasicBlock.java           |  6 ++
 .../ezlang/compiler/Instruction.java          |  3 +
 .../ezlang/compiler/Liveness.java             |  2 +
 .../SparseConditionalConstantPropagation.java | 67 +++++++++++++++++--
 .../ezlang/compiler/TestSCCP.java             | 35 +++++++++-
 5 files changed, 106 insertions(+), 7 deletions(-)

diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/BasicBlock.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/BasicBlock.java
index 04ad220..bb82949 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/BasicBlock.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/BasicBlock.java
@@ -102,9 +102,15 @@ public void deleteInstruction(Instruction instruction) {
         instructions.remove(instruction);
     }
     public void addSuccessor(BasicBlock successor) {
+        assert successors.contains(successor) == false;
         successors.add(successor);
+        assert successor.predecessors.contains(this) == false;
         successor.predecessors.add(this);
     }
+    public void removeSuccessor(BasicBlock successor) {
+        successors.remove(successor);
+        successor.predecessors.remove(this);
+    }
 
     /**
      * Initially the phi has the form
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java
index cde20ac..d689f84 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java
@@ -368,6 +368,9 @@ public void replaceInput(int i, Register newReg) {
         public Register inputAsRegister(int i) {
             return ((Operand.RegisterOperand) uses[i]).reg;
         }
+        public boolean isRegisterInput(int i) {
+            return uses[i] instanceof Operand.RegisterOperand;
+        }
         public Register[] inputRegisters() {
             return super.uses().toArray(new Register[super.uses().size()]);
         }
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Liveness.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Liveness.java
index daadd1f..f5b2802 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Liveness.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Liveness.java
@@ -69,6 +69,8 @@ private void init(List<BasicBlock> blocks) {
                 if (instruction instanceof Instruction.Phi phi) {
                     for (int i = 0; i < block.predecessors.size(); i++) {
                         BasicBlock pred = block.predecessors.get(i);
+                        if (!phi.isRegisterInput(i))
+                            continue;
                         Register use = phi.inputAsRegister(i);
                         // We can have a block referring it its own phis
                         // if there is loop back and there are cycles
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SparseConditionalConstantPropagation.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SparseConditionalConstantPropagation.java
index 1b43414..a8e77e3 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SparseConditionalConstantPropagation.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SparseConditionalConstantPropagation.java
@@ -14,8 +14,6 @@
  *     <li>Modern Compiler Implementation in C, Andrew Appel, section 19.3</li>
  *     <li>Building an Optimizing Compiler, Bob Morgan, section 8.3</li>
  * </ol>
- *
- *
  */
 public class SparseConditionalConstantPropagation {
 
@@ -50,6 +48,9 @@ public class SparseConditionalConstantPropagation {
     Map<Register, SSAEdges.SSADef> ssaEdges;
     CompiledFunction function;
 
+    /** Used to track reachable blocks when the SCCP changes are applied */
+    BitSet executableBlocks = new BitSet();
+
     public SparseConditionalConstantPropagation constantPropagation(CompiledFunction function) {
         init(function);
         while (!flowWorklist.isEmpty() || !instructionWorkList.isEmpty()) {
@@ -119,7 +120,7 @@ private void init(CompiledFunction function) {
         visited = new BitSet();
     }
 
-    private void apply() {
+    public SparseConditionalConstantPropagation apply() {
         /*
         The constant propagation algorithm does not change the flow graph - it computes
         information about the flow graph. The compiler now uses this information to improve
@@ -136,22 +137,76 @@ private void apply() {
 
          Bob Morgan. Building an Optimizing Compiler
          */
-
+        markExecutableBlocks();
+        removeBranchesThatAreNotExecutable();
         replaceVarsWithConstants();
+        // Unreachable blocks are eliminated as there are no paths to them
+        return this;
+    }
+
+    private void markExecutableBlocks() {
+        var blocks = function.getBlocks();
+        executableBlocks = new BitSet(blocks.size());
+        executableBlocks.set(function.entry.bid);
+        for (FlowEdge edge: flowEdges.keySet()) {
+            if (flowEdges.get(edge)) {
+                executableBlocks.set(edge.source.bid);
+                executableBlocks.set(edge.target.bid);
+            }
+        }
+    }
+
+    /**
+     * Where we know which branch will be executed on a CBR,
+     * we replace such a branch with a jump to the known
+     * basic block
+     */
+    private void removeBranchesThatAreNotExecutable() {
+        for (var flowEdge : flowEdges.keySet()) {
+            if (!flowEdges.get(flowEdge)) {
+                if (executableBlocks.get(flowEdge.source.bid) ||
+                    executableBlocks.get(flowEdge.target.bid))
+                    removeEdge(flowEdge.source, flowEdge.target);
+            }
+        }
     }
 
+    private void removeEdge(BasicBlock source, BasicBlock target) {
+        int j = target.whichPred(source);
+        // Replace cbr with jump
+        int idx = source.instructions.size()-1;
+        Instruction instruction = source.instructions.get(idx);
+        if (instruction instanceof Instruction.ConditionalBranch cbr) {
+            BasicBlock remainingExecutableBlock = (cbr.falseBlock == target) ? cbr.trueBlock : cbr.falseBlock;
+            source.instructions.set(idx, new Instruction.Jump(remainingExecutableBlock));
+        }
+        // Remove phis in target corresponding to the input
+        for (var phi: target.phis()) {
+            phi.removeInput(j);
+        }
+        // update cfg
+        source.removeSuccessor(target);
+    }
+
+    /**
+     * Where a definition is known to be a constant,
+     * replace all uses with the constant and then delete
+     * the defining instruction.
+     */
     private void replaceVarsWithConstants() {
         for (var register: valueLattice.getRegisters()) {
             var latticeElement = valueLattice.get(register);
             if (latticeElement.kind == V_CONSTANT) {
                 var constant = new Operand.ConstantOperand(latticeElement.value, register.type);
                 var defUseChain = this.ssaEdges.get(register);
-                // FIXME this only for live blocks
+                // replace uses with constant
                 for (var usingInstruction: defUseChain.useList) {
-                    usingInstruction.replaceWithConstant(register, constant);
+                    if (executableBlocks.get(usingInstruction.block.bid))
+                        usingInstruction.replaceWithConstant(register, constant);
                 }
                 defUseChain.useList.clear();
                 var block = defUseChain.instruction.block;
+                // delete defining instruction
                 block.deleteInstruction(defUseChain.instruction);
                 ssaEdges.remove(register);
             }
diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSCCP.java b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSCCP.java
index c0d4cae..530fbd9 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSCCP.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSCCP.java
@@ -18,7 +18,11 @@ String compileSrc(String src) {
                 new EnterSSA(functionBuilder);
                 BasicBlock.toStr(sb, functionBuilder.entry, new BitSet(), false);
                 //functionBuilder.toDot(sb, false);
-                sb.append(new SparseConditionalConstantPropagation().constantPropagation(functionBuilder).toString());
+                var sccp = new SparseConditionalConstantPropagation().constantPropagation(functionBuilder);
+                sb.append(sccp.toString());
+                sccp.apply();
+                sb.append("After SCCP changes:\n");
+                functionBuilder.toStr(sb, false);
             }
         }
         return sb.toString();
@@ -64,6 +68,15 @@ func foo()->Int {
 %t1_0=0
 i_1=3
 i_3=3
+After SCCP changes:
+L0:
+    goto  L3
+L3:
+    goto  L4
+L4:
+    ret 3
+    goto  L1
+L1:
 """;
         Assert.assertEquals(expected, actual);
     }
@@ -146,6 +159,26 @@ func foo()->Int {
 j_1=1
 %t3_0=varying
 %t4_0=1
+After SCCP changes:
+L0:
+    goto  L2
+L2:
+    k_1 = phi(0, k_4)
+    %t3_0 = k_1<100
+    if %t3_0 goto L3 else goto L4
+L3:
+    goto  L5
+L5:
+    %t5_0 = k_1+1
+    k_3 = %t5_0
+    goto  L7
+L7:
+    k_4 = phi(k_3)
+    goto  L2
+L4:
+    ret 1
+    goto  L1
+L1:
 """;
         Assert.assertEquals(expected, actual);
     }

From cb2a899babff0cf4b110a89bc58007e5d49e3b15 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <dibyendu@redukti.com>
Date: Sat, 18 Jan 2025 22:28:48 +0000
Subject: [PATCH 013/119] Dominance frontier calc fails in infinite loop
 scenario - need to check what the correct solution is

---
 .../ezlang/compiler/DominatorTree.java        |  4 +-
 .../ezlang/compiler/TestSSATransform.java     | 54 ++++++++++++++++++-
 2 files changed, 55 insertions(+), 3 deletions(-)

diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/DominatorTree.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/DominatorTree.java
index ed97438..ea3dc2f 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/DominatorTree.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/DominatorTree.java
@@ -193,7 +193,9 @@ private void calculateDominanceFrontiers() {
             if (b.predecessors.size() >= 2) {
                 for (BasicBlock p : b.predecessors) {
                     BasicBlock runner = p;
-                    while (runner != b.idom) {
+                    // re runner != null: Dominance frontier calc fails in infinite loop
+                    // scenario - need to check what the correct solution is
+                    while (runner != b.idom && runner != null) {
                         runner.dominationFrontier.add(b);
                         runner = runner.idom;
                     }
diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
index 9901dde..0f94b3e 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
@@ -862,7 +862,6 @@ func foo(x: Int)->Int {
 
     }
 
-    @Ignore
     @Test
     public void testInfiniteLoop() {
         String src = """
@@ -877,7 +876,58 @@ func foo() {
 }
                 """;
         String result = compileSrc(src);
-        System.out.println(result);
+        Assert.assertEquals("""
+func foo
+Before SSA
+==========
+L0:
+    i = 0
+    goto  L2
+L2:
+    if 1 goto L3 else goto L4
+L3:
+    if 1 goto L5 else goto L6
+L5:
+    goto  L2
+L6:
+    goto  L2
+L4:
+    goto  L1
+L1:
+After SSA
+=========
+L0:
+    i_0 = 0
+    goto  L2
+L2:
+    if 1 goto L3 else goto L4
+L3:
+    if 1 goto L5 else goto L6
+L5:
+    goto  L2
+L6:
+    goto  L2
+L4:
+    goto  L1
+L1:
+After exiting SSA
+=================
+L0:
+    i_0 = 0
+    goto  L2
+L2:
+    if 1 goto L3 else goto L4
+L3:
+    if 1 goto L5 else goto L6
+L5:
+    goto  L2
+L6:
+    goto  L2
+L4:
+    goto  L1
+L1:                
+""",
+        result);
 
     }
 

From 05b15c63941a1bc811f7e6595ed5cd690647073e Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <dibyendu@redukti.com>
Date: Sat, 18 Jan 2025 22:29:04 +0000
Subject: [PATCH 014/119] Add expected result

---
 .../ezlang/compiler/TestSSATransform.java     | 116 +++++++++++++++++-
 1 file changed, 115 insertions(+), 1 deletion(-)

diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
index 0f94b3e..d2b4f04 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
@@ -858,7 +858,121 @@ func foo(x: Int)->Int {
 }
                 """;
         String result = compileSrc(src);
-        System.out.println(result);
+        Assert.assertEquals("""
+func foo
+Before SSA
+==========
+L0:
+    arg x
+    sum = 0
+    i = 0
+    goto  L2
+L2:
+    %t3 = i<x
+    if %t3 goto L3 else goto L4
+L3:
+    %t4 = i%2
+    %t5 = %t4==0
+    if %t5 goto L5 else goto L6
+L5:
+    goto  L2
+L6:
+    %t6 = i/3
+    %t7 = %t6==1
+    if %t7 goto L7 else goto L8
+L7:
+    goto  L2
+L8:
+    %t8 = sum+1
+    sum = %t8
+    %t9 = i+1
+    i = %t9
+    goto  L2
+L4:
+    ret sum
+    goto  L1
+L1:
+After SSA
+=========
+L0:
+    arg x_0
+    sum_0 = 0
+    i_0 = 0
+    goto  L2
+L2:
+    i_1 = phi(i_0, i_1, i_1, i_2)
+    sum_1 = phi(sum_0, sum_1, sum_1, sum_2)
+    %t3_0 = i_1<x_0
+    if %t3_0 goto L3 else goto L4
+L3:
+    %t4_0 = i_1%2
+    %t5_0 = %t4_0==0
+    if %t5_0 goto L5 else goto L6
+L5:
+    goto  L2
+L6:
+    %t6_0 = i_1/3
+    %t7_0 = %t6_0==1
+    if %t7_0 goto L7 else goto L8
+L7:
+    goto  L2
+L8:
+    %t8_0 = sum_1+1
+    sum_2 = %t8_0
+    %t9_0 = i_1+1
+    i_2 = %t9_0
+    goto  L2
+L4:
+    ret sum_1
+    goto  L1
+L1:
+After exiting SSA
+=================
+L0:
+    arg x_0
+    sum_0 = 0
+    i_0 = 0
+    i_1 = i_0
+    sum_1 = sum_0
+    goto  L2
+L2:
+    i_1_29 = i_1
+    i_1_25 = i_1
+    sum_1_27 = sum_1
+    %t3_0 = i_1<x_0
+    if %t3_0 goto L3 else goto L4
+L3:
+    %t4_0 = i_1%2
+    %t5_0 = %t4_0==0
+    if %t5_0 goto L5 else goto L6
+L5:
+    i_1_28 = i_1
+    i_1 = i_1_28
+    goto  L2
+L6:
+    %t6_0 = i_1/3
+    %t7_0 = %t6_0==1
+    if %t7_0 goto L7 else goto L8
+L7:
+    i_1_24 = i_1
+    i_1 = i_1_24
+    sum_1_26 = sum_1
+    sum_1 = sum_1_26
+    goto  L2
+L8:
+    %t8_0 = sum_1+1
+    sum_2 = %t8_0
+    %t9_0 = i_1+1
+    i_2 = %t9_0
+    i_1 = i_2
+    sum_1 = sum_2
+    goto  L2
+L4:
+    ret sum_1
+    goto  L1
+L1:                        
+""",
+                result);
 
     }
 

From 319a32d85f713518b839addaee6e847a20132468 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <dibyendu@redukti.com>
Date: Sun, 19 Jan 2025 00:34:11 +0000
Subject: [PATCH 015/119] Call parms may be constants

---
 .../ezlang/interpreter/Interpreter.java                   | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/interpreter/Interpreter.java b/optvm/src/main/java/com/compilerprogramming/ezlang/interpreter/Interpreter.java
index a62375f..6aec1b8 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/interpreter/Interpreter.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/interpreter/Interpreter.java
@@ -101,8 +101,12 @@ else if (cbrInst.condition() instanceof Operand.ConstantOperand constantOperand)
                     int baseReg = base+currentFunction.frameSize();
                     int reg = baseReg;
                     for (Operand arg: callInst.args()) {
-                        Operand.RegisterOperand param = (Operand.RegisterOperand) arg;
-                        execStack.stack[base + reg] = execStack.stack[base + param.frameSlot()];
+                        if (arg instanceof Operand.RegisterOperand param) {
+                            execStack.stack[base + reg] = execStack.stack[base + param.frameSlot()];
+                        }
+                        else if (arg instanceof Operand.ConstantOperand constantOperand) {
+                            execStack.stack[base + reg] = new Value.IntegerValue(constantOperand.value);
+                        }
                         reg += 1;
                     }
                     // Call function

From 3898943c5ac2b208473e8e5ae581a3e3d2a796a7 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <dibyendu@redukti.com>
Date: Sun, 19 Jan 2025 00:34:49 +0000
Subject: [PATCH 016/119] Make sure uses is an Operand array

---
 .../compilerprogramming/ezlang/compiler/Instruction.java | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java
index d689f84..20f70a2 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java
@@ -34,12 +34,14 @@ public abstract class Instruction {
     public Instruction(int opcode, Operand... uses) {
         this.opcode = opcode;
         this.def = null;
-        this.uses = uses;
+        this.uses = new Operand[uses.length];
+        System.arraycopy(uses, 0, this.uses, 0, uses.length);
     }
     public Instruction(int opcode, Operand.RegisterOperand def, Operand... uses) {
         this.opcode = opcode;
         this.def = def;
-        this.uses = uses;
+        this.uses = new Operand[uses.length];
+        System.arraycopy(uses, 0, this.uses, 0, uses.length);
     }
 
     public boolean isTerminal() { return false; }
@@ -368,6 +370,9 @@ public void replaceInput(int i, Register newReg) {
         public Register inputAsRegister(int i) {
             return ((Operand.RegisterOperand) uses[i]).reg;
         }
+        public Operand input(int i) {
+            return uses[i];
+        }
         public boolean isRegisterInput(int i) {
             return uses[i] instanceof Operand.RegisterOperand;
         }

From 828acea454f2d2c06ceea8b8714484f7b9582b56 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <dibyendu@redukti.com>
Date: Sun, 19 Jan 2025 00:41:28 +0000
Subject: [PATCH 017/119] ExitSSA: Tentative solution for phis with partial
 constant inputs

---
 .../ezlang/compiler/ExitSSA.java              | 41 ++++++++++++-------
 1 file changed, 26 insertions(+), 15 deletions(-)

diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA.java
index 420f67f..5bac8f8 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA.java
@@ -70,12 +70,12 @@ private void replaceUses(Instruction i) {
     }
 
     static class CopyItem {
-        final Register src;
+        final Operand src;
         final Register dest;
         final BasicBlock destBlock;
         boolean removed;
 
-        public CopyItem(Register src, Register dest, BasicBlock destBlock) {
+        public CopyItem(Operand src, Register dest, BasicBlock destBlock) {
             this.src = src;
             this.dest = dest;
             this.destBlock = destBlock;
@@ -93,11 +93,14 @@ private void scheduleCopies(BasicBlock block, List<Integer> pushed) {
             int j = s.whichPred(block);
             for (Instruction.Phi phi: s.phis()) {
                 Register dst = phi.value();
-                Register src = phi.inputAsRegister(j);   // jth operand of phi node
-                copySet.add(new CopyItem(src, dst, s));
-                map.put(src.id, src);
+                Operand srcOperand = phi.input(j); // jth operand of phi node
+                if (srcOperand instanceof Operand.RegisterOperand srcRegisterOperand) {
+                    Register src = srcRegisterOperand.reg;
+                    map.put(src.id, src);
+                    usedByAnother.set(src.id);
+                }
+                copySet.add(new CopyItem(srcOperand, dst, s));
                 map.put(dst.id, dst);
-                usedByAnother.set(src.id);
             }
         }
 
@@ -118,7 +121,7 @@ private void scheduleCopies(BasicBlock block, List<Integer> pushed) {
         while (!workList.isEmpty() || !copySet.isEmpty()) {
             while (!workList.isEmpty()) {
                 final CopyItem copyItem = workList.remove(0);
-                final Register src = copyItem.src;
+                final Operand src = copyItem.src;
                 final Register dest = copyItem.dest;
                 final BasicBlock destBlock = copyItem.destBlock;
                 /* Engineering a Compiler: We can avoid the lost copy
@@ -135,13 +138,18 @@ private void scheduleCopies(BasicBlock block, List<Integer> pushed) {
                     pushed.add(dest.id);
                 }
                 /* Insert a copy operation from map[src] to dest at end of BB */
-                addMoveAtBBEnd(block, map.get(src.id), dest);
-                map.put(src.id, dest);
-                /* If src is the name of a dest in copySet add item to worklist */
-                /* see comment on phi cycles below. */
-                CopyItem item = isCycle(copySet, src);
-                if (item != null) {
-                    workList.add(item);
+                if (src instanceof Operand.RegisterOperand srcRegisterOperand) {
+                    addMoveAtBBEnd(block, map.get(srcRegisterOperand.reg.id), dest);
+                    map.put(srcRegisterOperand.reg.id, dest);
+                    /* If src is the name of a dest in copySet add item to worklist */
+                    /* see comment on phi cycles below. */
+                    CopyItem item = isCycle(copySet, srcRegisterOperand.reg);
+                    if (item != null) {
+                        workList.add(item);
+                    }
+                }
+                else if (src instanceof Operand.ConstantOperand srcConstantOperand) {
+                    addMoveAtBBEnd(block, srcConstantOperand, dest);
                 }
             }
             /* Engineering a Compiler: To solve the swap problem
@@ -212,7 +220,10 @@ private void addMoveAtBBEnd(BasicBlock block, Register src, Register dest) {
         var inst = new Instruction.Move(new Operand.RegisterOperand(src), new Operand.RegisterOperand(dest));
         insertAtEnd(block, inst);
     }
-
+    private void addMoveAtBBEnd(BasicBlock block, Operand.ConstantOperand src, Register dest) {
+        var inst = new Instruction.Move(src, new Operand.RegisterOperand(dest));
+        insertAtEnd(block, inst);
+    }
     /* Insert a copy dest to a new temp at phi node defining dest, return temp */
     private Register addMoveToTempAfterPhi(BasicBlock block, Register dest) {
         var temp = function.registerPool.newTempReg(dest.name(), dest.type);

From cb0213247b32e54e7177b04624a216e791bec454 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <dibyendu@redukti.com>
Date: Sun, 19 Jan 2025 00:43:47 +0000
Subject: [PATCH 018/119] InterferenceGraph: Tentative solution for copy
 coalesce where target is arg

---
 .../ezlang/compiler/InterferenceGraph.java                    | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/InterferenceGraph.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/InterferenceGraph.java
index 6691d06..1714f6b 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/InterferenceGraph.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/InterferenceGraph.java
@@ -63,6 +63,10 @@ public void rename(Integer source, Integer target) {
         // Move all interferences
         var fromSet = edges.remove(source);
         var toSet = edges.get(target);
+        if (toSet == null) {
+            //throw new RuntimeException("Cannot find edge " + target + " from " + source);
+            return; // FIXME this is workaround to handle sceanrio where target is arg register but we need a better way
+        }
         toSet.addAll(fromSet);
         // If any node interfered with from
         // it should now interfere with to

From 5584b4dbc215ad8d72a5d8b246078102952c9beb Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <dibyendu@redukti.com>
Date: Sun, 19 Jan 2025 00:43:57 +0000
Subject: [PATCH 019/119] Enable optimization

---
 .../java/com/compilerprogramming/ezlang/compiler/Optimizer.java | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Optimizer.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Optimizer.java
index f886a4a..8eeebbc 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Optimizer.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Optimizer.java
@@ -4,7 +4,7 @@ public class Optimizer {
 
     public void optimize(CompiledFunction function) {
         new EnterSSA(function);
-        new SparseConditionalConstantPropagation().constantPropagation(function);
+        new SparseConditionalConstantPropagation().constantPropagation(function).apply();
         new ExitSSA(function);
         new ChaitinGraphColoringRegisterAllocator().assignRegisters(function, 64);
     }

From 17629713dc8d24877514a759fff9ba236e00075c Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <dibyendu@redukti.com>
Date: Sun, 19 Jan 2025 15:04:35 +0000
Subject: [PATCH 020/119] Parser bugfix - failed to parse nested expression
 correctly

---
 .../main/java/com/compilerprogramming/ezlang/parser/Parser.java  | 1 -
 1 file changed, 1 deletion(-)

diff --git a/parser/src/main/java/com/compilerprogramming/ezlang/parser/Parser.java b/parser/src/main/java/com/compilerprogramming/ezlang/parser/Parser.java
index de65e19..2249d4d 100644
--- a/parser/src/main/java/com/compilerprogramming/ezlang/parser/Parser.java
+++ b/parser/src/main/java/com/compilerprogramming/ezlang/parser/Parser.java
@@ -348,7 +348,6 @@ private AST.Expr parsePrimary(Lexer lexer) {
             case PUNCT -> {
                 /* Nested expression */
                 matchPunctuation(lexer, "(");
-                nextToken(lexer);
                 var x = parseBool(lexer);
                 matchPunctuation(lexer, ")");
                 return x;

From 9aa77e17e625f536a70086d2155553aad2fd579c Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <dibyendu@redukti.com>
Date: Sun, 19 Jan 2025 15:05:09 +0000
Subject: [PATCH 021/119] Comments and test cases

---
 .../ezlang/compiler/ExitSSA.java              |  24 +-
 .../ezlang/compiler/TestSSATransform.java     | 275 ++++++++++++++++++
 2 files changed, 298 insertions(+), 1 deletion(-)

diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA.java
index 5bac8f8..7cec37a 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA.java
@@ -7,6 +7,9 @@
  * Implementation is based on description in
  * 'Practical Improvements to the Construction and Destruction
  * of Static Single Assignment Form' by Preston Briggs.
+ *
+ * The JikesRVM LeaveSSA implements a version of the
+ * same algorithm.
  */
 public class ExitSSA {
 
@@ -70,8 +73,11 @@ private void replaceUses(Instruction i) {
     }
 
     static class CopyItem {
+        /** Phi input can be a register or a constant so we record the operand */
         final Operand src;
+        /** The phi destination */
         final Register dest;
+        /** The basic block where the phi was present */
         final BasicBlock destBlock;
         boolean removed;
 
@@ -130,11 +136,19 @@ private void scheduleCopies(BasicBlock block, List<Integer> pushed) {
                    a copy target that is live, we must preserve the live
                    value in a temporary name and rewrite subsequent uses to
                    refer to the temporary name.
+
+                   This captures the cases when the result of a phi
+                   in a control successor is live on exit of the current block.
+                   This means that it is incorrect to simply insert a copy
+                   of the destination in the current block. So we rename
+                   the destination to a new temporary, and record the renaming
+                   so that the dominator blocks get the new name. Comment adapted
+                   from JikesRVM LeaveSSA
                  */
                 if (block.liveOut.get(dest.id)) {
                     /* Insert a copy from dest to a new temp t at phi node defining dest */
                     final Register t = addMoveToTempAfterPhi(destBlock, dest);
-                    stacks[dest.id].push(t);
+                    stacks[dest.id].push(t);    // record the temp name
                     pushed.add(dest.id);
                 }
                 /* Insert a copy operation from map[src] to dest at end of BB */
@@ -158,6 +172,13 @@ else if (src instanceof Operand.ConstantOperand srcConstantOperand) {
                cycle of references, it must insert a copy to a temporary
                that breaks the cycle. Then we can schedule the copies to
                respect the dependencies implied by the phi functions.
+
+               An empty work list with work remaining in the copy set
+               implies a cycle in the dependencies amongst copies. To break
+               the cycle copy the destination of an arbitrary member of the
+               copy set to a temporary. This destination has therefore been
+               saved and can be safely overwritten. So then add the copy to the
+               work list. Comment adapted from JikesRVM LeaveSSA.
              */
             if (!copySet.isEmpty()) {
                 CopyItem copyItem = copySet.remove(0);
@@ -220,6 +241,7 @@ private void addMoveAtBBEnd(BasicBlock block, Register src, Register dest) {
         var inst = new Instruction.Move(new Operand.RegisterOperand(src), new Operand.RegisterOperand(dest));
         insertAtEnd(block, inst);
     }
+    /* Insert a copy from constant src to dst at end of BB */
     private void addMoveAtBBEnd(BasicBlock block, Operand.ConstantOperand src, Register dest) {
         var inst = new Instruction.Move(src, new Operand.RegisterOperand(dest));
         insertAtEnd(block, inst);
diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
index d2b4f04..17ff310 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
@@ -1045,5 +1045,280 @@ func foo() {
 
     }
 
+    @Test
+    public void testParallelAssign() {
+        String src = """
+                func foo(n: Int)->Int {
+                   var a = 1
+                   var b = 2
+                   
+                   while (n > 0) {
+                        var t = a
+                        a = b
+                        b = t
+                        n = n - 1
+                   }
+                   return a
+                }
+                """;
+        String result = compileSrc(src);
+        System.out.println(result);
+    }
+
+    @Test
+    public void testSSA1() {
+        String src = """
+                func foo()->Int {
+                    var a = 5
+                    var b = 10
+                    var c = a + b
+                    return c
+                }
+                """;
+        String result = compileSrc(src);
+        System.out.println(result);
+    }
+
+    @Test
+    public void testSSA2() {
+        String src = """
+                func foo()->Int {
+                    var a = 5
+                    a = a + 1;
+                    return a
+                }
+                """;
+        String result = compileSrc(src);
+        System.out.println(result);
+    }
+
+    @Test
+    public void testSSA3() {
+        String src = """
+                func foo()->Int {
+                    var a = 5
+                    if (a > 3) {
+                        a = 10
+                    } else {
+                        a = 20
+                    }
+                    return a
+                }
+                """;
+        String result = compileSrc(src);
+        System.out.println(result);
+    }
+
+    @Test
+    public void testSSA4() {
+        String src = """
+                func foo()->Int {
+                    var a = 0
+                    while (a < 5) {
+                        a = a + 1;
+                    }
+                    return a
+                }
+                """;
+        String result = compileSrc(src);
+        System.out.println(result);
+    }
+
+    @Test
+    public void testSSA5() {
+        String src = """
+                func foo()->Int {
+                    var a = 0
+                    var b = 10
+                    if (b > 5) {
+                        if (a < 5) {
+                            a = 5
+                        } else {
+                            a = 15
+                        }
+                    } else {
+                        a = 20
+                    }
+                    return a
+                }
+                """;
+        String result = compileSrc(src);
+        System.out.println(result);
+    }
+
+    @Test
+    public void testSSA6() {
+        String src = """
+                func foo()->Int {
+                    var arr = new [Int] {1, 2};
+                    arr[0] = 10
+                    var x = arr[0]
+                    return x
+                }
+                """;
+        String result = compileSrc(src);
+        System.out.println(result);
+    }
+
+    @Test
+    public void testSSA7() {
+        String src = """
+        func add(x: Int, y : Int)->Int {
+            return x + y
+        }
+        func main()->Int {
+            var a = 5
+            var b = 10
+            var c = add(a, b)
+            return c
+        }
+""";
+        String result = compileSrc(src);
+        System.out.println(result);
+    }
+
+    @Test
+    public void testSSA8() {
+        String src = """
+        func main()->Int {
+            var a = 0
+            var b = 1
+            while (a < 10) {
+                a = a + 2
+            }
+            while (b < 20) {
+                b = b + 3
+            }
+            return a + b
+        }
+""";
+        String result = compileSrc(src);
+        System.out.println(result);
+    }
+
+    @Test
+    public void testSSA9() {
+        String src = """
+        func main()->Int {
+            var a = 0
+            var b = 0
+            var i = 0
+            var j = 0
+            while (i < 3) {
+                j = 0
+                while (j < 2) {
+                    a = a + 1
+                    i = j + 1
+                }
+                b = b + 1;
+                i = i + 1    
+            }
+            return a + b
+        }
+""";
+        String result = compileSrc(src);
+        System.out.println(result);
+    }
+
+    @Test
+    public void testSSA10() {
+        String src = """
+        func main()->Int {
+            var sum = 0
+            var i = 0
+            var j = 0
+            while (i < 5) {
+                j = 0
+                while (j < 5) {
+                    if (j % 2 == 0) 
+                        sum = sum + j
+                    j = j + 1    
+                }
+                i = i + 1    
+            }
+            return sum
+        }
+""";
+        String result = compileSrc(src);
+        System.out.println(result);
+    }
+
+    @Test
+    public void testSSA11() {
+        String src = """
+        func main()->Int {
+            var a = 0
+            var i = 0
+            var j = 0
+            while (i < 3) {
+                j = 0
+                while (j < 3) {
+                    if (i == j) 
+                        a = a + i + j
+                    else if (i > j)
+                        a = a - 1
+                    j = j + 1        
+                }
+                i = i + 1    
+            }
+            return a
+        }
+""";
+        String result = compileSrc(src);
+        System.out.println(result);
+    }
+
+    @Test
+    public void testSSA12() {
+        String src = """
+        func main()->Int {
+            var count = 0
+            var i = 0
+            var j = 0
+            while (i < 5) {
+                j = 0
+                while (j < 5) {
+                    if (i + j > 5)
+                        break
+                    if (i == j) {
+                        j = j + 1
+                        continue
+                    }
+                    count = count + 1
+                    j = j + 1
+                }
+                i = i + 1
+            }
+            return count
+        }
+""";
+        String result = compileSrc(src);
+        System.out.println(result);
+    }
+
+    @Test
+    public void testSSA13() {
+        String src = """
+        func main()->Int {
+            var outerSum = 0
+            var innerSum = 0
+            var i = 0
+            var j = 0
+            while (i < 4) {
+                j = 0
+                while (j < 4) {
+                    if ((i + j) % 2 == 0)
+                        innerSum = innerSum + j
+                    j = j + 1
+                }
+                outerSum = outerSum + innerSum
+                i = i + 1
+            }
+            return outerSum
+        }
+""";
+        String result = compileSrc(src);
+        System.out.println(result);
+    }
 
 }
\ No newline at end of file

From 4ead605cf602f553e2de3ef928496eed58746788 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Sun, 26 Jan 2025 15:37:18 +0000
Subject: [PATCH 022/119] SCCP: some more test cases Also introduce compiler
 options to control steps / debug info

---
 ...ChaitinGraphColoringRegisterAllocator.java |  12 +-
 .../ezlang/compiler/CompiledFunction.java     |   5 +
 .../ezlang/compiler/Compiler.java             |  16 +-
 .../ezlang/compiler/EnterSSA.java             |   7 +-
 .../ezlang/compiler/ExitSSA.java              |   9 +-
 .../ezlang/compiler/Optimizer.java            |  16 +-
 .../ezlang/compiler/Options.java              |  24 ++
 .../SparseConditionalConstantPropagation.java |   7 +-
 .../compiler/TestChaitinRegAllocator.java     |   2 +-
 .../ezlang/compiler/TestSCCP.java             |   6 +-
 .../ezlang/compiler/TestSSATransform.java     |   9 +-
 .../ezlang/interpreter/TestInterpreter.java   | 269 +++++++++++++++++-
 12 files changed, 350 insertions(+), 32 deletions(-)
 create mode 100644 optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Options.java

diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ChaitinGraphColoringRegisterAllocator.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ChaitinGraphColoringRegisterAllocator.java
index 47a03b9..abdce9f 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ChaitinGraphColoringRegisterAllocator.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ChaitinGraphColoringRegisterAllocator.java
@@ -17,10 +17,10 @@
  */
 public class ChaitinGraphColoringRegisterAllocator {
 
-    public Map<Integer, Integer> assignRegisters(CompiledFunction function, int numRegisters) {
+    public Map<Integer, Integer> assignRegisters(CompiledFunction function, int numRegisters, EnumSet<Options> options) {
         if (function.isSSA) throw new IllegalStateException("Register allocation should be done after exiting SSA");
         // Remove useless copy operations
-        InterferenceGraph g = coalesce(function);
+        InterferenceGraph g = coalesce(function, options);
         // Get used registers
         Set<Integer> registers = registersInIR(function);
         // Create color set
@@ -36,6 +36,8 @@ public Map<Integer, Integer> assignRegisters(CompiledFunction function, int numR
         updateInstructions(function, assignments);
         // Compute and set the new framesize
         function.setFrameSize(computeFrameSize(assignments));
+        if (options.contains(Options.DUMP_POST_CHAITIN_IR))
+            function.dumpIR(false, "Post Chaitin Register Allocation");
         return assignments;
     }
 
@@ -83,13 +85,17 @@ private void updateInstructions(CompiledFunction function, Map<Integer, Integer>
     /**
      * Chaitin: coalesce_nodes - coalesce away copy operations
      */
-    public InterferenceGraph coalesce(CompiledFunction function) {
+    public InterferenceGraph coalesce(CompiledFunction function, EnumSet<Options> options) {
         boolean changed = true;
         InterferenceGraph igraph = null;
         while (changed) {
             igraph = new InterferenceGraphBuilder().build(function);
             changed = coalesceCopyOperations(function, igraph);
         }
+        if (options.contains(Options.DUMP_CHAITIN_COALESCE)) {
+            System.out.println("Post Chaitin Coalesce Registers");
+            System.out.println(function.toStr(new StringBuilder(), false));
+        }
         return igraph;
     }
 
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
index 8cd4c23..a686e8e 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
@@ -566,6 +566,11 @@ public StringBuilder toStr(StringBuilder sb, boolean verbose) {
         return sb;
     }
 
+    public void dumpIR(boolean verbose, String title) {
+        System.out.println(title);
+        System.out.println(toStr(new StringBuilder(), verbose));
+    }
+
     public void livenessAnalysis() {
         new Liveness(this);
         this.hasLiveness = true;
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java
index e390fd1..df64e5c 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java
@@ -8,24 +8,26 @@
 import com.compilerprogramming.ezlang.types.Type;
 import com.compilerprogramming.ezlang.types.TypeDictionary;
 
+import java.util.EnumSet;
+
 public class Compiler {
 
-    private void compile(TypeDictionary typeDictionary, boolean opt) {
+    private void compile(TypeDictionary typeDictionary, EnumSet<Options> options) {
         for (Symbol symbol: typeDictionary.getLocalSymbols()) {
             if (symbol instanceof Symbol.FunctionTypeSymbol functionSymbol) {
                 Type.TypeFunction functionType = (Type.TypeFunction) functionSymbol.type;
                 var function = new CompiledFunction(functionSymbol);
+                if (options.contains(Options.DUMP_INITIAL_IR))
+                    function.dumpIR(false, "Initial IR");
                 functionType.code = function;
-                if (opt) {
-                    new Optimizer().optimize(function);
-                }
+                new Optimizer().optimize(function, options);
             }
         }
     }
     public TypeDictionary compileSrc(String src) {
-        return compileSrc(src, false);
+        return compileSrc(src, EnumSet.noneOf(Options.class));
     }
-    public TypeDictionary compileSrc(String src, boolean opt) {
+    public TypeDictionary compileSrc(String src, EnumSet<Options> options) {
         Parser parser = new Parser();
         var program = parser.parse(new Lexer(src));
         var typeDict = new TypeDictionary();
@@ -33,7 +35,7 @@ public TypeDictionary compileSrc(String src, boolean opt) {
         sema.analyze(program);
         var sema2 = new SemaAssignTypes(typeDict);
         sema2.analyze(program);
-        compile(typeDict, opt);
+        compile(typeDict, options);
         return typeDict;
     }
     public static String dumpIR(TypeDictionary typeDictionary) {
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/EnterSSA.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/EnterSSA.java
index 9365c82..13a8556 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/EnterSSA.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/EnterSSA.java
@@ -33,16 +33,21 @@ public class EnterSSA {
     int[] counters;
     VersionStack[] stacks;
 
-    public EnterSSA(CompiledFunction bytecodeFunction) {
+    public EnterSSA(CompiledFunction bytecodeFunction, EnumSet<Options> options) {
         this.function = bytecodeFunction;
         setupGlobals();
         computeDomTreeAndDominanceFrontiers();
+        if (options.contains(Options.DUMP_PRE_SSA_DOMTREE)) {
+            System.out.println("Pre SSA Dominator Tree");
+            System.out.println(domTree.generateDotOutput());
+        }
         this.blocks = domTree.blocks;   // the blocks are ordered reverse post order
         findNonLocalNames();
         insertPhis();
         renameVars();
         bytecodeFunction.isSSA = true;
         bytecodeFunction.hasLiveness = false;
+        if (options.contains(Options.DUMP_SSA_IR)) bytecodeFunction.dumpIR(false, "Post SSA IR");
     }
 
     private void computeDomTreeAndDominanceFrontiers() {
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA.java
index 7cec37a..745e181 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA.java
@@ -17,16 +17,21 @@ public class ExitSSA {
     NameStack[] stacks;
     DominatorTree tree;
 
-    public ExitSSA(CompiledFunction function) {
+    public ExitSSA(CompiledFunction function, EnumSet<Options> options) {
         this.function = function;
         if (!function.isSSA) throw new IllegalStateException();
         function.livenessAnalysis();
-        System.out.println(function.toStr(new StringBuilder(), true));
+        if (options.contains(Options.DUMP_SSA_LIVENESS)) function.dumpIR(true, "SSA Liveness Analysis");
         tree = new DominatorTree(function.entry);
+        if (options.contains(Options.DUMP_SSA_DOMTREE)) {
+            System.out.println("Pre SSA Dominator Tree");
+            System.out.println(tree.generateDotOutput());
+        }
         initStack();
         insertCopies(function.entry);
         removePhis();
         function.isSSA = false;
+        if (options.contains(Options.DUMP_POST_SSA_IR)) function.dumpIR(false, "After exiting SSA");
     }
 
     private void removePhis() {
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Optimizer.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Optimizer.java
index 8eeebbc..54a63a8 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Optimizer.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Optimizer.java
@@ -1,11 +1,17 @@
 package com.compilerprogramming.ezlang.compiler;
 
+import java.util.EnumSet;
+
 public class Optimizer {
 
-    public void optimize(CompiledFunction function) {
-        new EnterSSA(function);
-        new SparseConditionalConstantPropagation().constantPropagation(function).apply();
-        new ExitSSA(function);
-        new ChaitinGraphColoringRegisterAllocator().assignRegisters(function, 64);
+    public void optimize(CompiledFunction function, EnumSet<Options> options) {
+        if (options.contains(Options.OPTIMIZE)) {
+            new EnterSSA(function, options);
+            if (options.contains(Options.SCCP))
+                new SparseConditionalConstantPropagation().constantPropagation(function).apply(options);
+            new ExitSSA(function, options);
+        }
+        if (options.contains(Options.REGALLOC))
+            new ChaitinGraphColoringRegisterAllocator().assignRegisters(function, 64, options);
     }
 }
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Options.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Options.java
new file mode 100644
index 0000000..06a0c42
--- /dev/null
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Options.java
@@ -0,0 +1,24 @@
+package com.compilerprogramming.ezlang.compiler;
+
+import java.util.EnumSet;
+
+public enum Options {
+    OPTIMIZE,
+    SCCP,
+    REGALLOC,
+    DUMP_INITIAL_IR,
+    DUMP_PRE_SSA_DOMTREE,
+    DUMP_SSA_IR,
+    DUMP_SCCP_PREAPPLY,
+    DUMP_SCCP_POSTAPPLY,
+    DUMP_SSA_LIVENESS,
+    DUMP_SSA_DOMTREE,
+    DUMP_POST_SSA_IR,
+    DUMP_INTERFERENCE_GRAPH,
+    DUMP_CHAITIN_COALESCE,
+    DUMP_POST_CHAITIN_IR;
+
+    public static final EnumSet<Options> NONE = EnumSet.noneOf(Options.class);
+    public static final EnumSet<Options> OPT = EnumSet.of(Options.OPTIMIZE,Options.SCCP,Options.REGALLOC);
+    public static final EnumSet<Options> OPT_VERBOSE = EnumSet.allOf(Options.class);
+}
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SparseConditionalConstantPropagation.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SparseConditionalConstantPropagation.java
index a8e77e3..92f5fd6 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SparseConditionalConstantPropagation.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SparseConditionalConstantPropagation.java
@@ -120,7 +120,7 @@ private void init(CompiledFunction function) {
         visited = new BitSet();
     }
 
-    public SparseConditionalConstantPropagation apply() {
+    public SparseConditionalConstantPropagation apply(EnumSet<Options> options) {
         /*
         The constant propagation algorithm does not change the flow graph - it computes
         information about the flow graph. The compiler now uses this information to improve
@@ -137,10 +137,15 @@ public SparseConditionalConstantPropagation apply() {
 
          Bob Morgan. Building an Optimizing Compiler
          */
+        if (options.contains(Options.DUMP_SCCP_PREAPPLY)) {
+            System.out.println("SCCP analysis\n");
+            System.out.println(toString());
+        }
         markExecutableBlocks();
         removeBranchesThatAreNotExecutable();
         replaceVarsWithConstants();
         // Unreachable blocks are eliminated as there are no paths to them
+        if (options.contains(Options.DUMP_SCCP_POSTAPPLY)) function.dumpIR(false, "Post SCCP\n");
         return this;
     }
 
diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestChaitinRegAllocator.java b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestChaitinRegAllocator.java
index 4197e1f..e279cef 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestChaitinRegAllocator.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestChaitinRegAllocator.java
@@ -15,7 +15,7 @@ public void test4() {
         Assert.assertEquals(2, edges.size());
         Assert.assertTrue(edges.contains(new InterferenceGraph.Edge(0, 1)));
         Assert.assertTrue(edges.contains(new InterferenceGraph.Edge(0, 2)));
-        var regAssignments = new ChaitinGraphColoringRegisterAllocator().assignRegisters(function, 64);
+        var regAssignments = new ChaitinGraphColoringRegisterAllocator().assignRegisters(function, 64, Options.OPT);
         String result = function.toStr(new StringBuilder(), false).toString();
         Assert.assertEquals("""
 L0:
diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSCCP.java b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSCCP.java
index 530fbd9..bd25216 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSCCP.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSCCP.java
@@ -5,6 +5,7 @@
 import org.junit.Test;
 
 import java.util.BitSet;
+import java.util.EnumSet;
 
 public class TestSCCP {
 
@@ -12,15 +13,16 @@ String compileSrc(String src) {
         var compiler = new Compiler();
         var typeDict = compiler.compileSrc(src);
         StringBuilder sb = new StringBuilder();
+        var options = Options.NONE;
         for (Symbol s : typeDict.bindings.values()) {
             if (s instanceof Symbol.FunctionTypeSymbol f) {
                 var functionBuilder = (CompiledFunction) f.code();
-                new EnterSSA(functionBuilder);
+                new EnterSSA(functionBuilder, options);
                 BasicBlock.toStr(sb, functionBuilder.entry, new BitSet(), false);
                 //functionBuilder.toDot(sb, false);
                 var sccp = new SparseConditionalConstantPropagation().constantPropagation(functionBuilder);
                 sb.append(sccp.toString());
-                sccp.apply();
+                sccp.apply(options);
                 sb.append("After SCCP changes:\n");
                 functionBuilder.toStr(sb, false);
             }
diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
index 17ff310..27387b2 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
@@ -9,6 +9,7 @@
 
 import java.util.Arrays;
 import java.util.BitSet;
+import java.util.EnumSet;
 
 public class TestSSATransform {
 
@@ -23,11 +24,11 @@ String compileSrc(String src) {
                 sb.append("Before SSA\n");
                 sb.append("==========\n");
                 BasicBlock.toStr(sb, functionBuilder.entry, new BitSet(), false);
-                new EnterSSA(functionBuilder);
+                new EnterSSA(functionBuilder, Options.NONE);
                 sb.append("After SSA\n");
                 sb.append("=========\n");
                 BasicBlock.toStr(sb, functionBuilder.entry, new BitSet(), false);
-                new ExitSSA(functionBuilder);
+                new ExitSSA(functionBuilder, Options.NONE);
                 sb.append("After exiting SSA\n");
                 sb.append("=================\n");
                 BasicBlock.toStr(sb, functionBuilder.entry, new BitSet(), false);
@@ -680,7 +681,7 @@ public void testLostCopyProblem() {
     ret x2
 """;
         Assert.assertEquals(expected, function.toStr(new StringBuilder(), false).toString());
-        new ExitSSA(function);
+        new ExitSSA(function, EnumSet.noneOf(Options.class));
         expected = """
 L0:
     arg p
@@ -748,7 +749,7 @@ public void testSwapProblem() {
 L1:
 """;
         Assert.assertEquals(expected, function.toStr(new StringBuilder(), false).toString());
-        new ExitSSA(function);
+        new ExitSSA(function, EnumSet.noneOf(Options.class));
         expected = """
 L0:
     arg p
diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java b/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java
index 5d49505..2ccaca5 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java
@@ -1,17 +1,20 @@
 package com.compilerprogramming.ezlang.interpreter;
 
 import com.compilerprogramming.ezlang.compiler.Compiler;
+import com.compilerprogramming.ezlang.compiler.Options;
 import org.junit.Assert;
 import org.junit.Test;
 
+import java.util.EnumSet;
+
 public class TestInterpreter {
 
     Value compileAndRun(String src, String mainFunction) {
-        return compileAndRun(src, mainFunction, false);
+        return compileAndRun(src, mainFunction, Options.NONE);
     }
-    Value compileAndRun(String src, String mainFunction, boolean opt) {
+    Value compileAndRun(String src, String mainFunction, EnumSet<Options> options) {
         var compiler = new Compiler();
-        var typeDict = compiler.compileSrc(src, opt);
+        var typeDict = compiler.compileSrc(src, options);
         var compiled = compiler.dumpIR(typeDict);
         System.out.println(compiled);
         var interpreter = new Interpreter(typeDict);
@@ -140,7 +143,7 @@ func foo()->Int {
                     return factorial(5);
                 }
                 """;
-        var value = compileAndRun(src, "foo", true);
+        var value = compileAndRun(src, "foo", Options.OPT);
         Assert.assertNotNull(value);
         Assert.assertTrue(value instanceof Value.IntegerValue integerValue
                 && integerValue.value == 120);
@@ -168,7 +171,7 @@ func foo() {
                     return fib(10);
                 }
                 """;
-        var value = compileAndRun(src, "foo", true);
+        var value = compileAndRun(src, "foo", Options.OPT);
         Assert.assertNotNull(value);
         Assert.assertTrue(value instanceof Value.IntegerValue integerValue
                 && integerValue.value == 89);
@@ -183,9 +186,263 @@ func foo()->Int {
                     return 3;
                 }
                 """;
-        var value = compileAndRun(src, "foo", true);
+        var value = compileAndRun(src, "foo", Options.OPT);
         Assert.assertNotNull(value);
         Assert.assertTrue(value instanceof Value.IntegerValue integerValue
                 && integerValue.value == 2);
     }
+
+    // 4.1.2 optimizer evaluation
+    // propagating through expressions
+    @Test
+    public void testFunction11() {
+        String src = """
+                func bar(data: [Int]) {
+                    var j = 1
+                    var k = 2
+                    var m = 4
+                    var n = k * m
+                    j = n + j
+                    data[0] = j
+                }
+                func foo()->Int {
+                    var data = new [Int] {0}
+                    bar(data)
+                    return data[0]
+                }
+                """;
+        var value = compileAndRun(src, "foo", Options.OPT);
+        Assert.assertNotNull(value);
+        Assert.assertTrue(value instanceof Value.IntegerValue integerValue
+                && integerValue.value == 9);
+    }
+
+    // 4.2.1 optimizer evaluation
+    // extended basic blocks
+    @Test
+    public void testFunction12() {
+        String src = """
+                func bar(data: [Int]) {
+                    var j = 12345
+                    if (data[0])
+                        data[1] = 1 + j - 1234
+                    else
+                        data[2] = 123 + j + 10
+                }
+                func foo() {
+                    var data = new [Int] {0,0,0}
+                    bar(data)
+                    return data[0]+data[1]+data[2];
+                }
+                """;
+        var value = compileAndRun(src, "foo", Options.OPT);
+        Assert.assertNotNull(value);
+        Assert.assertTrue(value instanceof Value.IntegerValue integerValue
+                && integerValue.value == 12478);
+    }
+
+    // 4.2.1 optimizer evaluation
+    // extended basic blocks
+    @Test
+    public void testFunction13() {
+        String src = """
+                func bar(data: [Int]) {
+                    var j = 12345
+                    if (data[0])
+                        data[1] = 1 + j - 1234
+                    else
+                        data[2] = 123 - j + 10
+                }
+                func foo() {
+                    var data = new [Int] {0,0,0}
+                    bar(data)
+                    return data[0]+data[1]+data[2];
+                }
+                """;
+        var value = compileAndRun(src, "foo", Options.OPT);
+        Assert.assertNotNull(value);
+        Assert.assertTrue(value instanceof Value.IntegerValue integerValue
+                && integerValue.value == -12212);
+    }
+
+    // 4.2.2 dominators
+    @Test
+    public void testFunction14() {
+        String src = """
+                func bar(data: [Int]) {
+                    var j = 5
+                    if (data[0])
+                        data[1] = 10
+                    else
+                        data[2] = 15
+                    data[3] = j + 21    
+                }
+                func foo() {
+                    var data = new [Int] {0,0,0,0}
+                    bar(data)
+                    return data[0]+data[1]+data[2]+data[3];
+                }
+                """;
+        var value = compileAndRun(src, "foo", Options.OPT);
+        Assert.assertNotNull(value);
+        Assert.assertTrue(value instanceof Value.IntegerValue integerValue
+                && integerValue.value == 5+21+15);
+    }
+
+    // 4.2.2 dominators
+    @Test
+    public void testFunction15() {
+        String src = """
+                func bar(data: [Int]) {
+                    var j = 5
+                    if (data[0])
+                        data[1] = j * 10
+                    else
+                        data[2] = j * 15
+                    data[3] = j * 21    
+                }
+                func foo() {
+                    var data = new [Int] {0,0,0,0}
+                    bar(data)
+                    return data[0]+data[1]+data[2]+data[3];
+                }
+                """;
+        var value = compileAndRun(src, "foo", Options.OPT);
+        Assert.assertNotNull(value);
+        Assert.assertTrue(value instanceof Value.IntegerValue integerValue
+                && integerValue.value == 5*15+5*21);
+    }
+
+    // 4.2.3 DAGs
+    @Test
+    public void testFunction16() {
+        String src = """
+                func bar(data: [Int]) {
+                    var j: Int
+                    if (data[0]) {
+                        j = 5
+                        data[1] = 10
+                    }
+                    else {
+                        data[2] = 15
+                        j = 5
+                    }
+                    data[3] = j + 21
+                }
+                func foo() {
+                    var data = new [Int] {1,0,0,0}
+                    bar(data)
+                    return data[0]+data[1]+data[2]+data[3]
+                }
+                """;
+        var value = compileAndRun(src, "foo", Options.OPT);
+        Assert.assertNotNull(value);
+        Assert.assertTrue(value instanceof Value.IntegerValue integerValue
+                && integerValue.value == 1+10+5+21);
+    }
+
+    // 4.2.3 DAGs
+    // Doesn't give the outcome
+    @Test
+    public void testFunction17() {
+        String src = """
+                func bar(data: [Int]) {
+                    var j: Int
+                    var k: Int
+                    if (data[0]) {
+                        j = 4
+                        k = 6
+                        data[1] = j
+                    }
+                    else {
+                        j = 7
+                        k = 3
+                        data[2] = k
+                    }
+                    data[3] = (j+k) * 21
+                }
+                func foo() {
+                    var data = new [Int] {1,0,0,0}
+                    bar(data)
+                    return data[0]+data[1]+data[2]+data[3]
+                }
+                """;
+        var value = compileAndRun(src, "foo", Options.OPT);
+        Assert.assertNotNull(value);
+        Assert.assertTrue(value instanceof Value.IntegerValue integerValue
+                && integerValue.value == 1+4+210);
+    }
+
+    // 4.2.4 Loops
+    @Test
+    public void testFunction18() {
+        String src = """
+                func bar(data: [Int]) {
+                    var i: Int
+                    var stop = data[0]
+                    var j = 21
+                    i = 1
+                    while ( i < stop ) {
+                        j = (j - 20) * 21;
+                        i = i + 1
+                    }
+                    data[1] = j
+                    data[2] = i
+                }
+                func foo() {
+                    var data = new [Int] {2,0,0}
+                    bar(data)
+                    return data[0]+data[1]+data[2];
+                }
+                """;
+        var value = compileAndRun(src, "foo", Options.OPT);
+        Assert.assertNotNull(value);
+        Assert.assertTrue(value instanceof Value.IntegerValue integerValue
+                && integerValue.value == 2+21+2);
+    }
+
+    // 4.3 Conditional constants
+    @Test
+    public void testFunction19() {
+        String src = """
+                func bar(data: [Int]) {
+                    var j = 1
+                    if (j) j = 10
+                    else j = data[0]
+                    data[0] = j * 21 + data[1]
+                }
+                func foo() {
+                    var data = new [Int] {2,3}
+                    bar(data)
+                    return data[0]+data[1];
+                }
+                """;
+        var value = compileAndRun(src, "foo", Options.OPT);
+        Assert.assertNotNull(value);
+        Assert.assertTrue(value instanceof Value.IntegerValue integerValue
+                && integerValue.value == 213+3);
+    }
+
+    // 4.4 Conditional based assertions
+    @Test
+    public void testFunction20() {
+        String src = """
+                func bar(data: [Int]) {
+                    var j = data[0]
+                    if (j == 5)
+                        j = j * 21 + 25 / j
+                    data[1] = j
+                }
+                func foo() {
+                    var data = new [Int] {5,3}
+                    bar(data)
+                    return data[0]+data[1];
+                }
+                """;
+        var value = compileAndRun(src, "foo", Options.OPT);
+        Assert.assertNotNull(value);
+        Assert.assertTrue(value instanceof Value.IntegerValue integerValue
+                && integerValue.value == 5+(5*21+25/5));
+    }
+
 }

From 1232c4928deba9c21f8c6b43af06efd6b0988bff Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Sat, 15 Feb 2025 14:29:36 +0000
Subject: [PATCH 023/119] Fix bug - use correct integer ID for registers when
 creating pre-assigned set

---
 .../compiler/ChaitinGraphColoringRegisterAllocator.java  | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ChaitinGraphColoringRegisterAllocator.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ChaitinGraphColoringRegisterAllocator.java
index abdce9f..a2a4840 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ChaitinGraphColoringRegisterAllocator.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ChaitinGraphColoringRegisterAllocator.java
@@ -21,7 +21,7 @@ public Map<Integer, Integer> assignRegisters(CompiledFunction function, int numR
         if (function.isSSA) throw new IllegalStateException("Register allocation should be done after exiting SSA");
         // Remove useless copy operations
         InterferenceGraph g = coalesce(function, options);
-        // Get used registers
+        // Get used registers, indexed by reg.id
         Set<Integer> registers = registersInIR(function);
         // Create color set
         List<Integer> colors = new ArrayList<>(IntStream.range(0, numRegisters).boxed().toList());
@@ -59,9 +59,9 @@ private Map<Integer, Integer> preAssignArgsToColors(CompiledFunction function, S
         for (Instruction instruction : function.entry.instructions) {
             if (instruction instanceof Instruction.ArgInstruction argInstruction) {
                 Integer color = colors.get(count);
-                Register reg = argInstruction.arg().reg;
-                registers.remove(reg.nonSSAId());   // Remove register from set before changing slot
-                assignments.put(reg.nonSSAId(), color);
+                int reg = argInstruction.arg().reg.id;
+                registers.remove(reg);   // Remove register from set before changing slot
+                assignments.put(reg, color);
                 count++;
             }
             else break;
@@ -142,6 +142,7 @@ private void rewriteInstructions(CompiledFunction function, Instruction deadInst
 
     /**
      * Get the list of registers in use in the Intermediate Code
+     * Indexed by reg.id
      * Chaitin: registers_in_il()
      */
     private Set<Integer> registersInIR(CompiledFunction function) {

From 111a8fb3ac73abd8aa3f9afa4f848c51bb1c5ef4 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Sat, 15 Feb 2025 14:33:56 +0000
Subject: [PATCH 024/119] Fix bug - instruction.block not being set

---
 .../ezlang/compiler/BasicBlock.java                  | 11 +++++++++--
 .../compilerprogramming/ezlang/compiler/ExitSSA.java |  4 ++--
 .../ezlang/compiler/Instruction.java                 |  5 +++--
 .../SparseConditionalConstantPropagation.java        | 12 +++++++++---
 4 files changed, 23 insertions(+), 9 deletions(-)

diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/BasicBlock.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/BasicBlock.java
index bb82949..80c8193 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/BasicBlock.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/BasicBlock.java
@@ -98,6 +98,14 @@ public void add(Instruction instruction) {
         instructions.add(instruction);
         instruction.block = this;
     }
+    public void add(int pos, Instruction instruction) {
+        instructions.add(pos, instruction);
+        instruction.block = this;
+    }
+    public void update(int pos, Instruction instruction) {
+       instructions.set(pos, instruction);
+       instruction.block = this;
+    }
     public void deleteInstruction(Instruction instruction) {
         instructions.remove(instruction);
     }
@@ -129,8 +137,7 @@ public void insertPhiFor(Register var) {
         for (int i = 0; i < predecessors.size(); i++)
             inputs.add(var);
         Instruction.Phi phi = new Instruction.Phi(var, inputs);
-        instructions.add(0, phi);
-        phi.block = this;
+        add(0, phi);
     }
     public List<Instruction.Phi> phis() {
         List<Instruction.Phi> list = new ArrayList<>();
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA.java
index 745e181..0828f98 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA.java
@@ -200,7 +200,7 @@ private void insertAtEnd(BasicBlock bb, Instruction i) {
         // Last instruction is a branch - so new instruction will
         // go before that
         int pos = bb.instructions.size()-1;
-        bb.instructions.add(pos, i);
+        bb.add(pos, i);
     }
 
     private void insertAfterPhi(BasicBlock bb, Register phiDef, Instruction newInst) {
@@ -218,7 +218,7 @@ private void insertAfterPhi(BasicBlock bb, Register phiDef, Instruction newInst)
         if (insertionPos < 0) {
             throw new IllegalStateException();
         }
-        bb.instructions.add(insertionPos, newInst);
+        bb.add(insertionPos, newInst);
     }
 
     /* Insert a copy from dest to new temp at end of BB, and return temp */
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java
index 20f70a2..c4005a7 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java
@@ -31,13 +31,13 @@ public abstract class Instruction {
     protected Operand[] uses;
     public BasicBlock block;
 
-    public Instruction(int opcode, Operand... uses) {
+    protected Instruction(int opcode, Operand... uses) {
         this.opcode = opcode;
         this.def = null;
         this.uses = new Operand[uses.length];
         System.arraycopy(uses, 0, this.uses, 0, uses.length);
     }
-    public Instruction(int opcode, Operand.RegisterOperand def, Operand... uses) {
+    protected Instruction(int opcode, Operand.RegisterOperand def, Operand... uses) {
         this.opcode = opcode;
         this.def = def;
         this.uses = new Operand[uses.length];
@@ -373,6 +373,7 @@ public Register inputAsRegister(int i) {
         public Operand input(int i) {
             return uses[i];
         }
+        public int numInputs() { return uses.length; }
         public boolean isRegisterInput(int i) {
             return uses[i] instanceof Operand.RegisterOperand;
         }
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SparseConditionalConstantPropagation.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SparseConditionalConstantPropagation.java
index 92f5fd6..73a04fc 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SparseConditionalConstantPropagation.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SparseConditionalConstantPropagation.java
@@ -183,7 +183,7 @@ private void removeEdge(BasicBlock source, BasicBlock target) {
         Instruction instruction = source.instructions.get(idx);
         if (instruction instanceof Instruction.ConditionalBranch cbr) {
             BasicBlock remainingExecutableBlock = (cbr.falseBlock == target) ? cbr.trueBlock : cbr.falseBlock;
-            source.instructions.set(idx, new Instruction.Jump(remainingExecutableBlock));
+            source.update(idx, new Instruction.Jump(remainingExecutableBlock));
         }
         // Remove phis in target corresponding to the input
         for (var phi: target.phis()) {
@@ -318,6 +318,7 @@ public String toString() {
      */
     private boolean evalInstruction(Instruction instruction) {
         BasicBlock block = instruction.block;
+        assert block != null;
         boolean changed = false;
         switch (instruction) {
             case Instruction.Ret retInst -> {
@@ -448,8 +449,13 @@ private boolean visitPhi(BasicBlock block, Instruction.Phi phiInst) {
             BasicBlock pred = block.predecessors.get(j);
             // We ignore non-executable edges
             if (isEdgeExecutable(pred, block)) {
-                LatticeElement varValue = valueLattice.get(phiInst.inputAsRegister(j));
-                newValue.meet(varValue);
+                if (phiInst.isRegisterInput(j)) {
+                    LatticeElement varValue = valueLattice.get(phiInst.inputAsRegister(j));
+                    newValue.meet(varValue);
+                }
+                else if (phiInst.input(j) instanceof Operand.ConstantOperand constantOperand) {
+                    newValue.meet(constantOperand.value);
+                }
             }
         }
         return oldValue.meet(newValue);

From 7e320f7b491895f858c287a6ec82112d834044da Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Sat, 15 Feb 2025 14:52:05 +0000
Subject: [PATCH 025/119] New pass that detects == operations against constant
 value in conditional branches - and uses the knowledge to propagate the
 constant value in the true branch.

---
 .../ConstantComparisonPropagation.java        | 152 ++++++++++++++++++
 .../ezlang/compiler/Optimizer.java            |   8 +-
 .../ezlang/compiler/Options.java              |   4 +-
 .../ezlang/compiler/SSAEdges.java             |  12 ++
 4 files changed, 174 insertions(+), 2 deletions(-)
 create mode 100644 optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ConstantComparisonPropagation.java

diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ConstantComparisonPropagation.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ConstantComparisonPropagation.java
new file mode 100644
index 0000000..f3ef77c
--- /dev/null
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ConstantComparisonPropagation.java
@@ -0,0 +1,152 @@
+package com.compilerprogramming.ezlang.compiler;
+
+import java.util.EnumSet;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * The goal of this pass is to detect conditional branching based
+ * on comparison with a constant. Example
+ *
+ * <pre>
+ *     if (i == 5)
+ *     {
+ *         // some use of i
+ *     }
+ * </pre>
+ *
+ * SCCP generates a lattice value for i, and if i changes outside the if block, then
+ * it is not classed as a constant. This means that inside the if block, we cannot exploit
+ * the knowledge that i is a constant locally.
+ *
+ * To enable this, we need to detect such comparisons and then insert a temp
+ * variable which is set to the constant value. The new temp variable is inserted
+ * inside the if block only and does not affect the meaning of i outside the if block.
+ * After this transformation we can run SCCP again to take advantage of the local
+ * knowledge.
+ *
+ * This technique can be extended to null checks too, but we do not do that yet.
+ */
+public class ConstantComparisonPropagation {
+
+    private final CompiledFunction function;
+    private DominatorTree domTree;
+    private Map<Register, SSAEdges.SSADef> ssaDefUse;
+    private boolean updated = false;
+
+    public ConstantComparisonPropagation(CompiledFunction function) {
+        this.function = function;
+    }
+
+    public boolean apply(EnumSet<Options> options) {
+        if (options.contains(Options.CCP)) {
+            updated = false;
+            domTree = new DominatorTree(function.entry);
+            ssaDefUse = SSAEdges.buildDefUseChains(function);
+            walkBlocks();
+        }
+        return updated;
+    }
+
+    private void walkBlocks() {
+        walkBlock(function.entry);
+    }
+
+    private void walkBlock(BasicBlock block) {
+        propagateConstantsInComparisons(block);
+        for (BasicBlock c : block.dominatedChildren) {
+            walkBlock(c);
+        }
+    }
+
+    private void propagateConstantsInComparisons(BasicBlock block) {
+        if (block == function.exit) return;
+        // Get terminal instruction
+        Instruction instruction = block.instructions.getLast();
+        if (instruction instanceof Instruction.ConditionalBranch cbr) {
+            if (cbr.condition() instanceof Operand.RegisterOperand conditionVar) {
+                SSAEdges.SSADef defUse = ssaDefUse.get(conditionVar.reg);
+                // If the condition var was result of == with constant value
+                if (defUse.instruction.block.bid == block.bid
+                        && defUse.instruction instanceof Instruction.Binary binary
+                        && binary.binOp.equals("==")) {
+                    // Get the constant and the register operands
+                    // from the binary
+                    Operand.ConstantOperand constantOp = null;
+                    Operand.RegisterOperand registerOp = null;
+                    if (binary.left() instanceof Operand.ConstantOperand leftConstant
+                            && binary.right() instanceof Operand.RegisterOperand rightReg) {
+                        constantOp = leftConstant;
+                        registerOp = rightReg;
+                    } else if (binary.right() instanceof Operand.ConstantOperand rightConstant
+                            && binary.left() instanceof Operand.RegisterOperand leftReg) {
+                        constantOp = rightConstant;
+                        registerOp = leftReg;
+                    }
+                    if (constantOp != null) {
+                        // Since reg is constant in true branch
+                        // We can replace all uses of reg with the constant
+                        // in blocks dominated by the true block
+                        BasicBlock trueBlock = cbr.trueBlock;
+                        // I am not sure if this scenario can occur  but
+                        // for safety we check that the register we will
+                        // replace is not immediately used inside the true block
+                        // in a phi, because we intend to add the definition of
+                        // the replacement after any phis
+                        if (!checkUsedInPhi(trueBlock, registerOp.reg)) {
+                            // Create a temp and move constant to it.
+                            // We insert the new instruction at the top of the True Block,
+                            // where it should dominate all uses of it
+                            // We could just replace with a  constant here instead of creating
+                            // a temp and a move instruction but that would be less general a solution as it
+                            // would not work for other scenarios such as null status which we will
+                            // be adding in future
+                            var replacementRegister = function.registerPool.newTempReg(registerOp.reg.type);
+                            var defInst = new Instruction.Move(constantOp, new Operand.TempRegisterOperand(replacementRegister));
+                            insertAtBeginning(trueBlock, defInst);
+                            var replacementRegisterUses = SSAEdges.addDef(ssaDefUse, replacementRegister, defInst); // Update SSA Def Use chains, add def for new reg
+                            Iterator<Instruction> useIter = ssaDefUse.get(registerOp.reg).useList.iterator();
+                            while (useIter.hasNext()) {
+                                Instruction use = useIter.next();
+                                if (trueBlock.dominates(use.block)) {
+                                    use.replaceUse(registerOp.reg, replacementRegister);
+                                    // Update SSA Def use chains
+                                    useIter.remove(); // No longer a use of old register
+                                    replacementRegisterUses.addUse(use); // Use of new temp register
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /* Check if the register is used in a phi instruction within the block */
+    private static boolean checkUsedInPhi(BasicBlock block, Register register) {
+        for (Instruction instruction : block.instructions) {
+            if (instruction instanceof Instruction.Phi phi) {
+                for (int i = 0; i < phi.numInputs(); i++) {
+                    if (phi.isRegisterInput(i)) {
+                        Register phiUse = phi.inputAsRegister(i);
+                        if (phiUse.equals(register)) return true;
+                    }
+                }
+            }
+            else break;
+        }
+        return false;
+    }
+
+    /* Insert instruction at the start of BB after any phis */
+    private static void insertAtBeginning(BasicBlock block, Instruction instruction) {
+        int pos = 0; // insertion point
+        for (; pos < block.instructions.size(); pos++) {
+            if (!(block.instructions.get(pos) instanceof Instruction.Phi))
+                break;
+        }
+        if (pos == block.instructions.size())
+            throw new IllegalStateException();
+        block.add(pos, instruction);
+    }
+}
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Optimizer.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Optimizer.java
index 54a63a8..c9e21d8 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Optimizer.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Optimizer.java
@@ -7,8 +7,14 @@ public class Optimizer {
     public void optimize(CompiledFunction function, EnumSet<Options> options) {
         if (options.contains(Options.OPTIMIZE)) {
             new EnterSSA(function, options);
-            if (options.contains(Options.SCCP))
+            if (options.contains(Options.SCCP)) {
                 new SparseConditionalConstantPropagation().constantPropagation(function).apply(options);
+                if (new ConstantComparisonPropagation(function).apply(options)) {
+                    // Run SCCP again
+                    // We could repeat this until no further changes occur
+                    new SparseConditionalConstantPropagation().constantPropagation(function).apply(options);
+                }
+            }
             new ExitSSA(function, options);
         }
         if (options.contains(Options.REGALLOC))
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Options.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Options.java
index 06a0c42..56280e2 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Options.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Options.java
@@ -5,12 +5,14 @@
 public enum Options {
     OPTIMIZE,
     SCCP,
+    CCP, // constant comparison propagation
     REGALLOC,
     DUMP_INITIAL_IR,
     DUMP_PRE_SSA_DOMTREE,
     DUMP_SSA_IR,
     DUMP_SCCP_PREAPPLY,
     DUMP_SCCP_POSTAPPLY,
+    DUMP_CCP_POSTAPPLY,
     DUMP_SSA_LIVENESS,
     DUMP_SSA_DOMTREE,
     DUMP_POST_SSA_IR,
@@ -19,6 +21,6 @@ public enum Options {
     DUMP_POST_CHAITIN_IR;
 
     public static final EnumSet<Options> NONE = EnumSet.noneOf(Options.class);
-    public static final EnumSet<Options> OPT = EnumSet.of(Options.OPTIMIZE,Options.SCCP,Options.REGALLOC);
+    public static final EnumSet<Options> OPT = EnumSet.of(Options.OPTIMIZE,Options.SCCP,Options.CCP,Options.REGALLOC);
     public static final EnumSet<Options> OPT_VERBOSE = EnumSet.allOf(Options.class);
 }
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SSAEdges.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SSAEdges.java
index ed8b8ea..e6fcdcb 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SSAEdges.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SSAEdges.java
@@ -28,6 +28,10 @@ public SSADef(Instruction instruction) {
             this.instruction = instruction;
             this.useList = new ArrayList<>();
         }
+
+        public void addUse(Instruction instruction) {
+            useList.add(instruction);
+        }
     }
 
     public static Map<Register, SSADef> buildDefUseChains(CompiledFunction function) {
@@ -41,6 +45,14 @@ public static Map<Register, SSADef> buildDefUseChains(CompiledFunction function)
         return defUseChains;
     }
 
+    public static SSADef addDef(Map<Register, SSADef> defUseChains, Register register, Instruction instruction) {
+        if (defUseChains.get(register) != null)
+            throw new CompilerException("Duplicate definition for register " + register);
+        var ssaDef = new SSADef(instruction);
+        defUseChains.put(register, ssaDef);
+        return ssaDef;
+    }
+
     private static void recordDefs(CompiledFunction function, Map<Register, SSADef> defUseChains) {
         for (BasicBlock block : function.getBlocks()) {
             for (Instruction instruction : block.instructions) {

From 1256402247f231f1f62d221b6af134510cd1b439 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Sat, 15 Feb 2025 20:48:40 +0000
Subject: [PATCH 026/119] Cleanup types a bit - arrays do not have the second
 type anymore. Any is now Unknown.

---
 .../ezlang/compiler/TestCompiler.java         |  4 +--
 .../ezlang/compiler/TestCompiler.java         |  4 +--
 .../ezlang/semantic/SemaAssignTypes.java      |  5 +--
 .../ezlang/semantic/SemaDefineTypes.java      |  2 +-
 .../ezlang/semantic/TestSemaDefineTypes.java  |  8 ++---
 .../ezlang/compiler/TestCompiler.java         |  4 +--
 .../ezlang/types/Type.java                    | 35 ++++++++++++-------
 .../ezlang/types/TypeDictionary.java          | 14 ++++----
 8 files changed, 43 insertions(+), 33 deletions(-)

diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestCompiler.java b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestCompiler.java
index 15c31d4..fa148b6 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestCompiler.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestCompiler.java
@@ -214,7 +214,7 @@ func foo()->[Int] {
         String result = compileSrc(src);
         Assert.assertEquals("""
 L0:
-    %t0 = New([Int,Int])
+    %t0 = New([Int])
     %t0.append(1)
     %t0.append(2)
     %t0.append(3)
@@ -235,7 +235,7 @@ func foo(n: Int) -> [Int] {
         Assert.assertEquals("""
 L0:
     arg n
-    %t1 = New([Int,Int])
+    %t1 = New([Int])
     %t1.append(n)
     ret %t1
     goto  L1
diff --git a/registervm/src/test/java/com/compilerprogramming/ezlang/compiler/TestCompiler.java b/registervm/src/test/java/com/compilerprogramming/ezlang/compiler/TestCompiler.java
index 294d6ac..c2f9767 100644
--- a/registervm/src/test/java/com/compilerprogramming/ezlang/compiler/TestCompiler.java
+++ b/registervm/src/test/java/com/compilerprogramming/ezlang/compiler/TestCompiler.java
@@ -203,7 +203,7 @@ func foo()->[Int] {
         String result = compileSrc(src);
         Assert.assertEquals("""
                 L0:
-                    %t0 = New([Int,Int])
+                    %t0 = New([Int])
                     %t0.append(1)
                     %t0.append(2)
                     %t0.append(3)
@@ -223,7 +223,7 @@ func foo(n: Int) -> [Int] {
         String result = compileSrc(src);
         Assert.assertEquals("""
                 L0:
-                    %t1 = New([Int,Int])
+                    %t1 = New([Int])
                     %t1.append(n)
                     %ret = %t1
                     goto  L1
diff --git a/semantic/src/main/java/com/compilerprogramming/ezlang/semantic/SemaAssignTypes.java b/semantic/src/main/java/com/compilerprogramming/ezlang/semantic/SemaAssignTypes.java
index 0ddf525..07908a1 100644
--- a/semantic/src/main/java/com/compilerprogramming/ezlang/semantic/SemaAssignTypes.java
+++ b/semantic/src/main/java/com/compilerprogramming/ezlang/semantic/SemaAssignTypes.java
@@ -294,7 +294,8 @@ public ASTVisitor visit(AST.AssignStmt assignStmt, boolean enter) {
         if (!enter) {
             validType(assignStmt.lhs.type);
             validType(assignStmt.rhs.type);
-            // TODO check assignment
+            if (!assignStmt.lhs.type.isAssignable(assignStmt.rhs.type))
+                throw new CompilerException("Value of type " + assignStmt.rhs.type + " cannot be assigned to type " + assignStmt.lhs.type);
         }
         return this;
     }
@@ -306,7 +307,7 @@ public void analyze(AST.Program program) {
     private void validType(Type t) {
         if (t == null)
             throw new CompilerException("Undefined type");
-        if (t == typeDictionary.ANY)
+        if (t == typeDictionary.UNKNOWN)
             throw new CompilerException("Undefined type");
     }
 }
diff --git a/semantic/src/main/java/com/compilerprogramming/ezlang/semantic/SemaDefineTypes.java b/semantic/src/main/java/com/compilerprogramming/ezlang/semantic/SemaDefineTypes.java
index 1c126b1..95d1ab8 100644
--- a/semantic/src/main/java/com/compilerprogramming/ezlang/semantic/SemaDefineTypes.java
+++ b/semantic/src/main/java/com/compilerprogramming/ezlang/semantic/SemaDefineTypes.java
@@ -293,7 +293,7 @@ public ASTVisitor visit(AST.VarStmt varStmt, boolean enter) {
         if (enter) {
             if (currentScope.localLookup(varStmt.varName) != null)
                 throw new CompilerException("Variable " + varStmt.varName + " already declared in current scope");
-            varStmt.symbol = (Symbol.VarSymbol) currentScope.install(varStmt.varName, new Symbol.VarSymbol(varStmt.varName, typeDictionary.ANY));
+            varStmt.symbol = (Symbol.VarSymbol) currentScope.install(varStmt.varName, new Symbol.VarSymbol(varStmt.varName, typeDictionary.UNKNOWN));
         }
         return this;
     }
diff --git a/semantic/src/test/java/com/compilerprogramming/ezlang/semantic/TestSemaDefineTypes.java b/semantic/src/test/java/com/compilerprogramming/ezlang/semantic/TestSemaDefineTypes.java
index 28ef408..8de6c06 100644
--- a/semantic/src/test/java/com/compilerprogramming/ezlang/semantic/TestSemaDefineTypes.java
+++ b/semantic/src/test/java/com/compilerprogramming/ezlang/semantic/TestSemaDefineTypes.java
@@ -56,7 +56,7 @@ public void test3() {
         sema.analyze(program);
         var symbol = typeDict.lookup("TreeArray");
         Assert.assertNotNull(symbol);
-        Assert.assertEquals("struct TreeArray{data: [Tree,Int];}", symbol.type.describe());
+        Assert.assertEquals("struct TreeArray{data: [Tree];}", symbol.type.describe());
     }
 
     @Test
@@ -72,7 +72,7 @@ public void test4() {
         sema.analyze(program);
         var symbol = typeDict.lookup("TreeArray");
         Assert.assertNotNull(symbol);
-        Assert.assertEquals("struct TreeArray{data: [Tree?,Int];}", symbol.type.describe());
+        Assert.assertEquals("struct TreeArray{data: [Tree?];}", symbol.type.describe());
     }
 
     @Test
@@ -88,7 +88,7 @@ public void test5() {
         sema.analyze(program);
         var symbol = typeDict.lookup("TreeArray");
         Assert.assertNotNull(symbol);
-        Assert.assertEquals("struct TreeArray{data: [Tree?,Int]?;}", symbol.type.describe());
+        Assert.assertEquals("struct TreeArray{data: [Tree?]?;}", symbol.type.describe());
     }
 
     @Test
@@ -147,7 +147,7 @@ public void test9() {
         sema.analyze(program);
         var symbol = typeDict.lookup("TreeArray");
         Assert.assertNotNull(symbol);
-        Assert.assertEquals("struct TreeArray{data: [Int,Int];}", symbol.type.describe());
+        Assert.assertEquals("struct TreeArray{data: [Int];}", symbol.type.describe());
     }
 
 }
diff --git a/stackvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestCompiler.java b/stackvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestCompiler.java
index eeb0a02..2a9a21e 100644
--- a/stackvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestCompiler.java
+++ b/stackvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestCompiler.java
@@ -227,7 +227,7 @@ func foo()->[Int] {
         String result = compileSrc(src);
         Assert.assertEquals("""
 L0:
-	new [Int,Int]
+	new [Int]
 	pushi 1
 	storeappend
 	pushi 2
@@ -249,7 +249,7 @@ func foo(n: Int) -> [Int] {
         String result = compileSrc(src);
         Assert.assertEquals("""
 L0:
-	new [Int,Int]
+	new [Int]
 	load 0
 	storeappend
 	jump L1
diff --git a/types/src/main/java/com/compilerprogramming/ezlang/types/Type.java b/types/src/main/java/com/compilerprogramming/ezlang/types/Type.java
index 2ba5aab..7e1d44c 100644
--- a/types/src/main/java/com/compilerprogramming/ezlang/types/Type.java
+++ b/types/src/main/java/com/compilerprogramming/ezlang/types/Type.java
@@ -14,7 +14,7 @@ public abstract class Type {
 
     // Type classes
     static final byte TVOID = 0;
-    static final byte TANY = 1;
+    static final byte TUNKNOWN = 1;
     static final byte TNULL = 2;
     static final byte TINT = 3;      // Int, Bool
     static final byte TNULLABLE = 4;   // Null, or not null ptr
@@ -52,19 +52,31 @@ public String toString() {
     }
     public String name() { return name; }
 
+    public boolean isAssignable(Type other) {
+        if (other instanceof TypeVoid || other instanceof TypeUnknown)
+            return false;
+        if (this == other || equals(other)) return true;
+        if (this instanceof TypeNullable nullable) {
+            if (other instanceof TypeNull)
+                return true;
+            return nullable.baseType.isAssignable(other);
+        }
+        return false;
+    }
+
+    /**
+     * Represents no type - useful for defining functions
+     * that do not return a value
+     */
     public static class TypeVoid extends Type {
         public TypeVoid() {
             super(TVOID, "$Void");
         }
     }
 
-    /**
-     * We give it the name $Any so that it cannot be referenced in
-     * the language
-     */
-    public static class TypeAny extends Type {
-        public TypeAny() {
-            super(TANY, "$Any");
+    public static class TypeUnknown extends Type {
+        public TypeUnknown() {
+            super(TUNKNOWN, "$Unknown");
         }
     }
 
@@ -127,11 +139,9 @@ public int getFieldIndex(String name) {
 
     public static class TypeArray extends Type {
         Type elementType;
-        Type.TypeInteger size;
 
-        public TypeArray(Type baseType, Type.TypeInteger size) {
-            super(TARRAY, "[" + baseType.name() + "," + size.name() + "]");
-            this.size = size;
+        public TypeArray(Type baseType) {
+            super(TARRAY, "[" + baseType.name() + "]");
             this.elementType = baseType;
             if (baseType instanceof TypeArray)
                 throw new CompilerException("Array of array type not supported");
@@ -139,7 +149,6 @@ public TypeArray(Type baseType, Type.TypeInteger size) {
         public Type getElementType() {
             return elementType;
         }
-        public Type getSizeType() { return size; }
     }
 
     // This is really a dedicated Union type for T|Null.
diff --git a/types/src/main/java/com/compilerprogramming/ezlang/types/TypeDictionary.java b/types/src/main/java/com/compilerprogramming/ezlang/types/TypeDictionary.java
index 9f296c0..9fc233b 100644
--- a/types/src/main/java/com/compilerprogramming/ezlang/types/TypeDictionary.java
+++ b/types/src/main/java/com/compilerprogramming/ezlang/types/TypeDictionary.java
@@ -3,7 +3,7 @@
 import com.compilerprogramming.ezlang.exceptions.CompilerException;
 
 public class TypeDictionary extends Scope {
-    public final Type.TypeAny ANY;
+    public final Type.TypeUnknown UNKNOWN;
     public final Type.TypeInteger INT;
     public final Type.TypeNull NULL;
     public final Type.TypeVoid VOID;
@@ -11,22 +11,22 @@ public class TypeDictionary extends Scope {
     public TypeDictionary() {
         super(null);
         INT = (Type.TypeInteger) intern(new Type.TypeInteger());
-        ANY = (Type.TypeAny) intern(new Type.TypeAny());
+        UNKNOWN = (Type.TypeUnknown) intern(new Type.TypeUnknown());
         NULL = (Type.TypeNull) intern(new Type.TypeNull());
         VOID = (Type.TypeVoid) intern(new Type.TypeVoid());
     }
     public Type makeArrayType(Type elementType, boolean isNullable) {
         switch (elementType) {
             case Type.TypeInteger ti -> {
-                var arrayType = intern(new Type.TypeArray(ti, INT));
+                var arrayType = intern(new Type.TypeArray(ti));
                 return isNullable ? intern(new Type.TypeNullable(arrayType)) : arrayType;
             }
             case Type.TypeStruct ts -> {
-                var arrayType = intern(new Type.TypeArray(ts, INT));
+                var arrayType = intern(new Type.TypeArray(ts));
                 return isNullable ? intern(new Type.TypeNullable(arrayType)) : arrayType;
             }
             case Type.TypeNullable nullable when nullable.baseType instanceof Type.TypeStruct -> {
-                var arrayType = intern(new Type.TypeArray(elementType, INT));
+                var arrayType = intern(new Type.TypeArray(elementType));
                 return isNullable ? intern(new Type.TypeNullable(arrayType)) : arrayType;
             }
             case null, default -> throw new CompilerException("Unsupported array element type: " + elementType);
@@ -50,9 +50,9 @@ else if (t1 instanceof Type.TypeArray && t2 instanceof Type.TypeNull) {
         else if (t2 instanceof Type.TypeArray && t1 instanceof Type.TypeNull) {
             return intern(new Type.TypeNullable(t2));
         }
-        else if (t1 instanceof Type.TypeAny)
+        else if (t1 instanceof Type.TypeUnknown)
             return t2;
-        else if (t2 instanceof Type.TypeAny)
+        else if (t2 instanceof Type.TypeUnknown)
             return t1;
         else if (!t1.equals(t2))
             throw new CompilerException("Unsupported merge type: " + t1 + " and " + t2);

From 35b6814fa45acbb00d002f34bb31efc0b708fe4d Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Sat, 15 Feb 2025 23:10:54 +0000
Subject: [PATCH 027/119] Start work on supporting null

---
 .../ezlang/parser/Parser.java                 |   7 +-
 .../ezlang/semantic/SemaAssignTypes.java      |  61 +++-
 .../ezlang/semantic/TestSemaAssignTypes.java  | 331 +++++-------------
 .../ezlang/semantic/TestSemaDefineTypes.java  |  83 +----
 .../ezlang/types/Type.java                    |   6 +
 5 files changed, 154 insertions(+), 334 deletions(-)

diff --git a/parser/src/main/java/com/compilerprogramming/ezlang/parser/Parser.java b/parser/src/main/java/com/compilerprogramming/ezlang/parser/Parser.java
index 2249d4d..ca9deb9 100644
--- a/parser/src/main/java/com/compilerprogramming/ezlang/parser/Parser.java
+++ b/parser/src/main/java/com/compilerprogramming/ezlang/parser/Parser.java
@@ -358,7 +358,12 @@ private AST.Expr parsePrimary(Lexer lexer) {
                 return x;
             }
             case IDENT -> {
-                if (isToken(currentToken, "new")) {
+                if (isToken(currentToken, "null")) {
+                    var x = new AST.LiteralExpr(currentToken);
+                    nextToken(lexer);
+                    return x;
+                }
+                else if (isToken(currentToken, "new")) {
                     return parseNew(lexer);
                 }
                 else {
diff --git a/semantic/src/main/java/com/compilerprogramming/ezlang/semantic/SemaAssignTypes.java b/semantic/src/main/java/com/compilerprogramming/ezlang/semantic/SemaAssignTypes.java
index 07908a1..f3a87d2 100644
--- a/semantic/src/main/java/com/compilerprogramming/ezlang/semantic/SemaAssignTypes.java
+++ b/semantic/src/main/java/com/compilerprogramming/ezlang/semantic/SemaAssignTypes.java
@@ -67,11 +67,19 @@ public ASTVisitor visit(AST.VarDecl varDecl, boolean enter) {
     @Override
     public ASTVisitor visit(AST.BinaryExpr binaryExpr, boolean enter) {
         if (!enter) {
-            validType(binaryExpr.expr1.type);
-            validType(binaryExpr.expr2.type);
-            if (binaryExpr.expr1.type instanceof Type.TypeInteger t1 &&
-                binaryExpr.expr2.type instanceof Type.TypeInteger t2) {
-                binaryExpr.type = typeDictionary.merge(t1, t2);
+            validType(binaryExpr.expr1.type, true);
+            validType(binaryExpr.expr2.type, true);
+            if (binaryExpr.expr1.type instanceof Type.TypeInteger &&
+                binaryExpr.expr2.type instanceof Type.TypeInteger) {
+                // booleans are int too
+                binaryExpr.type = typeDictionary.INT;
+            }
+            else if (((binaryExpr.expr1.type instanceof Type.TypeNull &&
+                     binaryExpr.expr2.type instanceof Type.TypeNullable) ||
+                    (binaryExpr.expr1.type instanceof Type.TypeNullable &&
+                     binaryExpr.expr2.type instanceof Type.TypeNull)) &&
+                    (binaryExpr.op.str.equals("==") || binaryExpr.op.str.equals("!="))) {
+                binaryExpr.type = typeDictionary.INT;
             }
             else {
                 throw new CompilerException("Binary operator " + binaryExpr.op + " not supported for operands");
@@ -85,7 +93,7 @@ public ASTVisitor visit(AST.UnaryExpr unaryExpr, boolean enter) {
         if (enter) {
             return this;
         }
-        validType(unaryExpr.expr.type);
+        validType(unaryExpr.expr.type, false);
         if (unaryExpr.expr.type instanceof Type.TypeInteger ti) {
             unaryExpr.type = unaryExpr.expr.type;
         }
@@ -99,7 +107,7 @@ public ASTVisitor visit(AST.UnaryExpr unaryExpr, boolean enter) {
     public ASTVisitor visit(AST.FieldExpr fieldExpr, boolean enter) {
         if (enter)
             return this;
-        validType(fieldExpr.object.type);
+        validType(fieldExpr.object.type, false);
         Type.TypeStruct structType = null;
         if (fieldExpr.object.type instanceof Type.TypeStruct ts) {
             structType = ts;
@@ -120,7 +128,7 @@ else if (fieldExpr.object.type instanceof Type.TypeNullable ptr &&
     @Override
     public ASTVisitor visit(AST.CallExpr callExpr, boolean enter) {
         if (!enter) {
-            validType(callExpr.callee.type);
+            validType(callExpr.callee.type, false);
             if (callExpr.callee.type instanceof Type.TypeFunction f) {
                 callExpr.type = f.returnType;
             }
@@ -133,7 +141,7 @@ public ASTVisitor visit(AST.CallExpr callExpr, boolean enter) {
     @Override
     public ASTVisitor visit(AST.SetFieldExpr setFieldExpr, boolean enter) {
         if (!enter) {
-            validType(setFieldExpr.value.type);
+            validType(setFieldExpr.value.type, true);
         }
         return this;
     }
@@ -168,6 +176,10 @@ public ASTVisitor visit(AST.LiteralExpr literalExpr, boolean enter) {
             if (literalExpr.value.kind == Token.Kind.NUM) {
                 literalExpr.type = typeDictionary.INT;
             }
+            else if (literalExpr.value.kind == Token.Kind.IDENT
+                     && literalExpr.value.str.equals("null")) {
+                literalExpr.type = typeDictionary.NULL;
+            }
             else {
                 throw new CompilerException("Unsupported literal " + literalExpr.value);
             }
@@ -178,7 +190,7 @@ public ASTVisitor visit(AST.LiteralExpr literalExpr, boolean enter) {
     @Override
     public ASTVisitor visit(AST.ArrayIndexExpr arrayIndexExpr, boolean enter) {
         if (!enter) {
-            validType(arrayIndexExpr.array.type);
+            validType(arrayIndexExpr.array.type, false);
             Type.TypeArray arrayType = null;
             if (arrayIndexExpr.array.type instanceof Type.TypeArray ta) {
                 arrayType = ta;
@@ -189,8 +201,10 @@ else if (arrayIndexExpr.array.type instanceof Type.TypeNullable ptr &&
             }
             else
                 throw new CompilerException("Unexpected array type " + arrayIndexExpr.array.type);
+            if (!(arrayIndexExpr.expr.type instanceof Type.TypeInteger))
+                throw new CompilerException("Array index must be integer type");
             arrayIndexExpr.type = arrayType.getElementType();
-            validType(arrayIndexExpr.type);
+            validType(arrayIndexExpr.type, false);
         }
         return this;
     }
@@ -201,6 +215,7 @@ public ASTVisitor visit(AST.NewExpr newExpr, boolean enter) {
             return this;
         if (newExpr.typeExpr.type == null)
             throw new CompilerException("Unresolved type in new expression");
+        validType(newExpr.typeExpr.type, false);
         if (newExpr.typeExpr.type instanceof Type.TypeStruct ||
             newExpr.typeExpr.type instanceof Type.TypeArray) {
             newExpr.type = newExpr.typeExpr.type;
@@ -223,7 +238,7 @@ public ASTVisitor visit(AST.NameExpr nameExpr, boolean enter) {
         if (symbol == null) {
             throw new CompilerException("Unknown symbol " + nameExpr.name);
         }
-        validType(symbol.type);
+        validType(symbol.type, false);
         nameExpr.symbol = symbol;
         nameExpr.type = symbol.type;
         return this;
@@ -244,7 +259,7 @@ public ASTVisitor visit(AST.ReturnStmt returnStmt, boolean enter) {
         if (enter)
             return this;
         if (returnStmt.expr != null)
-            validType(returnStmt.expr.type);
+            validType(returnStmt.expr.type, false);
         return this;
     }
 
@@ -261,9 +276,11 @@ public ASTVisitor visit(AST.WhileStmt whileStmt, boolean enter) {
     @Override
     public ASTVisitor visit(AST.VarStmt varStmt, boolean enter) {
         if (!enter) {
-            validType(varStmt.expr.type);
+            validType(varStmt.expr.type, true);
             var symbol = currentScope.lookup(varStmt.varName);
             symbol.type = typeDictionary.merge(varStmt.expr.type, symbol.type);
+            if (symbol.type == typeDictionary.NULL)
+                throw new CompilerException("Variable " + varStmt.varName + " cannot be Null type");
         }
         return this;
     }
@@ -292,10 +309,9 @@ public ASTVisitor visit(AST.ExprStmt exprStmt, boolean enter) {
     @Override
     public ASTVisitor visit(AST.AssignStmt assignStmt, boolean enter) {
         if (!enter) {
-            validType(assignStmt.lhs.type);
-            validType(assignStmt.rhs.type);
-            if (!assignStmt.lhs.type.isAssignable(assignStmt.rhs.type))
-                throw new CompilerException("Value of type " + assignStmt.rhs.type + " cannot be assigned to type " + assignStmt.lhs.type);
+            validType(assignStmt.lhs.type, false);
+            validType(assignStmt.rhs.type, true);
+            checkAssignmentCompatible(assignStmt.lhs.type, assignStmt.rhs.type);
         }
         return this;
     }
@@ -304,10 +320,17 @@ public void analyze(AST.Program program) {
         program.accept(this);
     }
 
-    private void validType(Type t) {
+    private void validType(Type t, boolean allowNull) {
         if (t == null)
             throw new CompilerException("Undefined type");
         if (t == typeDictionary.UNKNOWN)
             throw new CompilerException("Undefined type");
+        if (!allowNull && t == typeDictionary.NULL)
+            throw new CompilerException("Null type not allowed");
+    }
+
+    private void checkAssignmentCompatible(Type var, Type value) {
+        if (!var.isAssignable(value))
+            throw new CompilerException("Value of type " + value + " cannot be assigned to type " + var);
     }
 }
diff --git a/semantic/src/test/java/com/compilerprogramming/ezlang/semantic/TestSemaAssignTypes.java b/semantic/src/test/java/com/compilerprogramming/ezlang/semantic/TestSemaAssignTypes.java
index 3f68c20..39b8302 100644
--- a/semantic/src/test/java/com/compilerprogramming/ezlang/semantic/TestSemaAssignTypes.java
+++ b/semantic/src/test/java/com/compilerprogramming/ezlang/semantic/TestSemaAssignTypes.java
@@ -1,5 +1,6 @@
 package com.compilerprogramming.ezlang.semantic;
 
+import com.compilerprogramming.ezlang.exceptions.CompilerException;
 import com.compilerprogramming.ezlang.lexer.Lexer;
 import com.compilerprogramming.ezlang.parser.Parser;
 import com.compilerprogramming.ezlang.types.TypeDictionary;
@@ -8,494 +9,290 @@
 
 public class TestSemaAssignTypes {
 
-    @Test
-    public void test1() {
+    static void analyze(String src, String symbolName, String typeSig) {
         Parser parser = new Parser();
-        String src = """
-    func foo(a: Int, b: Int)->Int 
-    {
-       return a+b;
-    }
-""";
         var program = parser.parse(new Lexer(src));
         var typeDict = new TypeDictionary();
         var sema = new SemaDefineTypes(typeDict);
         sema.analyze(program);
-        var symbol = typeDict.lookup("foo");
+        var symbol = typeDict.lookup(symbolName);
         Assert.assertNotNull(symbol);
-        Assert.assertEquals("func foo(a: Int,b: Int)->Int", symbol.type.describe());
+        Assert.assertEquals(typeSig, symbol.type.describe());
         var sema2 = new SemaAssignTypes(typeDict);
         sema2.analyze(program);
     }
 
+    @Test
+    public void test1() {
+        String src = """
+    func foo(a: Int, b: Int)->Int 
+    {
+       return a+b;
+    }
+""";
+        analyze(src, "foo", "func foo(a: Int,b: Int)->Int");
+    }
+
     @Test
     public void test51() {
-        Parser parser = new Parser();
         String src = """
     func foo()->Int 
     {
        return 1+1;
     }
 """;
-        var program = parser.parse(new Lexer(src));
-        var typeDict = new TypeDictionary();
-        var sema = new SemaDefineTypes(typeDict);
-        sema.analyze(program);
-        var symbol = typeDict.lookup("foo");
-        Assert.assertNotNull(symbol);
-        Assert.assertEquals("func foo()->Int", symbol.type.describe());
-        var sema2 = new SemaAssignTypes(typeDict);
-        sema2.analyze(program);
+        analyze(src, "foo", "func foo()->Int");
     }
 
     @Test
     public void test2() {
-        Parser parser = new Parser();
         String src = """
     func foo(a: Int, b: Int)->Int 
     {
        return a-b;
     }
 """;
-        var program = parser.parse(new Lexer(src));
-        var typeDict = new TypeDictionary();
-        var sema = new SemaDefineTypes(typeDict);
-        sema.analyze(program);
-        var symbol = typeDict.lookup("foo");
-        Assert.assertNotNull(symbol);
-        Assert.assertEquals("func foo(a: Int,b: Int)->Int", symbol.type.describe());
-        var sema2 = new SemaAssignTypes(typeDict);
-        sema2.analyze(program);
+        analyze(src, "foo", "func foo(a: Int,b: Int)->Int");
     }
 
     @Test
     public void test52() {
-        Parser parser = new Parser();
         String src = """
     func foo()->Int 
     {
        return 1-1;
     }
 """;
-        var program = parser.parse(new Lexer(src));
-        var typeDict = new TypeDictionary();
-        var sema = new SemaDefineTypes(typeDict);
-        sema.analyze(program);
-        var symbol = typeDict.lookup("foo");
-        Assert.assertNotNull(symbol);
-        Assert.assertEquals("func foo()->Int", symbol.type.describe());
-        var sema2 = new SemaAssignTypes(typeDict);
-        sema2.analyze(program);
+        analyze(src, "foo", "func foo()->Int");
     }
 
 
     @Test
     public void test3() {
-        Parser parser = new Parser();
         String src = """
     func foo(a: Int, b: Int)->Int 
     {
        return a*b;
     }
 """;
-        var program = parser.parse(new Lexer(src));
-        var typeDict = new TypeDictionary();
-        var sema = new SemaDefineTypes(typeDict);
-        sema.analyze(program);
-        var symbol = typeDict.lookup("foo");
-        Assert.assertNotNull(symbol);
-        Assert.assertEquals("func foo(a: Int,b: Int)->Int", symbol.type.describe());
-        var sema2 = new SemaAssignTypes(typeDict);
-        sema2.analyze(program);
+        analyze(src, "foo", "func foo(a: Int,b: Int)->Int");
     }
 
     @Test
     public void test53() {
-        Parser parser = new Parser();
         String src = """
     func foo()->Int 
     {
        return 4*2;
     }
 """;
-        var program = parser.parse(new Lexer(src));
-        var typeDict = new TypeDictionary();
-        var sema = new SemaDefineTypes(typeDict);
-        sema.analyze(program);
-        var symbol = typeDict.lookup("foo");
-        Assert.assertNotNull(symbol);
-        Assert.assertEquals("func foo()->Int", symbol.type.describe());
-        var sema2 = new SemaAssignTypes(typeDict);
-        sema2.analyze(program);
+        analyze(src, "foo", "func foo()->Int");
     }
 
     @Test
     public void test4() {
-        Parser parser = new Parser();
         String src = """
     func foo(a: Int, b: Int)->Int 
     {
        return a/b;
     }
 """;
-        var program = parser.parse(new Lexer(src));
-        var typeDict = new TypeDictionary();
-        var sema = new SemaDefineTypes(typeDict);
-        sema.analyze(program);
-        var symbol = typeDict.lookup("foo");
-        Assert.assertNotNull(symbol);
-        Assert.assertEquals("func foo(a: Int,b: Int)->Int", symbol.type.describe());
-        var sema2 = new SemaAssignTypes(typeDict);
-        sema2.analyze(program);
+        analyze(src, "foo", "func foo(a: Int,b: Int)->Int");
     }
 
     @Test
     public void test54() {
-        Parser parser = new Parser();
         String src = """
     func foo()->Int 
     {
        return 4/2;
     }
 """;
-        var program = parser.parse(new Lexer(src));
-        var typeDict = new TypeDictionary();
-        var sema = new SemaDefineTypes(typeDict);
-        sema.analyze(program);
-        var symbol = typeDict.lookup("foo");
-        Assert.assertNotNull(symbol);
-        Assert.assertEquals("func foo()->Int", symbol.type.describe());
-        var sema2 = new SemaAssignTypes(typeDict);
-        sema2.analyze(program);
+        analyze(src, "foo", "func foo()->Int");
     }
 
 
     @Test
     public void test5() {
-        Parser parser = new Parser();
         String src = """
     func foo(a: Int, b: Int)->Int 
     {
        return a==b;
     }
 """;
-        var program = parser.parse(new Lexer(src));
-        var typeDict = new TypeDictionary();
-        var sema = new SemaDefineTypes(typeDict);
-        sema.analyze(program);
-        var symbol = typeDict.lookup("foo");
-        Assert.assertNotNull(symbol);
-        Assert.assertEquals("func foo(a: Int,b: Int)->Int", symbol.type.describe());
-        var sema2 = new SemaAssignTypes(typeDict);
-        sema2.analyze(program);
+        analyze(src, "foo", "func foo(a: Int,b: Int)->Int");
     }
 
 
     @Test
     public void test55() {
-        Parser parser = new Parser();
         String src = """
     func foo()->Int 
     {
        return 4==2;
     }
 """;
-        var program = parser.parse(new Lexer(src));
-        var typeDict = new TypeDictionary();
-        var sema = new SemaDefineTypes(typeDict);
-        sema.analyze(program);
-        var symbol = typeDict.lookup("foo");
-        Assert.assertNotNull(symbol);
-        Assert.assertEquals("func foo()->Int", symbol.type.describe());
-        var sema2 = new SemaAssignTypes(typeDict);
-        sema2.analyze(program);
+        analyze(src, "foo", "func foo()->Int");
     }
 
     @Test
     public void test6() {
-        Parser parser = new Parser();
         String src = """
     func foo(a: Int, b: Int)->Int 
     {
        return a!=b;
     }
 """;
-        var program = parser.parse(new Lexer(src));
-        var typeDict = new TypeDictionary();
-        var sema = new SemaDefineTypes(typeDict);
-        sema.analyze(program);
-        var symbol = typeDict.lookup("foo");
-        Assert.assertNotNull(symbol);
-        Assert.assertEquals("func foo(a: Int,b: Int)->Int", symbol.type.describe());
-        var sema2 = new SemaAssignTypes(typeDict);
-        sema2.analyze(program);
+        analyze(src, "foo", "func foo(a: Int,b: Int)->Int");
     }
 
     @Test
     public void test56() {
-        Parser parser = new Parser();
         String src = """
     func foo()->Int 
     {
        return 4!=2;
     }
 """;
-        var program = parser.parse(new Lexer(src));
-        var typeDict = new TypeDictionary();
-        var sema = new SemaDefineTypes(typeDict);
-        sema.analyze(program);
-        var symbol = typeDict.lookup("foo");
-        Assert.assertNotNull(symbol);
-        Assert.assertEquals("func foo()->Int", symbol.type.describe());
-        var sema2 = new SemaAssignTypes(typeDict);
-        sema2.analyze(program);
+        analyze(src, "foo", "func foo()->Int");
     }
 
 
     @Test
     public void test7() {
-        Parser parser = new Parser();
         String src = """
     func foo(a: Int, b: Int)->Int 
     {
        return a<b;
     }
 """;
-        var program = parser.parse(new Lexer(src));
-        var typeDict = new TypeDictionary();
-        var sema = new SemaDefineTypes(typeDict);
-        sema.analyze(program);
-        var symbol = typeDict.lookup("foo");
-        Assert.assertNotNull(symbol);
-        Assert.assertEquals("func foo(a: Int,b: Int)->Int", symbol.type.describe());
-        var sema2 = new SemaAssignTypes(typeDict);
-        sema2.analyze(program);
+        analyze(src, "foo", "func foo(a: Int,b: Int)->Int");
     }
 
 
     @Test
     public void test57() {
-        Parser parser = new Parser();
         String src = """
     func foo()->Int 
     {
        return 4<2;
     }
 """;
-        var program = parser.parse(new Lexer(src));
-        var typeDict = new TypeDictionary();
-        var sema = new SemaDefineTypes(typeDict);
-        sema.analyze(program);
-        var symbol = typeDict.lookup("foo");
-        Assert.assertNotNull(symbol);
-        Assert.assertEquals("func foo()->Int", symbol.type.describe());
-        var sema2 = new SemaAssignTypes(typeDict);
-        sema2.analyze(program);
+        analyze(src, "foo", "func foo()->Int");
     }
 
     @Test
     public void test8() {
-        Parser parser = new Parser();
         String src = """
     func foo(a: Int, b: Int)->Int 
     {
        return a<=b;
     }
 """;
-        var program = parser.parse(new Lexer(src));
-        var typeDict = new TypeDictionary();
-        var sema = new SemaDefineTypes(typeDict);
-        sema.analyze(program);
-        var symbol = typeDict.lookup("foo");
-        Assert.assertNotNull(symbol);
-        Assert.assertEquals("func foo(a: Int,b: Int)->Int", symbol.type.describe());
-        var sema2 = new SemaAssignTypes(typeDict);
-        sema2.analyze(program);
+        analyze(src, "foo", "func foo(a: Int,b: Int)->Int");
     }
 
     @Test
     public void test58() {
-        Parser parser = new Parser();
         String src = """
     func foo()->Int 
     {
        return 4<=2;
     }
 """;
-        var program = parser.parse(new Lexer(src));
-        var typeDict = new TypeDictionary();
-        var sema = new SemaDefineTypes(typeDict);
-        sema.analyze(program);
-        var symbol = typeDict.lookup("foo");
-        Assert.assertNotNull(symbol);
-        Assert.assertEquals("func foo()->Int", symbol.type.describe());
-        var sema2 = new SemaAssignTypes(typeDict);
-        sema2.analyze(program);
+        analyze(src, "foo", "func foo()->Int");
     }
 
     @Test
     public void test9() {
-        Parser parser = new Parser();
         String src = """
     func foo(a: Int, b: Int)->Int 
     {
        return a>b;
     }
 """;
-        var program = parser.parse(new Lexer(src));
-        var typeDict = new TypeDictionary();
-        var sema = new SemaDefineTypes(typeDict);
-        sema.analyze(program);
-        var symbol = typeDict.lookup("foo");
-        Assert.assertNotNull(symbol);
-        Assert.assertEquals("func foo(a: Int,b: Int)->Int", symbol.type.describe());
-        var sema2 = new SemaAssignTypes(typeDict);
-        sema2.analyze(program);
+        analyze(src, "foo", "func foo(a: Int,b: Int)->Int");
     }
 
     @Test
     public void test59() {
-        Parser parser = new Parser();
         String src = """
     func foo()->Int 
     {
        return 4>2;
     }
 """;
-        var program = parser.parse(new Lexer(src));
-        var typeDict = new TypeDictionary();
-        var sema = new SemaDefineTypes(typeDict);
-        sema.analyze(program);
-        var symbol = typeDict.lookup("foo");
-        Assert.assertNotNull(symbol);
-        Assert.assertEquals("func foo()->Int", symbol.type.describe());
-        var sema2 = new SemaAssignTypes(typeDict);
-        sema2.analyze(program);
+        analyze(src, "foo", "func foo()->Int");
     }
 
     @Test
     public void test10() {
-        Parser parser = new Parser();
         String src = """
     func foo(a: Int, b: Int)->Int 
     {
        return a>=b;
     }
 """;
-        var program = parser.parse(new Lexer(src));
-        var typeDict = new TypeDictionary();
-        var sema = new SemaDefineTypes(typeDict);
-        sema.analyze(program);
-        var symbol = typeDict.lookup("foo");
-        Assert.assertNotNull(symbol);
-        Assert.assertEquals("func foo(a: Int,b: Int)->Int", symbol.type.describe());
-        var sema2 = new SemaAssignTypes(typeDict);
-        sema2.analyze(program);
+        analyze(src, "foo", "func foo(a: Int,b: Int)->Int");
     }
 
     @Test
     public void test60() {
-        Parser parser = new Parser();
         String src = """
     func foo()->Int 
     {
        return 4>=2;
     }
 """;
-        var program = parser.parse(new Lexer(src));
-        var typeDict = new TypeDictionary();
-        var sema = new SemaDefineTypes(typeDict);
-        sema.analyze(program);
-        var symbol = typeDict.lookup("foo");
-        Assert.assertNotNull(symbol);
-        Assert.assertEquals("func foo()->Int", symbol.type.describe());
-        var sema2 = new SemaAssignTypes(typeDict);
-        sema2.analyze(program);
+        analyze(src, "foo", "func foo()->Int");
     }
 
     @Test
     public void test11() {
-        Parser parser = new Parser();
         String src = """
     func foo(a: Int, b: Int)->Int 
     {
        return a&&b;
     }
 """;
-        var program = parser.parse(new Lexer(src));
-        var typeDict = new TypeDictionary();
-        var sema = new SemaDefineTypes(typeDict);
-        sema.analyze(program);
-        var symbol = typeDict.lookup("foo");
-        Assert.assertNotNull(symbol);
-        Assert.assertEquals("func foo(a: Int,b: Int)->Int", symbol.type.describe());
-        var sema2 = new SemaAssignTypes(typeDict);
-        sema2.analyze(program);
+        analyze(src, "foo", "func foo(a: Int,b: Int)->Int");
     }
 
     @Test
     public void test61() {
-        Parser parser = new Parser();
         String src = """
     func foo()->Int 
     {
        return 4&&2;
     }
 """;
-        var program = parser.parse(new Lexer(src));
-        var typeDict = new TypeDictionary();
-        var sema = new SemaDefineTypes(typeDict);
-        sema.analyze(program);
-        var symbol = typeDict.lookup("foo");
-        Assert.assertNotNull(symbol);
-        Assert.assertEquals("func foo()->Int", symbol.type.describe());
-        var sema2 = new SemaAssignTypes(typeDict);
-        sema2.analyze(program);
+        analyze(src, "foo", "func foo()->Int");
     }
 
     @Test
     public void test12() {
-        Parser parser = new Parser();
         String src = """
     func foo(a: Int, b: Int)->Int 
     {
        return a||b;
     }
 """;
-        var program = parser.parse(new Lexer(src));
-        var typeDict = new TypeDictionary();
-        var sema = new SemaDefineTypes(typeDict);
-        sema.analyze(program);
-        var symbol = typeDict.lookup("foo");
-        Assert.assertNotNull(symbol);
-        Assert.assertEquals("func foo(a: Int,b: Int)->Int", symbol.type.describe());
-        var sema2 = new SemaAssignTypes(typeDict);
-        sema2.analyze(program);
+        analyze(src, "foo", "func foo(a: Int,b: Int)->Int");
     }
 
     @Test
     public void test62() {
-        Parser parser = new Parser();
         String src = """
     func foo()->Int 
     {
        return 4||2;
     }
 """;
-        var program = parser.parse(new Lexer(src));
-        var typeDict = new TypeDictionary();
-        var sema = new SemaDefineTypes(typeDict);
-        sema.analyze(program);
-        var symbol = typeDict.lookup("foo");
-        Assert.assertNotNull(symbol);
-        Assert.assertEquals("func foo()->Int", symbol.type.describe());
-        var sema2 = new SemaAssignTypes(typeDict);
-        sema2.analyze(program);
+        analyze(src, "foo", "func foo()->Int");
     }
 
     @Test
     public void test13() {
-        Parser parser = new Parser();
         String src = """
     struct Foo
     {
@@ -508,15 +305,49 @@ func foo()->Foo
        return f.bar[0]
     }
 """;
-        var program = parser.parse(new Lexer(src));
-        var typeDict = new TypeDictionary();
-        var sema = new SemaDefineTypes(typeDict);
-        sema.analyze(program);
-        var symbol = typeDict.lookup("foo");
-        Assert.assertNotNull(symbol);
-        Assert.assertEquals("func foo()->Foo", symbol.type.describe());
-        var sema2 = new SemaAssignTypes(typeDict);
-        sema2.analyze(program);
+        analyze(src, "foo", "func foo()->Foo");
     }
 
+    @Test(expected = CompilerException.class)
+    public void test14() {
+        String src = """
+    struct Foo
+    {
+        var bar: [Int]
+    }
+    func foo()
+    {
+       var f: Foo
+       f = null
+    }
+""";
+        analyze(src, "foo", "func foo()");
+    }
+
+    @Test
+    public void test15() {
+        String src = """
+    struct Foo
+    {
+        var bar: [Int]
+    }
+    func foo()
+    {
+       var f: Foo?
+       f = null
+    }
+""";
+        analyze(src, "foo", "func foo()");
+    }
+
+    @Test(expected = CompilerException.class)
+    public void test16() {
+        String src = """
+    func foo()
+    {
+       var f = null
+    }
+""";
+        analyze(src, "foo", "func foo()");
+    }
 }
diff --git a/semantic/src/test/java/com/compilerprogramming/ezlang/semantic/TestSemaDefineTypes.java b/semantic/src/test/java/com/compilerprogramming/ezlang/semantic/TestSemaDefineTypes.java
index 8de6c06..0b87090 100644
--- a/semantic/src/test/java/com/compilerprogramming/ezlang/semantic/TestSemaDefineTypes.java
+++ b/semantic/src/test/java/com/compilerprogramming/ezlang/semantic/TestSemaDefineTypes.java
@@ -9,116 +9,78 @@
 
 public class TestSemaDefineTypes {
 
+    private void analyze(String src, String symName, String typeSig) {
+        Parser parser = new Parser();
+        var program = parser.parse(new Lexer(src));
+        var typeDict = new TypeDictionary();
+        var sema = new SemaDefineTypes(typeDict);
+        sema.analyze(program);
+        var symbol = typeDict.lookup(symName);
+        Assert.assertNotNull(symbol);
+        Assert.assertEquals(typeSig, symbol.type.describe());
+    }
+
     @Test
     public void test1() {
-        Parser parser = new Parser();
         String src = """
 struct Tree {
     var left: Tree?
     var right: Tree?
 }                """;
-        var program = parser.parse(new Lexer(src));
-        var typeDict = new TypeDictionary();
-        var sema = new SemaDefineTypes(typeDict);
-        sema.analyze(program);
-        var symbol = typeDict.lookup("Tree");
-        Assert.assertNotNull(symbol);
-        Assert.assertEquals("struct Tree{left: Tree?;right: Tree?;}", symbol.type.describe());
+        analyze(src, "Tree", "struct Tree{left: Tree?;right: Tree?;}");
     }
 
     @Test
     public void test2() {
-        Parser parser = new Parser();
         String src = """
 struct Tree {
     var left: Tree
     var right: Tree
 }                """;
-        var program = parser.parse(new Lexer(src));
-        var typeDict = new TypeDictionary();
-        var sema = new SemaDefineTypes(typeDict);
-        sema.analyze(program);
-        var symbol = typeDict.lookup("Tree");
-        Assert.assertNotNull(symbol);
-        Assert.assertEquals("struct Tree{left: Tree;right: Tree;}", symbol.type.describe());
+        analyze(src, "Tree", "struct Tree{left: Tree;right: Tree;}");
     }
 
     @Test
     public void test3() {
-        Parser parser = new Parser();
         String src = """
 struct TreeArray {
     var data: [Tree]
 }                """;
-        var program = parser.parse(new Lexer(src));
-        var typeDict = new TypeDictionary();
-        var sema = new SemaDefineTypes(typeDict);
-        sema.analyze(program);
-        var symbol = typeDict.lookup("TreeArray");
-        Assert.assertNotNull(symbol);
-        Assert.assertEquals("struct TreeArray{data: [Tree];}", symbol.type.describe());
+        analyze(src, "TreeArray", "struct TreeArray{data: [Tree];}");
     }
 
     @Test
     public void test4() {
-        Parser parser = new Parser();
         String src = """
 struct TreeArray {
     var data: [Tree?]
 }                """;
-        var program = parser.parse(new Lexer(src));
-        var typeDict = new TypeDictionary();
-        var sema = new SemaDefineTypes(typeDict);
-        sema.analyze(program);
-        var symbol = typeDict.lookup("TreeArray");
-        Assert.assertNotNull(symbol);
-        Assert.assertEquals("struct TreeArray{data: [Tree?];}", symbol.type.describe());
+        analyze(src, "TreeArray", "struct TreeArray{data: [Tree?];}");
     }
 
     @Test
     public void test5() {
-        Parser parser = new Parser();
         String src = """
 struct TreeArray {
     var data: [Tree?]?
 }                """;
-        var program = parser.parse(new Lexer(src));
-        var typeDict = new TypeDictionary();
-        var sema = new SemaDefineTypes(typeDict);
-        sema.analyze(program);
-        var symbol = typeDict.lookup("TreeArray");
-        Assert.assertNotNull(symbol);
-        Assert.assertEquals("struct TreeArray{data: [Tree?]?;}", symbol.type.describe());
+        analyze(src, "TreeArray", "struct TreeArray{data: [Tree?]?;}");
     }
 
     @Test
     public void test6() {
-        Parser parser = new Parser();
         String src = """
 func print(t: Tree) {
 }                """;
-        var program = parser.parse(new Lexer(src));
-        var typeDict = new TypeDictionary();
-        var sema = new SemaDefineTypes(typeDict);
-        sema.analyze(program);
-        var symbol = typeDict.lookup("print");
-        Assert.assertNotNull(symbol);
-        Assert.assertEquals("func print(t: Tree)", symbol.type.describe());
+        analyze(src, "print", "func print(t: Tree)");
     }
 
     @Test
     public void test7() {
-        Parser parser = new Parser();
         String src = """
 func makeTree()->Tree {
 }                """;
-        var program = parser.parse(new Lexer(src));
-        var typeDict = new TypeDictionary();
-        var sema = new SemaDefineTypes(typeDict);
-        sema.analyze(program);
-        var symbol = typeDict.lookup("makeTree");
-        Assert.assertNotNull(symbol);
-        Assert.assertEquals("func makeTree()->Tree", symbol.type.describe());
+        analyze(src, "makeTree", "func makeTree()->Tree");
     }
 
     @Test(expected = CompilerException.class)
@@ -136,18 +98,11 @@ public void test8() {
 
     @Test
     public void test9() {
-        Parser parser = new Parser();
         String src = """
 struct TreeArray {
     var data: [Int]
 }                """;
-        var program = parser.parse(new Lexer(src));
-        var typeDict = new TypeDictionary();
-        var sema = new SemaDefineTypes(typeDict);
-        sema.analyze(program);
-        var symbol = typeDict.lookup("TreeArray");
-        Assert.assertNotNull(symbol);
-        Assert.assertEquals("struct TreeArray{data: [Int];}", symbol.type.describe());
+        analyze(src, "TreeArray", "struct TreeArray{data: [Int];}");
     }
 
 }
diff --git a/types/src/main/java/com/compilerprogramming/ezlang/types/Type.java b/types/src/main/java/com/compilerprogramming/ezlang/types/Type.java
index 7e1d44c..1233a64 100644
--- a/types/src/main/java/com/compilerprogramming/ezlang/types/Type.java
+++ b/types/src/main/java/com/compilerprogramming/ezlang/types/Type.java
@@ -61,6 +61,12 @@ public boolean isAssignable(Type other) {
                 return true;
             return nullable.baseType.isAssignable(other);
         }
+        else if (other instanceof TypeNullable nullable) {
+            // At compile time we allow nullable value to be
+            // assigned to base type, but null check must be inserted
+            // Optimizer may remove null check
+            return isAssignable(nullable.baseType);
+        }
         return false;
     }
 

From 5d4824c2c64450729857dc194ddb2addc277a9bc Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Sun, 16 Feb 2025 10:28:14 +0000
Subject: [PATCH 028/119] Sync register vm compiler and interpreter with optvm
 so that both are close. This will make it easier to see what the differences
 are and also to make the changes to support null

---
 .../ezlang/compiler/CompiledFunction.java     |  13 +-
 .../ezlang/compiler/Instruction.java          | 187 ++++++++++--------
 .../ezlang/compiler/Operand.java              |  25 +--
 .../ezlang/interpreter/Interpreter.java       | 116 +++++------
 .../ezlang/compiler/TestCompiler.java         |  48 ++---
 5 files changed, 201 insertions(+), 188 deletions(-)

diff --git a/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java b/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
index dedc42e..56d5f55 100644
--- a/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
+++ b/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
@@ -13,8 +13,8 @@ public class CompiledFunction {
 
     public BasicBlock entry;
     public BasicBlock exit;
-    private int bid = 0;
-    private BasicBlock currentBlock;
+    private int BID = 0;
+    public BasicBlock currentBlock;
     private BasicBlock currentBreakTarget;
     private BasicBlock currentContinueTarget;
     public int maxLocalReg;
@@ -33,7 +33,7 @@ public class CompiledFunction {
     public CompiledFunction(Symbol.FunctionTypeSymbol functionSymbol) {
         AST.FuncDecl funcDecl = (AST.FuncDecl) functionSymbol.functionDecl;
         setVirtualRegisters(funcDecl.scope);
-        this.bid = 0;
+        this.BID = 0;
         this.entry = this.currentBlock = createBlock();
         this.exit = createBlock();
         this.currentBreakTarget = null;
@@ -71,11 +71,11 @@ private void setVirtualRegisters(Scope scope) {
     }
 
     private BasicBlock createBlock() {
-        return new BasicBlock(bid++);
+        return new BasicBlock(BID++);
     }
 
     private BasicBlock createLoopHead() {
-        return new BasicBlock(bid++, true);
+        return new BasicBlock(BID++, true);
     }
 
     private void compileBlock(AST.BlockStmt block) {
@@ -90,7 +90,7 @@ private void compileReturn(AST.ReturnStmt returnStmt) {
             if (isIndexed)
                 codeIndexedLoad();
             if (virtualStack.size() == 1)
-                code(new Instruction.Return(pop()));
+                code(new Instruction.Ret(pop()));
             else if (virtualStack.size() > 1)
                 throw new CompilerException("Virtual stack has more than one item at return");
         }
@@ -109,6 +109,7 @@ private void compileStatement(AST.Stmt statement) {
             case AST.VarStmt letStmt -> {
                 compileLet(letStmt);
             }
+            case AST.VarDeclStmt varDeclStmt -> {}
             case AST.IfElseStmt ifElseStmt -> {
                 compileIf(ifElseStmt);
             }
diff --git a/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java b/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java
index 448c0da..35c1fda 100644
--- a/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java
+++ b/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java
@@ -4,37 +4,69 @@
 
 public abstract class Instruction {
 
-    public boolean isTerminal() {
-        return false;
+    static final int I_NOOP = 0;
+    static final int I_MOVE = 1;
+    static final int I_RET  = 2;
+    static final int I_UNARY = 3;
+    static final int I_BINARY = 4;
+    static final int I_BR = 5;
+    static final int I_CBR = 6;
+    static final int I_ARG = 7;
+    static final int I_CALL = 8;
+    static final int I_PHI = 9;
+    static final int I_NEW_ARRAY = 10;
+    static final int I_NEW_STRUCT = 11;
+    static final int I_ARRAY_STORE = 12;
+    static final int I_ARRAY_LOAD = 13;
+    static final int I_ARRAY_APPEND = 14;
+    static final int I_FIELD_GET = 15;
+    static final int I_FIELD_SET = 16;
+
+    public final int opcode;
+    protected Operand.RegisterOperand def;
+    protected Operand[] uses;
+
+    protected Instruction(int opcode, Operand... uses) {
+        this.opcode = opcode;
+        this.def = null;
+        this.uses = new Operand[uses.length];
+        System.arraycopy(uses, 0, this.uses, 0, uses.length);
+    }
+    protected Instruction(int opcode, Operand.RegisterOperand def, Operand... uses) {
+        this.opcode = opcode;
+        this.def = def;
+        this.uses = new Operand[uses.length];
+        System.arraycopy(uses, 0, this.uses, 0, uses.length);
     }
+
+    public boolean isTerminal() { return false; }
     @Override
     public String toString() {
         return toStr(new StringBuilder()).toString();
     }
 
     public static class Move extends Instruction {
-        public final Operand from;
-        public final Operand to;
         public Move(Operand from, Operand to) {
-            this.from = from;
-            this.to = to;
+            super(I_MOVE, (Operand.RegisterOperand) to, from);
         }
+        public Operand from() { return uses[0]; }
+        public Operand.RegisterOperand to() { return def; }
         @Override
         public StringBuilder toStr(StringBuilder sb) {
-            return sb.append(to).append(" = ").append(from);
+            return sb.append(to()).append(" = ").append(from());
         }
     }
 
     public static class NewArray extends Instruction {
         public final Type.TypeArray type;
-        public final Operand.RegisterOperand destOperand;
         public NewArray(Type.TypeArray type, Operand.RegisterOperand destOperand) {
+            super(I_NEW_ARRAY, destOperand);
             this.type = type;
-            this.destOperand = destOperand;
         }
+        public Operand.RegisterOperand destOperand() { return def; }
         @Override
         public StringBuilder toStr(StringBuilder sb) {
-            return sb.append(destOperand)
+            return sb.append(def)
                     .append(" = ")
                     .append("New(")
                     .append(type)
@@ -44,14 +76,14 @@ public StringBuilder toStr(StringBuilder sb) {
 
     public static class NewStruct extends Instruction {
         public final Type.TypeStruct type;
-        public final Operand.RegisterOperand destOperand;
         public NewStruct(Type.TypeStruct type, Operand.RegisterOperand destOperand) {
+            super(I_NEW_STRUCT, destOperand);
             this.type = type;
-            this.destOperand = destOperand;
         }
+        public Operand.RegisterOperand destOperand() { return def; }
         @Override
         public StringBuilder toStr(StringBuilder sb) {
-            return sb.append(destOperand)
+            return sb.append(def)
                     .append(" = ")
                     .append("New(")
                     .append(type)
@@ -60,182 +92,177 @@ public StringBuilder toStr(StringBuilder sb) {
     }
 
     public static class ArrayLoad extends Instruction {
-        public final Operand arrayOperand;
-        public final Operand indexOperand;
-        public final Operand.RegisterOperand destOperand;
         public ArrayLoad(Operand.LoadIndexedOperand from, Operand.RegisterOperand to) {
-            arrayOperand = from.arrayOperand;
-            indexOperand = from.indexOperand;
-            destOperand = to;
+            super(I_ARRAY_LOAD, to, from.arrayOperand, from.indexOperand);
         }
+        public Operand arrayOperand() { return uses[0]; }
+        public Operand indexOperand() { return uses[1]; }
+        public Operand.RegisterOperand destOperand() { return def; }
         @Override
         public StringBuilder toStr(StringBuilder sb) {
-            return sb.append(destOperand)
+            return sb.append(destOperand())
                     .append(" = ")
-                    .append(arrayOperand)
+                    .append(arrayOperand())
                     .append("[")
-                    .append(indexOperand)
+                    .append(indexOperand())
                     .append("]");
         }
     }
 
     public static class ArrayStore extends Instruction {
-        public final Operand arrayOperand;
-        public final Operand indexOperand;
-        public final Operand sourceOperand;
         public ArrayStore(Operand from, Operand.LoadIndexedOperand to) {
-            arrayOperand = to.arrayOperand;
-            indexOperand = to.indexOperand;
-            sourceOperand = from;
+            super(I_ARRAY_STORE, (Operand.RegisterOperand) null, to.arrayOperand, to.indexOperand, from);
         }
+        public Operand arrayOperand() { return uses[0]; }
+        public Operand indexOperand() { return uses[1]; }
+        public Operand sourceOperand() { return uses[2]; }
         @Override
         public StringBuilder toStr(StringBuilder sb) {
             return sb
-                    .append(arrayOperand)
+                    .append(arrayOperand())
                     .append("[")
-                    .append(indexOperand)
+                    .append(indexOperand())
                     .append("] = ")
-                    .append(sourceOperand);
+                    .append(sourceOperand());
         }
     }
 
     public static class GetField extends Instruction {
-        public final Operand structOperand;
         public final String fieldName;
         public final int fieldIndex;
-        public final Operand.RegisterOperand destOperand;
-        public GetField(Operand.LoadFieldOperand from, Operand.RegisterOperand to)
-        {
-            this.structOperand = from.structOperand;
+        public GetField(Operand.LoadFieldOperand from, Operand.RegisterOperand to) {
+            super(I_FIELD_GET, to, from.structOperand);
             this.fieldName = from.fieldName;
             this.fieldIndex = from.fieldIndex;
-            this.destOperand = to;
         }
+        public Operand structOperand() { return uses[0]; }
+        public Operand.RegisterOperand destOperand() { return def; }
         @Override
         public StringBuilder toStr(StringBuilder sb) {
-            return sb.append(destOperand)
+            return sb.append(def)
                     .append(" = ")
-                    .append(structOperand)
+                    .append(uses[0])
                     .append(".")
                     .append(fieldName);
         }
     }
 
     public static class SetField extends Instruction {
-        public final Operand structOperand;
         public final String fieldName;
         public final int fieldIndex;
-        public final Operand sourceOperand;
-        public SetField(Operand from,Operand.LoadFieldOperand to)
-        {
-            this.structOperand = to.structOperand;
+        public SetField(Operand from,Operand.LoadFieldOperand to) {
+            super(I_FIELD_SET, (Operand.RegisterOperand) null, to.structOperand, from);
             this.fieldName = to.fieldName;
             this.fieldIndex = to.fieldIndex;
-            this.sourceOperand = from;
         }
+        public Operand structOperand() { return uses[0]; }
+        public Operand sourceOperand() { return uses[1]; }
         @Override
         public StringBuilder toStr(StringBuilder sb) {
             return sb
-                    .append(structOperand)
+                    .append(structOperand())
                     .append(".")
                     .append(fieldName)
                     .append(" = ")
-                    .append(sourceOperand);
+                    .append(sourceOperand());
         }
     }
-    public static class Return extends Move {
-        public Return(Operand from) {
-            super(from, new Operand.ReturnRegisterOperand());
+    public static class Ret extends Instruction {
+        public Ret(Operand value) {
+            super(I_RET, (Operand.RegisterOperand) null, value);
+        }
+        public Operand value() { return uses[0]; }
+        @Override
+        public StringBuilder toStr(StringBuilder sb) {
+            sb.append("ret");
+            if (uses[0] != null)
+                sb.append(" ").append(value());
+            return sb;
         }
     }
     public static class Unary extends Instruction {
         public final String unop;
-        public final Operand.RegisterOperand result;
-        public final Operand operand;
         public Unary(String unop, Operand.RegisterOperand result, Operand operand) {
+            super(I_UNARY, result, operand);
             this.unop = unop;
-            this.result = result;
-            this.operand = operand;
         }
+        public Operand.RegisterOperand result() { return def; }
+        public Operand operand() { return uses[0]; }
         @Override
         public StringBuilder toStr(StringBuilder sb) {
-            return sb.append(result).append(" = ").append(unop).append(operand);
+            return sb.append(result()).append(" = ").append(unop).append(operand());
         }
     }
 
     public static class Binary extends Instruction {
         public final String binOp;
-        public final Operand.RegisterOperand result;
-        public final Operand left;
-        public final Operand right;
         public Binary(String binop, Operand.RegisterOperand result, Operand left, Operand right) {
+            super(I_BINARY, result, left, right);
             this.binOp = binop;
-            this.result = result;
-            this.left = left;
-            this.right = right;
         }
+        public Operand.RegisterOperand result() { return def; }
+        public Operand left() { return uses[0]; }
+        public Operand right() { return uses[1]; }
         @Override
         public StringBuilder toStr(StringBuilder sb) {
-            return sb.append(result).append(" = ").append(left).append(binOp).append(right);
+            return sb.append(def).append(" = ").append(uses[0]).append(binOp).append(uses[1]);
         }
     }
 
     public static class AStoreAppend extends Instruction {
-        public final Operand.RegisterOperand array;
-        public final Operand value;
         public AStoreAppend(Operand.RegisterOperand array, Operand value) {
-            this.array = array;
-            this.value = value;
+            super(I_ARRAY_APPEND, (Operand.RegisterOperand) null, array, value);
         }
+        public Operand.RegisterOperand array() { return (Operand.RegisterOperand) uses[0]; }
+        public Operand value() { return uses[1]; }
         @Override
         public StringBuilder toStr(StringBuilder sb) {
-            return sb.append(array).append(".append(").append(value).append(")");
+            return sb.append(uses[0]).append(".append(").append(uses[1]).append(")");
         }
     }
 
     public static class ConditionalBranch extends Instruction {
-        public final Operand condition;
         public final BasicBlock trueBlock;
         public final BasicBlock falseBlock;
         public ConditionalBranch(BasicBlock currentBlock, Operand condition, BasicBlock trueBlock, BasicBlock falseBlock) {
-            this.condition = condition;
+            super(I_CBR, (Operand.RegisterOperand) null, condition);
             this.trueBlock = trueBlock;
             this.falseBlock = falseBlock;
             currentBlock.addSuccessor(trueBlock);
             currentBlock.addSuccessor(falseBlock);
         }
+        public Operand condition() { return uses[0]; }
         @Override
         public boolean isTerminal() {
             return true;
         }
         @Override
         public StringBuilder toStr(StringBuilder sb) {
-            return sb.append("if ").append(condition).append(" goto L").append(trueBlock.bid).append(" else goto L").append(falseBlock.bid);
+            return sb.append("if ").append(condition()).append(" goto L").append(trueBlock.bid).append(" else goto L").append(falseBlock.bid);
         }
     }
 
     public static class Call extends Instruction {
         public final Type.TypeFunction callee;
-        public final Operand.RegisterOperand[] args;
-        public final Operand.RegisterOperand returnOperand;
         public final int newbase;
         public Call(int newbase, Operand.RegisterOperand returnOperand, Type.TypeFunction callee, Operand.RegisterOperand... args) {
-            this.returnOperand = returnOperand;
+            super(I_CALL, returnOperand, args);
             this.callee = callee;
-            this.args = args;
             this.newbase = newbase;
         }
+        public Operand.RegisterOperand returnOperand() { return def; }
+        public Operand[] args() { return uses; }
         @Override
         public StringBuilder toStr(StringBuilder sb) {
-            if (returnOperand != null) {
-                sb.append(returnOperand).append(" = ");
+            if (def != null) {
+                sb.append(def).append(" = ");
             }
             sb.append("call ").append(callee);
-            if (args.length > 0)
+            if (uses.length > 0)
                 sb.append(" params ");
-            for (int i = 0; i < args.length; i++) {
+            for (int i = 0; i < uses.length; i++) {
                 if (i > 0) sb.append(", ");
-                sb.append(args[i]);
+                sb.append(uses[i]);
             }
             return sb;
         }
@@ -244,6 +271,7 @@ public StringBuilder toStr(StringBuilder sb) {
     public static class Jump extends Instruction {
         public final BasicBlock jumpTo;
         public Jump(BasicBlock jumpTo) {
+            super(I_BR);
             this.jumpTo = jumpTo;
         }
         @Override
@@ -256,5 +284,6 @@ public StringBuilder toStr(StringBuilder sb) {
         }
     }
 
+
     public abstract StringBuilder toStr(StringBuilder sb);
 }
diff --git a/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/Operand.java b/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/Operand.java
index bff7d66..9cb9a70 100644
--- a/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/Operand.java
+++ b/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/Operand.java
@@ -23,6 +23,7 @@ public static abstract class RegisterOperand extends Operand {
         public RegisterOperand(int regnum) {
             this.regnum = regnum;
         }
+        public int frameSlot() { return regnum; }
     }
 
     public static class LocalRegisterOperand extends RegisterOperand {
@@ -48,17 +49,6 @@ public String toString() {
         }
     }
 
-    /**
-     * Represents the return register, which is the location where
-     * the caller will expect to see any return value. The VM must map
-     * this to appropriate location.
-     */
-    public static class ReturnRegisterOperand extends RegisterOperand {
-        public ReturnRegisterOperand() { super(0); }
-        @Override
-        public String toString() { return "%ret"; }
-    }
-
     /**
      * Represents a temp register, maps to a location on the
      * virtual stack. Temps start at offset 0, but this is a relative
@@ -108,17 +98,4 @@ public String toString() {
             return structOperand + "." + fieldName;
         }
     }
-
-    public static class NewTypeOperand extends Operand {
-        public final Type type;
-        public NewTypeOperand(Type type) {
-            this.type = type;
-        }
-
-        @Override
-        public String toString() {
-            return "New(" + type + ")";
-        }
-    }
-
 }
diff --git a/registervm/src/main/java/com/compilerprogramming/ezlang/interpreter/Interpreter.java b/registervm/src/main/java/com/compilerprogramming/ezlang/interpreter/Interpreter.java
index 3c06472..7be8ef9 100644
--- a/registervm/src/main/java/com/compilerprogramming/ezlang/interpreter/Interpreter.java
+++ b/registervm/src/main/java/com/compilerprogramming/ezlang/interpreter/Interpreter.java
@@ -44,23 +44,24 @@ public Value interpret(ExecutionStack execStack, Frame frame) {
             ip++;
             instruction = currentBlock.instructions.get(ip);
             switch (instruction) {
-                case Instruction.Return returnInst -> {
-                    if (returnInst.from instanceof Operand.ConstantOperand constantOperand) {
+                case Instruction.Ret retInst -> {
+                    if (retInst.value() instanceof Operand.ConstantOperand constantOperand) {
                         execStack.stack[base] = new Value.IntegerValue(constantOperand.value);
                     }
-                    else if (returnInst.from instanceof Operand.RegisterOperand registerOperand) {
-                        execStack.stack[base] = execStack.stack[base+registerOperand.regnum];
+                    else if (retInst.value() instanceof Operand.RegisterOperand registerOperand) {
+                        execStack.stack[base] = execStack.stack[base+registerOperand.frameSlot()];
                     }
                     else throw new IllegalStateException();
                     returnValue = execStack.stack[base];
                 }
+
                 case Instruction.Move moveInst -> {
-                    if (moveInst.to instanceof Operand.RegisterOperand toReg) {
-                        if (moveInst.from instanceof Operand.RegisterOperand fromReg) {
-                            execStack.stack[base + toReg.regnum] = execStack.stack[base + fromReg.regnum];
+                    if (moveInst.to() instanceof Operand.RegisterOperand toReg) {
+                        if (moveInst.from() instanceof Operand.RegisterOperand fromReg) {
+                            execStack.stack[base + toReg.frameSlot()] = execStack.stack[base + fromReg.frameSlot()];
                         }
-                        else if (moveInst.from instanceof Operand.ConstantOperand constantOperand) {
-                            execStack.stack[base + toReg.regnum] = new Value.IntegerValue(constantOperand.value);
+                        else if (moveInst.from() instanceof Operand.ConstantOperand constantOperand) {
+                            execStack.stack[base + toReg.frameSlot()] = new Value.IntegerValue(constantOperand.value);
                         }
                         else throw new IllegalStateException();
                     }
@@ -74,8 +75,8 @@ else if (moveInst.from instanceof Operand.ConstantOperand constantOperand) {
                 }
                 case Instruction.ConditionalBranch cbrInst -> {
                     boolean condition;
-                    if (cbrInst.condition instanceof Operand.RegisterOperand registerOperand) {
-                        Value value = execStack.stack[base + registerOperand.regnum];
+                    if (cbrInst.condition() instanceof Operand.RegisterOperand registerOperand) {
+                        Value value = execStack.stack[base + registerOperand.frameSlot()];
                         if (value instanceof Value.IntegerValue integerValue) {
                             condition = integerValue.value != 0;
                         }
@@ -83,7 +84,7 @@ else if (moveInst.from instanceof Operand.ConstantOperand constantOperand) {
                             condition = value != null;
                         }
                     }
-                    else if (cbrInst.condition instanceof Operand.ConstantOperand constantOperand) {
+                    else if (cbrInst.condition() instanceof Operand.ConstantOperand constantOperand) {
                         condition = constantOperand.value != 0;
                     }
                     else throw new IllegalStateException();
@@ -99,8 +100,13 @@ else if (cbrInst.condition instanceof Operand.ConstantOperand constantOperand) {
                     // Copy args to new frame
                     int baseReg = base+currentFunction.frameSize();
                     int reg = baseReg;
-                    for (Operand.RegisterOperand arg: callInst.args) {
-                        execStack.stack[base + reg] = execStack.stack[base + arg.regnum];
+                    for (Operand arg: callInst.args()) {
+                        if (arg instanceof Operand.RegisterOperand param) {
+                            execStack.stack[base + reg] = execStack.stack[base + param.frameSlot()];
+                        }
+                        else if (arg instanceof Operand.ConstantOperand constantOperand) {
+                            execStack.stack[base + reg] = new Value.IntegerValue(constantOperand.value);
+                        }
                         reg += 1;
                     }
                     // Call function
@@ -108,18 +114,18 @@ else if (cbrInst.condition instanceof Operand.ConstantOperand constantOperand) {
                     interpret(execStack, newFrame);
                     // Copy return value in expected location
                     if (!(callInst.callee.returnType instanceof Type.TypeVoid)) {
-                        execStack.stack[base + callInst.returnOperand.regnum] = execStack.stack[baseReg];
+                        execStack.stack[base + callInst.returnOperand().frameSlot()] = execStack.stack[baseReg];
                     }
                 }
                 case Instruction.Unary unaryInst -> {
                     // We don't expect constant here because we fold constants in unary expressions
-                    Operand.RegisterOperand unaryOperand = (Operand.RegisterOperand) unaryInst.operand;
-                    Value unaryValue = execStack.stack[base + unaryOperand.regnum];
+                    Operand.RegisterOperand unaryOperand = (Operand.RegisterOperand) unaryInst.operand();
+                    Value unaryValue = execStack.stack[base + unaryOperand.frameSlot()];
                     if (unaryValue instanceof Value.IntegerValue integerValue) {
                         switch (unaryInst.unop) {
-                            case "-": execStack.stack[base + unaryInst.result.regnum] = new Value.IntegerValue(-integerValue.value); break;
+                            case "-": execStack.stack[base + unaryInst.result().frameSlot()] = new Value.IntegerValue(-integerValue.value); break;
                             // Maybe below we should explicitly set Int
-                            case "!": execStack.stack[base + unaryInst.result.regnum] = new Value.IntegerValue(integerValue.value==0?1:0); break;
+                            case "!": execStack.stack[base + unaryInst.result().frameSlot()] = new Value.IntegerValue(integerValue.value==0?1:0); break;
                             default: throw new CompilerException("Invalid unary op");
                         }
                     }
@@ -129,15 +135,15 @@ else if (cbrInst.condition instanceof Operand.ConstantOperand constantOperand) {
                 case Instruction.Binary binaryInst -> {
                     long x, y;
                     long value = 0;
-                    if (binaryInst.left instanceof Operand.ConstantOperand constant)
+                    if (binaryInst.left() instanceof Operand.ConstantOperand constant)
                         x = constant.value;
-                    else if (binaryInst.left instanceof Operand.RegisterOperand registerOperand)
-                        x = ((Value.IntegerValue) execStack.stack[base + registerOperand.regnum]).value;
+                    else if (binaryInst.left() instanceof Operand.RegisterOperand registerOperand)
+                        x = ((Value.IntegerValue) execStack.stack[base + registerOperand.frameSlot()]).value;
                     else throw new IllegalStateException();
-                    if (binaryInst.right instanceof Operand.ConstantOperand constant)
+                    if (binaryInst.right() instanceof Operand.ConstantOperand constant)
                         y = constant.value;
-                    else if (binaryInst.right instanceof Operand.RegisterOperand registerOperand)
-                        y = ((Value.IntegerValue) execStack.stack[base + registerOperand.regnum]).value;
+                    else if (binaryInst.right() instanceof Operand.RegisterOperand registerOperand)
+                        y = ((Value.IntegerValue) execStack.stack[base + registerOperand.frameSlot()]).value;
                     else throw new IllegalStateException();
                     switch (binaryInst.binOp) {
                         case "+": value = x + y; break;
@@ -153,80 +159,80 @@ else if (binaryInst.right instanceof Operand.RegisterOperand registerOperand)
                         case ">=": value = x <= y ? 1 : 0; break;
                         default: throw new IllegalStateException();
                     }
-                    execStack.stack[base + binaryInst.result.regnum] = new Value.IntegerValue(value);
+                    execStack.stack[base + binaryInst.result().frameSlot()] = new Value.IntegerValue(value);
                 }
                 case Instruction.NewArray newArrayInst -> {
-                    execStack.stack[base + newArrayInst.destOperand.regnum] = new Value.ArrayValue(newArrayInst.type);
+                    execStack.stack[base + newArrayInst.destOperand().frameSlot()] = new Value.ArrayValue(newArrayInst.type);
                 }
                 case Instruction.NewStruct newStructInst -> {
-                    execStack.stack[base + newStructInst.destOperand.regnum] = new Value.StructValue(newStructInst.type);
+                    execStack.stack[base + newStructInst.destOperand().frameSlot()] = new Value.StructValue(newStructInst.type);
                 }
                 case Instruction.AStoreAppend arrayAppendInst -> {
-                    Value.ArrayValue arrayValue = (Value.ArrayValue) execStack.stack[base + arrayAppendInst.array.regnum];
-                    if (arrayAppendInst.value instanceof Operand.ConstantOperand constant) {
+                    Value.ArrayValue arrayValue = (Value.ArrayValue) execStack.stack[base + arrayAppendInst.array().frameSlot()];
+                    if (arrayAppendInst.value() instanceof Operand.ConstantOperand constant) {
                         arrayValue.values.add(new Value.IntegerValue(constant.value));
                     }
-                    else if (arrayAppendInst.value instanceof Operand.RegisterOperand registerOperand) {
-                        arrayValue.values.add(execStack.stack[base + registerOperand.regnum]);
+                    else if (arrayAppendInst.value() instanceof Operand.RegisterOperand registerOperand) {
+                        arrayValue.values.add(execStack.stack[base + registerOperand.frameSlot()]);
                     }
                     else throw new IllegalStateException();
                 }
                 case Instruction.ArrayStore arrayStoreInst -> {
-                    if (arrayStoreInst.arrayOperand instanceof Operand.RegisterOperand arrayOperand) {
-                        Value.ArrayValue arrayValue = (Value.ArrayValue) execStack.stack[base + arrayOperand.regnum];
+                    if (arrayStoreInst.arrayOperand() instanceof Operand.RegisterOperand arrayOperand) {
+                        Value.ArrayValue arrayValue = (Value.ArrayValue) execStack.stack[base + arrayOperand.frameSlot()];
                         int index = 0;
-                        if (arrayStoreInst.indexOperand instanceof Operand.ConstantOperand constant) {
+                        if (arrayStoreInst.indexOperand() instanceof Operand.ConstantOperand constant) {
                             index = (int) constant.value;
                         }
-                        else if (arrayStoreInst.indexOperand instanceof Operand.RegisterOperand registerOperand) {
-                            Value.IntegerValue indexValue = (Value.IntegerValue) execStack.stack[base + registerOperand.regnum];
+                        else if (arrayStoreInst.indexOperand() instanceof Operand.RegisterOperand registerOperand) {
+                            Value.IntegerValue indexValue = (Value.IntegerValue) execStack.stack[base + registerOperand.frameSlot()];
                             index = (int) indexValue.value;
                         }
                         else throw new IllegalStateException();
                         Value value;
-                        if (arrayStoreInst.sourceOperand instanceof Operand.ConstantOperand constantOperand) {
+                        if (arrayStoreInst.sourceOperand() instanceof Operand.ConstantOperand constantOperand) {
                             value = new Value.IntegerValue(constantOperand.value);
                         }
-                        else if (arrayStoreInst.sourceOperand instanceof Operand.RegisterOperand registerOperand) {
-                            value = execStack.stack[base + registerOperand.regnum];
+                        else if (arrayStoreInst.sourceOperand() instanceof Operand.RegisterOperand registerOperand) {
+                            value = execStack.stack[base + registerOperand.frameSlot()];
                         }
                         else throw new IllegalStateException();
                         arrayValue.values.set(index, value);
                     } else throw new IllegalStateException();
                 }
                 case Instruction.ArrayLoad arrayLoadInst -> {
-                    if (arrayLoadInst.arrayOperand instanceof Operand.RegisterOperand arrayOperand) {
-                        Value.ArrayValue arrayValue = (Value.ArrayValue) execStack.stack[base + arrayOperand.regnum];
-                        if (arrayLoadInst.indexOperand instanceof Operand.ConstantOperand constant) {
-                            execStack.stack[base + arrayLoadInst.destOperand.regnum] = arrayValue.values.get((int) constant.value);
+                    if (arrayLoadInst.arrayOperand() instanceof Operand.RegisterOperand arrayOperand) {
+                        Value.ArrayValue arrayValue = (Value.ArrayValue) execStack.stack[base + arrayOperand.frameSlot()];
+                        if (arrayLoadInst.indexOperand() instanceof Operand.ConstantOperand constant) {
+                            execStack.stack[base + arrayLoadInst.destOperand().frameSlot()] = arrayValue.values.get((int) constant.value);
                         }
-                        else if (arrayLoadInst.indexOperand instanceof Operand.RegisterOperand registerOperand) {
-                            Value.IntegerValue index = (Value.IntegerValue) execStack.stack[base + registerOperand.regnum];
-                            execStack.stack[base + arrayLoadInst.destOperand.regnum] = arrayValue.values.get((int) index.value);
+                        else if (arrayLoadInst.indexOperand() instanceof Operand.RegisterOperand registerOperand) {
+                            Value.IntegerValue index = (Value.IntegerValue) execStack.stack[base + registerOperand.frameSlot()];
+                            execStack.stack[base + arrayLoadInst.destOperand().frameSlot()] = arrayValue.values.get((int) index.value);
                         }
                         else throw new IllegalStateException();
                     } else throw new IllegalStateException();
                 }
                 case Instruction.SetField setFieldInst -> {
-                    if (setFieldInst.structOperand instanceof Operand.RegisterOperand structOperand) {
-                        Value.StructValue structValue = (Value.StructValue) execStack.stack[base + structOperand.regnum];
+                    if (setFieldInst.structOperand() instanceof Operand.RegisterOperand structOperand) {
+                        Value.StructValue structValue = (Value.StructValue) execStack.stack[base + structOperand.frameSlot()];
                         int index = setFieldInst.fieldIndex;
                         Value value;
-                        if (setFieldInst.sourceOperand instanceof Operand.ConstantOperand constant) {
+                        if (setFieldInst.sourceOperand() instanceof Operand.ConstantOperand constant) {
                             value = new Value.IntegerValue(constant.value);
                         }
-                        else if (setFieldInst.sourceOperand instanceof Operand.RegisterOperand registerOperand) {
-                            value = execStack.stack[base + registerOperand.regnum];
+                        else if (setFieldInst.sourceOperand() instanceof Operand.RegisterOperand registerOperand) {
+                            value = execStack.stack[base + registerOperand.frameSlot()];
                         }
                         else throw new IllegalStateException();
                         structValue.fields[index] = value;
                     } else throw new IllegalStateException();
                 }
                 case Instruction.GetField getFieldInst -> {
-                    if (getFieldInst.structOperand instanceof Operand.RegisterOperand structOperand) {
-                        Value.StructValue structValue = (Value.StructValue) execStack.stack[base + structOperand.regnum];
+                    if (getFieldInst.structOperand() instanceof Operand.RegisterOperand structOperand) {
+                        Value.StructValue structValue = (Value.StructValue) execStack.stack[base + structOperand.frameSlot()];
                         int index = getFieldInst.fieldIndex;
-                        execStack.stack[base + getFieldInst.destOperand.regnum] = structValue.fields[index];
+                        execStack.stack[base + getFieldInst.destOperand().frameSlot()] = structValue.fields[index];
                     } else throw new IllegalStateException();
                 }
                 default -> throw new IllegalStateException("Unexpected value: " + instruction);
diff --git a/registervm/src/test/java/com/compilerprogramming/ezlang/compiler/TestCompiler.java b/registervm/src/test/java/com/compilerprogramming/ezlang/compiler/TestCompiler.java
index c2f9767..0b0d91f 100644
--- a/registervm/src/test/java/com/compilerprogramming/ezlang/compiler/TestCompiler.java
+++ b/registervm/src/test/java/com/compilerprogramming/ezlang/compiler/TestCompiler.java
@@ -21,7 +21,7 @@ func foo(n: Int)->Int {
         String result = compileSrc(src);
         Assert.assertEquals("""
                 L0:
-                    %ret = 1
+                    ret 1
                     goto  L1
                 L1:
                 """, result);
@@ -37,7 +37,7 @@ func foo(n: Int)->Int {
         String result = compileSrc(src);
         Assert.assertEquals("""
                 L0:
-                    %ret = -1
+                    ret -1
                     goto  L1
                 L1:
                 """, result);
@@ -53,7 +53,7 @@ func foo(n: Int)->Int {
         String result = compileSrc(src);
         Assert.assertEquals("""
                 L0:
-                    %ret = n
+                    ret n
                     goto  L1
                 L1:
                 """, result);
@@ -70,7 +70,7 @@ func foo(n: Int)->Int {
         Assert.assertEquals("""
                 L0:
                     %t1 = -n
-                    %ret = %t1
+                    ret %t1
                     goto  L1
                 L1:
                 """, result);
@@ -87,7 +87,7 @@ func foo(n: Int)->Int {
         Assert.assertEquals("""
                 L0:
                     %t1 = n+1
-                    %ret = %t1
+                    ret %t1
                     goto  L1
                 L1:
                 """, result);
@@ -103,7 +103,7 @@ func foo(n: Int)->Int {
         String result = compileSrc(src);
         Assert.assertEquals("""
                 L0:
-                    %ret = 2
+                    ret 2
                     goto  L1
                 L1:
                 """, result);
@@ -119,7 +119,7 @@ func foo(n: Int)->Int {
         String result = compileSrc(src);
         Assert.assertEquals("""
                 L0:
-                    %ret = 1
+                    ret 1
                     goto  L1
                 L1:
                 """, result);
@@ -135,7 +135,7 @@ func foo(n: Int)->Int {
         String result = compileSrc(src);
         Assert.assertEquals("""
                 L0:
-                    %ret = 1
+                    ret 1
                     goto  L1
                 L1:
                 """, result);
@@ -151,7 +151,7 @@ func foo(n: Int)->Int {
         String result = compileSrc(src);
         Assert.assertEquals("""
                 L0:
-                    %ret = 0
+                    ret 0
                     goto  L1
                 L1:
                 """, result);
@@ -168,7 +168,7 @@ func foo(n: [Int])->Int {
         Assert.assertEquals("""
                 L0:
                     %t1 = n[0]
-                    %ret = %t1
+                    ret %t1
                     goto  L1
                 L1:
                 """, result);
@@ -187,7 +187,7 @@ func foo(n: [Int])->Int {
                     %t1 = n[0]
                     %t2 = n[1]
                     %t1 = %t1+%t2
-                    %ret = %t1
+                    ret %t1
                     goto  L1
                 L1:
                 """, result);
@@ -207,7 +207,7 @@ func foo()->[Int] {
                     %t0.append(1)
                     %t0.append(2)
                     %t0.append(3)
-                    %ret = %t0
+                    ret %t0
                     goto  L1
                 L1:
                 """, result);
@@ -225,7 +225,7 @@ func foo(n: Int) -> [Int] {
                 L0:
                     %t1 = New([Int])
                     %t1.append(n)
-                    %ret = %t1
+                    ret %t1
                     goto  L1
                 L1:
                 """, result);
@@ -242,7 +242,7 @@ func add(x: Int, y: Int) -> Int {
         Assert.assertEquals("""
                 L0:
                     %t2 = x+y
-                    %ret = %t2
+                    ret %t2
                     goto  L1
                 L1:
                 """, result);
@@ -287,7 +287,7 @@ func foo() -> Person {
                     %t0 = New(Person)
                     %t0.age = 10
                     %t0.children = 0
-                    %ret = %t0
+                    ret %t0
                     goto  L1
                 L1:
                 """, result);
@@ -326,11 +326,11 @@ func min(x: Int, y: Int) -> Int {
                     %t2 = x<y
                     if %t2 goto L2 else goto L3
                 L2:
-                    %ret = x
+                    ret x
                     goto  L1
                 L1:
                 L3:
-                    %ret = y
+                    ret y
                     goto  L1
                 """, result);
     }
@@ -456,7 +456,7 @@ public void testFunction24() {
         Assert.assertEquals("""
                 L0:
                     %t2 = x+y
-                    %ret = %t2
+                    ret %t2
                     goto  L1
                 L1:
                 L0:
@@ -465,7 +465,7 @@ public void testFunction24() {
                     %t1 = call foo params %t1, %t2
                     t = %t1
                     %t1 = t+1
-                    %ret = %t1
+                    ret %t1
                     goto  L1
                 L1:
                 """, result);
@@ -487,7 +487,7 @@ func foo(p: Person) -> Int {
         Assert.assertEquals("""
                 L0:
                     %t1 = p.age
-                    %ret = %t1
+                    ret %t1
                     goto  L1
                 L1:
                 """, result);
@@ -510,7 +510,7 @@ func foo(p: Person) -> Int {
                 L0:
                     %t1 = p.parent
                     %t1 = %t1.age
-                    %ret = %t1
+                    ret %t1
                     goto  L1
                 L1:
                 """, result);
@@ -534,7 +534,7 @@ func foo(p: [Person], i: Int) -> Int {
                     %t2 = p[i]
                     %t2 = %t2.parent
                     %t2 = %t2.age
-                    %ret = %t2
+                    ret %t2
                     goto  L1
                 L1:
                 """, result);
@@ -550,7 +550,7 @@ public void testFunction28() {
         Assert.assertEquals("""
                 L0:
                     %t2 = x+y
-                    %ret = %t2
+                    ret %t2
                     goto  L1
                 L1:
                 L0:
@@ -559,7 +559,7 @@ public void testFunction28() {
                     %t2 = call foo params %t2, %t3
                     t = %t2
                     %t2 = t+1
-                    %ret = %t2
+                    ret %t2
                     goto  L1
                 L1:
                 """, result);

From f01de7b70636ea8a57214cd1a832263e33d28213 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Sun, 16 Feb 2025 11:42:48 +0000
Subject: [PATCH 029/119] Improved type checking - also some fixes to test
 cases that broke after type checking was improved

---
 .../ezlang/compiler/TestSSATransform.java     |  2 +-
 .../ezlang/interpreter/TestInterpreter.java   | 20 ++---
 .../ezlang/semantic/SemaAssignTypes.java      | 21 +++++-
 .../ezlang/semantic/SemaDefineTypes.java      |  2 +-
 .../ezlang/semantic/TestSemaAssignTypes.java  | 74 ++++++++++++++++++-
 5 files changed, 102 insertions(+), 17 deletions(-)

diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
index 27387b2..d4d1026 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
@@ -788,7 +788,7 @@ func bar(x: Int)->Int {
                     return x;
                 }
 
-                func foo() {
+                func foo()->Int {
                     return bar(10);
                 }
                 """;
diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java b/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java
index 2ccaca5..01eb89c 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java
@@ -167,7 +167,7 @@ func fib(n: Int)->Int {
                     return f2;
                 }
 
-                func foo() {
+                func foo()->Int {
                     return fib(10);
                 }
                 """;
@@ -229,7 +229,7 @@ func bar(data: [Int]) {
                     else
                         data[2] = 123 + j + 10
                 }
-                func foo() {
+                func foo()->Int {
                     var data = new [Int] {0,0,0}
                     bar(data)
                     return data[0]+data[1]+data[2];
@@ -253,7 +253,7 @@ func bar(data: [Int]) {
                     else
                         data[2] = 123 - j + 10
                 }
-                func foo() {
+                func foo()->Int {
                     var data = new [Int] {0,0,0}
                     bar(data)
                     return data[0]+data[1]+data[2];
@@ -277,7 +277,7 @@ func bar(data: [Int]) {
                         data[2] = 15
                     data[3] = j + 21    
                 }
-                func foo() {
+                func foo()->Int {
                     var data = new [Int] {0,0,0,0}
                     bar(data)
                     return data[0]+data[1]+data[2]+data[3];
@@ -301,7 +301,7 @@ func bar(data: [Int]) {
                         data[2] = j * 15
                     data[3] = j * 21    
                 }
-                func foo() {
+                func foo()->Int {
                     var data = new [Int] {0,0,0,0}
                     bar(data)
                     return data[0]+data[1]+data[2]+data[3];
@@ -329,7 +329,7 @@ func bar(data: [Int]) {
                     }
                     data[3] = j + 21
                 }
-                func foo() {
+                func foo()->Int {
                     var data = new [Int] {1,0,0,0}
                     bar(data)
                     return data[0]+data[1]+data[2]+data[3]
@@ -361,7 +361,7 @@ func bar(data: [Int]) {
                     }
                     data[3] = (j+k) * 21
                 }
-                func foo() {
+                func foo()->Int {
                     var data = new [Int] {1,0,0,0}
                     bar(data)
                     return data[0]+data[1]+data[2]+data[3]
@@ -389,7 +389,7 @@ func bar(data: [Int]) {
                     data[1] = j
                     data[2] = i
                 }
-                func foo() {
+                func foo()->Int {
                     var data = new [Int] {2,0,0}
                     bar(data)
                     return data[0]+data[1]+data[2];
@@ -411,7 +411,7 @@ func bar(data: [Int]) {
                     else j = data[0]
                     data[0] = j * 21 + data[1]
                 }
-                func foo() {
+                func foo()->Int {
                     var data = new [Int] {2,3}
                     bar(data)
                     return data[0]+data[1];
@@ -433,7 +433,7 @@ func bar(data: [Int]) {
                         j = j * 21 + 25 / j
                     data[1] = j
                 }
-                func foo() {
+                func foo()->Int {
                     var data = new [Int] {5,3}
                     bar(data)
                     return data[0]+data[1];
diff --git a/semantic/src/main/java/com/compilerprogramming/ezlang/semantic/SemaAssignTypes.java b/semantic/src/main/java/com/compilerprogramming/ezlang/semantic/SemaAssignTypes.java
index f3a87d2..d996df5 100644
--- a/semantic/src/main/java/com/compilerprogramming/ezlang/semantic/SemaAssignTypes.java
+++ b/semantic/src/main/java/com/compilerprogramming/ezlang/semantic/SemaAssignTypes.java
@@ -216,15 +216,24 @@ public ASTVisitor visit(AST.NewExpr newExpr, boolean enter) {
         if (newExpr.typeExpr.type == null)
             throw new CompilerException("Unresolved type in new expression");
         validType(newExpr.typeExpr.type, false);
-        if (newExpr.typeExpr.type instanceof Type.TypeStruct ||
-            newExpr.typeExpr.type instanceof Type.TypeArray) {
+        if (newExpr.typeExpr.type instanceof Type.TypeNullable)
+            throw new CompilerException("new cannot be used to create a Nullable type");
+        if (newExpr.typeExpr.type instanceof Type.TypeStruct typeStruct) {
             newExpr.type = newExpr.typeExpr.type;
             for (AST.Expr expr: newExpr.initExprList) {
                 if (expr instanceof AST.SetFieldExpr setFieldExpr) {
                     setFieldExpr.objectType = newExpr.typeExpr.type;
+                    var fieldType = typeStruct.getField(setFieldExpr.fieldName);
+                    checkAssignmentCompatible(fieldType, setFieldExpr.value.type);
                 }
             }
         }
+        else if (newExpr.typeExpr.type instanceof Type.TypeArray arrayType) {
+            newExpr.type = newExpr.typeExpr.type;
+            for (AST.Expr expr: newExpr.initExprList) {
+                checkAssignmentCompatible(arrayType.getElementType(), expr.type);
+            }
+        }
         else
             throw new CompilerException("Unsupported type in new expression");
         return this;
@@ -258,8 +267,14 @@ public ASTVisitor visit(AST.ContinueStmt continueStmt, boolean enter) {
     public ASTVisitor visit(AST.ReturnStmt returnStmt, boolean enter) {
         if (enter)
             return this;
-        if (returnStmt.expr != null)
+        Type.TypeFunction functionType = (Type.TypeFunction) currentFuncDecl.symbol.type;
+        if (returnStmt.expr != null) {
             validType(returnStmt.expr.type, false);
+            checkAssignmentCompatible(functionType.returnType, returnStmt.expr.type);
+        }
+        else if (!(functionType.returnType instanceof Type.TypeVoid)) {
+            throw new CompilerException("A return value of type " + functionType.returnType + " is expected");
+        }
         return this;
     }
 
diff --git a/semantic/src/main/java/com/compilerprogramming/ezlang/semantic/SemaDefineTypes.java b/semantic/src/main/java/com/compilerprogramming/ezlang/semantic/SemaDefineTypes.java
index 95d1ab8..f0bf0c6 100644
--- a/semantic/src/main/java/com/compilerprogramming/ezlang/semantic/SemaDefineTypes.java
+++ b/semantic/src/main/java/com/compilerprogramming/ezlang/semantic/SemaDefineTypes.java
@@ -162,7 +162,7 @@ Type getNullableSimpleType(AST.NullableSimpleTypeExpr simpleTypeExpr) {
         else
             baseType = typeSymbol.type;
         if (baseType.isPrimitive())
-            throw new CompilerException("Cannot make nullable instance of primitive type");
+            throw new CompilerException("Cannot make Nullable instance of primitive type");
         return typeDictionary.intern(new Type.TypeNullable(baseType));
     }
 
diff --git a/semantic/src/test/java/com/compilerprogramming/ezlang/semantic/TestSemaAssignTypes.java b/semantic/src/test/java/com/compilerprogramming/ezlang/semantic/TestSemaAssignTypes.java
index 39b8302..91f6700 100644
--- a/semantic/src/test/java/com/compilerprogramming/ezlang/semantic/TestSemaAssignTypes.java
+++ b/semantic/src/test/java/com/compilerprogramming/ezlang/semantic/TestSemaAssignTypes.java
@@ -298,14 +298,14 @@ public void test13() {
     {
         var bar: [Int]
     }
-    func foo()->Foo 
+    func foo()->Int 
     {
        var f: Foo
        f = new Foo{}
        return f.bar[0]
     }
 """;
-        analyze(src, "foo", "func foo()->Foo");
+        analyze(src, "foo", "func foo()->Int");
     }
 
     @Test(expected = CompilerException.class)
@@ -347,6 +347,76 @@ func foo()
     {
        var f = null
     }
+""";
+        analyze(src, "foo", "func foo()");
+    }
+
+    @Test(expected = CompilerException.class)
+    public void test17() {
+        String src = """
+    func foo()
+    {
+       var f = new [Int] {null}
+    }
+""";
+        analyze(src, "foo", "func foo()");
+    }
+
+    @Test(expected = CompilerException.class)
+    public void test18() {
+        String src = """
+    func foo()
+    {
+       var f = new [Int?] {null}
+    }
+""";
+        analyze(src, "foo", "func foo()");
+    }
+
+    @Test
+    public void test19() {
+        String src = """
+    struct Foo { var bar: Int }
+    func foo()
+    {
+       var f = new [Foo?] {null}
+    }
+""";
+        analyze(src, "foo", "func foo()");
+    }
+
+    @Test
+    public void test20() {
+        String src = """
+    struct Foo { var bar: Int }
+    func foo()
+    {
+       var f = new [Foo?] {new Foo{ bar = 1}}
+    }
+""";
+        analyze(src, "foo", "func foo()");
+    }
+
+    @Test(expected = CompilerException.class)
+    public void test21() {
+        String src = """
+    struct Foo { var bar: Int }
+    func foo()
+    {
+       var f = new [Foo?] {new Foo{ bar = null}}
+    }
+""";
+        analyze(src, "foo", "func foo()");
+    }
+
+    @Test(expected = CompilerException.class)
+    public void test22() {
+        String src = """
+    struct Foo { var bar: Int }
+    func foo()
+    {
+       var f = new [Foo?]? {}
+    }
 """;
         analyze(src, "foo", "func foo()");
     }

From 2c2cabcc9ef04c5c7c5ca21ccf63f605fb1cef6c Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Sun, 16 Feb 2025 13:26:15 +0000
Subject: [PATCH 030/119] Improved type checking - also some fixes to test
 cases that broke after type checking was improved

---
 .../compilerprogramming/ezlang/semantic/SemaAssignTypes.java    | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/semantic/src/main/java/com/compilerprogramming/ezlang/semantic/SemaAssignTypes.java b/semantic/src/main/java/com/compilerprogramming/ezlang/semantic/SemaAssignTypes.java
index d996df5..d5504f4 100644
--- a/semantic/src/main/java/com/compilerprogramming/ezlang/semantic/SemaAssignTypes.java
+++ b/semantic/src/main/java/com/compilerprogramming/ezlang/semantic/SemaAssignTypes.java
@@ -269,7 +269,7 @@ public ASTVisitor visit(AST.ReturnStmt returnStmt, boolean enter) {
             return this;
         Type.TypeFunction functionType = (Type.TypeFunction) currentFuncDecl.symbol.type;
         if (returnStmt.expr != null) {
-            validType(returnStmt.expr.type, false);
+            validType(returnStmt.expr.type, true);
             checkAssignmentCompatible(functionType.returnType, returnStmt.expr.type);
         }
         else if (!(functionType.returnType instanceof Type.TypeVoid)) {

From 985cf88354364012eca206c5c50647a9510f59e2 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Sun, 16 Feb 2025 23:32:50 +0000
Subject: [PATCH 031/119] Initial work on implementing Null support in
 registervm - as well as boolean expressions in the compiler

---
 .../ezlang/compiler/CompiledFunction.java     | 75 ++++++++++++++-
 .../ezlang/compiler/Compiler.java             |  2 +-
 .../ezlang/compiler/Operand.java              | 10 ++
 .../ezlang/interpreter/Interpreter.java       | 95 ++++++++++++++-----
 .../ezlang/interpreter/Value.java             |  3 +
 .../ezlang/compiler/TestCompiler.java         | 16 ++++
 .../ezlang/interpreter/TestInterpreter.java   | 74 +++++++++++++++
 .../ezlang/semantic/SemaAssignTypes.java      |  2 +
 8 files changed, 247 insertions(+), 30 deletions(-)

diff --git a/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java b/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
index 56d5f55..ef5a842 100644
--- a/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
+++ b/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
@@ -5,6 +5,7 @@
 import com.compilerprogramming.ezlang.types.Scope;
 import com.compilerprogramming.ezlang.types.Symbol;
 import com.compilerprogramming.ezlang.types.Type;
+import com.compilerprogramming.ezlang.types.TypeDictionary;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -19,6 +20,7 @@ public class CompiledFunction {
     private BasicBlock currentContinueTarget;
     public int maxLocalReg;
     public int maxStackSize;
+    private final TypeDictionary typeDictionary;
 
     /**
      * We essentially do a form of abstract interpretation as we generate
@@ -30,7 +32,7 @@ public class CompiledFunction {
      */
     private List<Operand> virtualStack = new ArrayList<>();
 
-    public CompiledFunction(Symbol.FunctionTypeSymbol functionSymbol) {
+    public CompiledFunction(Symbol.FunctionTypeSymbol functionSymbol, TypeDictionary typeDictionary) {
         AST.FuncDecl funcDecl = (AST.FuncDecl) functionSymbol.functionDecl;
         setVirtualRegisters(funcDecl.scope);
         this.BID = 0;
@@ -38,6 +40,7 @@ public CompiledFunction(Symbol.FunctionTypeSymbol functionSymbol) {
         this.exit = createBlock();
         this.currentBreakTarget = null;
         this.currentContinueTarget = null;
+        this.typeDictionary = typeDictionary;
         compileStatement(funcDecl.block);
         exitBlockIfNeeded();
     }
@@ -408,18 +411,72 @@ private boolean compileSymbolExpr(AST.NameExpr symbolExpr) {
         return false;
     }
 
+    private boolean codeBoolean(AST.BinaryExpr binaryExpr) {
+        BasicBlock l1;
+        BasicBlock l2;
+        BasicBlock l3;
+        boolean indexed;
+        boolean isAnd;
+
+        l1 = createBlock();
+        l2 = createBlock();
+        l3 = createBlock();
+        indexed = compileExpr(binaryExpr.expr1);
+        if (indexed) {
+            codeIndexedLoad();
+        }
+        isAnd = binaryExpr.op.str.equals("&&");
+        // FIXME ensure temp
+        var operand = pop();
+        var temp = createTemp(typeDictionary.INT);
+        code(new Instruction.Move(operand, temp));
+        if (isAnd) {
+            code(new Instruction.ConditionalBranch(currentBlock, pop(), l1, l2));
+        } else {
+            code(new Instruction.ConditionalBranch(currentBlock, pop(), l2, l1));
+        }
+        startBlock(l1);
+        indexed = compileExpr(binaryExpr.expr2);
+        if (indexed) {
+            codeIndexedLoad();
+        }
+        operand = pop();
+        temp = createTemp(typeDictionary.INT);
+        code(new Instruction.Move(operand, temp));
+        jumpTo(l3);
+        startBlock(l2);
+        code(new Instruction.Move(new Operand.ConstantOperand(isAnd ? 0 : 1, typeDictionary.INT), temp));
+        jumpTo(l3);
+        startBlock(l3);
+        return false;
+    }
+
+
     private boolean compileBinaryExpr(AST.BinaryExpr binaryExpr) {
-        String opCode = null;
+        String opCode = binaryExpr.op.str;
+        if (opCode.equals("&&") ||
+            opCode.equals("||")) {
+            return codeBoolean(binaryExpr);
+        }
         boolean indexed = compileExpr(binaryExpr.expr1);
         if (indexed)
             codeIndexedLoad();
         indexed = compileExpr(binaryExpr.expr2);
         if (indexed)
             codeIndexedLoad();
-        opCode = binaryExpr.op.str;
         Operand right = pop();
         Operand left = pop();
-        if (left instanceof Operand.ConstantOperand leftconstant &&
+        if (left instanceof Operand.NullConstantOperand &&
+            right instanceof Operand.NullConstantOperand) {
+            long value = 0;
+            switch (opCode) {
+                case "==": value = 1; break;
+                case "!=": value = 0; break;
+                default: throw new CompilerException("Invalid binary op");
+            }
+            pushConstant(value, typeDictionary.INT);
+        }
+        else if (left instanceof Operand.ConstantOperand leftconstant &&
                 right instanceof Operand.ConstantOperand rightconstant) {
             long value = 0;
             switch (opCode) {
@@ -468,7 +525,11 @@ private boolean compileUnaryExpr(AST.UnaryExpr unaryExpr) {
     }
 
     private boolean compileConstantExpr(AST.LiteralExpr constantExpr) {
-        pushConstant(constantExpr.value.num.intValue(), constantExpr.type);
+        if (constantExpr.type instanceof Type.TypeInteger)
+            pushConstant(constantExpr.value.num.intValue(), constantExpr.type);
+        else if (constantExpr.type instanceof Type.TypeNull)
+            pushNullConstant(constantExpr.type);
+        else throw new CompilerException("Invalid constant type");
         return false;
     }
 
@@ -476,6 +537,10 @@ private void pushConstant(long value, Type type) {
         pushOperand(new Operand.ConstantOperand(value, type));
     }
 
+    private void pushNullConstant(Type type) {
+        pushOperand(new Operand.NullConstantOperand(type));
+    }
+
     private Operand.TempRegisterOperand createTemp(Type type) {
         var tempRegister = new Operand.TempRegisterOperand(virtualStack.size()+maxLocalReg, type);
         pushOperand(tempRegister);
diff --git a/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java b/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java
index 5c0b365..37eaea0 100644
--- a/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java
+++ b/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java
@@ -16,7 +16,7 @@ private void compile(TypeDictionary typeDictionary) {
         for (Symbol symbol: typeDictionary.getLocalSymbols()) {
             if (symbol instanceof Symbol.FunctionTypeSymbol functionSymbol) {
                 Type.TypeFunction functionType = (Type.TypeFunction) functionSymbol.type;
-                functionType.code = new CompiledFunction(functionSymbol);
+                functionType.code = new CompiledFunction(functionSymbol, typeDictionary);
             }
         }
     }
diff --git a/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/Operand.java b/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/Operand.java
index 9cb9a70..54188da 100644
--- a/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/Operand.java
+++ b/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/Operand.java
@@ -18,6 +18,16 @@ public String toString() {
         }
     }
 
+    public static class NullConstantOperand extends Operand {
+        public NullConstantOperand(Type type) {
+            this.type = type;
+        }
+        @Override
+        public String toString() {
+            return "null";
+        }
+    }
+
     public static abstract class RegisterOperand extends Operand {
         public final int regnum;
         public RegisterOperand(int regnum) {
diff --git a/registervm/src/main/java/com/compilerprogramming/ezlang/interpreter/Interpreter.java b/registervm/src/main/java/com/compilerprogramming/ezlang/interpreter/Interpreter.java
index 7be8ef9..5ceaeaa 100644
--- a/registervm/src/main/java/com/compilerprogramming/ezlang/interpreter/Interpreter.java
+++ b/registervm/src/main/java/com/compilerprogramming/ezlang/interpreter/Interpreter.java
@@ -48,6 +48,9 @@ public Value interpret(ExecutionStack execStack, Frame frame) {
                     if (retInst.value() instanceof Operand.ConstantOperand constantOperand) {
                         execStack.stack[base] = new Value.IntegerValue(constantOperand.value);
                     }
+                    else if (retInst.value() instanceof Operand.NullConstantOperand) {
+                        execStack.stack[base] = new Value.NullValue();
+                    }
                     else if (retInst.value() instanceof Operand.RegisterOperand registerOperand) {
                         execStack.stack[base] = execStack.stack[base+registerOperand.frameSlot()];
                     }
@@ -63,6 +66,9 @@ else if (retInst.value() instanceof Operand.RegisterOperand registerOperand) {
                         else if (moveInst.from() instanceof Operand.ConstantOperand constantOperand) {
                             execStack.stack[base + toReg.frameSlot()] = new Value.IntegerValue(constantOperand.value);
                         }
+                        else if (moveInst.from() instanceof Operand.NullConstantOperand) {
+                            execStack.stack[base + toReg.frameSlot()] = new Value.NullValue();
+                        }
                         else throw new IllegalStateException();
                     }
                     else throw new IllegalStateException();
@@ -107,6 +113,9 @@ else if (cbrInst.condition() instanceof Operand.ConstantOperand constantOperand)
                         else if (arg instanceof Operand.ConstantOperand constantOperand) {
                             execStack.stack[base + reg] = new Value.IntegerValue(constantOperand.value);
                         }
+                        else if (arg instanceof Operand.NullConstantOperand) {
+                            execStack.stack[base + reg] = new Value.NullValue();
+                        }
                         reg += 1;
                     }
                     // Call function
@@ -135,31 +144,60 @@ else if (arg instanceof Operand.ConstantOperand constantOperand) {
                 case Instruction.Binary binaryInst -> {
                     long x, y;
                     long value = 0;
-                    if (binaryInst.left() instanceof Operand.ConstantOperand constant)
-                        x = constant.value;
-                    else if (binaryInst.left() instanceof Operand.RegisterOperand registerOperand)
-                        x = ((Value.IntegerValue) execStack.stack[base + registerOperand.frameSlot()]).value;
-                    else throw new IllegalStateException();
-                    if (binaryInst.right() instanceof Operand.ConstantOperand constant)
-                        y = constant.value;
-                    else if (binaryInst.right() instanceof Operand.RegisterOperand registerOperand)
-                        y = ((Value.IntegerValue) execStack.stack[base + registerOperand.frameSlot()]).value;
-                    else throw new IllegalStateException();
-                    switch (binaryInst.binOp) {
-                        case "+": value = x + y; break;
-                        case "-": value = x - y; break;
-                        case "*": value = x * y; break;
-                        case "/": value = x / y; break;
-                        case "%": value = x % y; break;
-                        case "==": value = x == y ? 1 : 0; break;
-                        case "!=": value = x != y ? 1 : 0; break;
-                        case "<": value = x < y ? 1: 0; break;
-                        case ">": value = x > y ? 1 : 0; break;
-                        case "<=": value = x <= y ? 1 : 0; break;
-                        case ">=": value = x <= y ? 1 : 0; break;
-                        default: throw new IllegalStateException();
+                    boolean intOp = true;
+                    if (binaryInst.binOp.equals("==") || binaryInst.binOp.equals("!=")) {
+                        Operand.RegisterOperand nonNullLitOperand = null;
+                        if (binaryInst.left() instanceof Operand.NullConstantOperand) {
+                            nonNullLitOperand = (Operand.RegisterOperand)binaryInst.right();
+                        }
+                        else if (binaryInst.right() instanceof Operand.NullConstantOperand) {
+                            nonNullLitOperand = (Operand.RegisterOperand)binaryInst.left();
+                        }
+                        if (nonNullLitOperand != null) {
+                            intOp = false;
+                            Value otherValue = execStack.stack[base + nonNullLitOperand.frameSlot()];
+                            switch (binaryInst.binOp) {
+                                case "==": {
+                                    value = otherValue instanceof Value.NullValue ? 1 : 0;
+                                    break;
+                                }
+                                case "!=": {
+                                    value = otherValue instanceof Value.NullValue ? 0 : 1;
+                                    break;
+                                }
+                                default:
+                                    throw new IllegalStateException();
+                            }
+                            execStack.stack[base + binaryInst.result().frameSlot()] = new Value.IntegerValue(value);
+                        }
+                    }
+                    if (intOp) {
+                        if (binaryInst.left() instanceof Operand.ConstantOperand constant)
+                            x = constant.value;
+                        else if (binaryInst.left() instanceof Operand.RegisterOperand registerOperand)
+                            x = ((Value.IntegerValue) execStack.stack[base + registerOperand.frameSlot()]).value;
+                        else throw new IllegalStateException();
+                        if (binaryInst.right() instanceof Operand.ConstantOperand constant)
+                            y = constant.value;
+                        else if (binaryInst.right() instanceof Operand.RegisterOperand registerOperand)
+                            y = ((Value.IntegerValue) execStack.stack[base + registerOperand.frameSlot()]).value;
+                        else throw new IllegalStateException();
+                        switch (binaryInst.binOp) {
+                            case "+": value = x + y; break;
+                            case "-": value = x - y; break;
+                            case "*": value = x * y; break;
+                            case "/": value = x / y; break;
+                            case "%": value = x % y; break;
+                            case "==": value = x == y ? 1 : 0; break;
+                            case "!=": value = x != y ? 1 : 0; break;
+                            case "<": value = x < y ? 1: 0; break;
+                            case ">": value = x > y ? 1 : 0; break;
+                            case "<=": value = x <= y ? 1 : 0; break;
+                            case ">=": value = x <= y ? 1 : 0; break;
+                            default: throw new IllegalStateException();
+                        }
+                        execStack.stack[base + binaryInst.result().frameSlot()] = new Value.IntegerValue(value);
                     }
-                    execStack.stack[base + binaryInst.result().frameSlot()] = new Value.IntegerValue(value);
                 }
                 case Instruction.NewArray newArrayInst -> {
                     execStack.stack[base + newArrayInst.destOperand().frameSlot()] = new Value.ArrayValue(newArrayInst.type);
@@ -172,6 +210,9 @@ else if (binaryInst.right() instanceof Operand.RegisterOperand registerOperand)
                     if (arrayAppendInst.value() instanceof Operand.ConstantOperand constant) {
                         arrayValue.values.add(new Value.IntegerValue(constant.value));
                     }
+                    else if (arrayAppendInst.value() instanceof Operand.NullConstantOperand) {
+                        arrayValue.values.add(new Value.NullValue());
+                    }
                     else if (arrayAppendInst.value() instanceof Operand.RegisterOperand registerOperand) {
                         arrayValue.values.add(execStack.stack[base + registerOperand.frameSlot()]);
                     }
@@ -193,6 +234,9 @@ else if (arrayStoreInst.indexOperand() instanceof Operand.RegisterOperand regist
                         if (arrayStoreInst.sourceOperand() instanceof Operand.ConstantOperand constantOperand) {
                             value = new Value.IntegerValue(constantOperand.value);
                         }
+                        else if (arrayStoreInst.sourceOperand() instanceof Operand.NullConstantOperand) {
+                            value = new Value.NullValue();
+                        }
                         else if (arrayStoreInst.sourceOperand() instanceof Operand.RegisterOperand registerOperand) {
                             value = execStack.stack[base + registerOperand.frameSlot()];
                         }
@@ -221,6 +265,9 @@ else if (arrayLoadInst.indexOperand() instanceof Operand.RegisterOperand registe
                         if (setFieldInst.sourceOperand() instanceof Operand.ConstantOperand constant) {
                             value = new Value.IntegerValue(constant.value);
                         }
+                        else if (setFieldInst.sourceOperand() instanceof Operand.NullConstantOperand) {
+                            value = new Value.NullValue();
+                        }
                         else if (setFieldInst.sourceOperand() instanceof Operand.RegisterOperand registerOperand) {
                             value = execStack.stack[base + registerOperand.frameSlot()];
                         }
diff --git a/registervm/src/main/java/com/compilerprogramming/ezlang/interpreter/Value.java b/registervm/src/main/java/com/compilerprogramming/ezlang/interpreter/Value.java
index e03fb80..7f914a6 100644
--- a/registervm/src/main/java/com/compilerprogramming/ezlang/interpreter/Value.java
+++ b/registervm/src/main/java/com/compilerprogramming/ezlang/interpreter/Value.java
@@ -11,6 +11,9 @@ public IntegerValue(long value) {
         }
         public final long value;
     }
+    static public class NullValue extends Value {
+        public NullValue() {}
+    }
     static public class ArrayValue extends Value {
         public final Type.TypeArray arrayType;
         public final ArrayList<Value> values;
diff --git a/registervm/src/test/java/com/compilerprogramming/ezlang/compiler/TestCompiler.java b/registervm/src/test/java/com/compilerprogramming/ezlang/compiler/TestCompiler.java
index 0b0d91f..2deb384 100644
--- a/registervm/src/test/java/com/compilerprogramming/ezlang/compiler/TestCompiler.java
+++ b/registervm/src/test/java/com/compilerprogramming/ezlang/compiler/TestCompiler.java
@@ -564,4 +564,20 @@ public void testFunction28() {
                 L1:
                 """, result);
     }
+
+    @Test
+    public void testFunction29() {
+        String src = """
+                struct Foo { var bar: Int }
+                func foo()->Foo? { return null; }
+                """;
+        String result = compileSrc(src);
+        Assert.assertEquals("""
+L0:
+    ret null
+    goto  L1
+L1:
+""", result);
+    }
+
 }
diff --git a/registervm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java b/registervm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java
index 4913352..c99ce22 100644
--- a/registervm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java
+++ b/registervm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java
@@ -102,4 +102,78 @@ func bar()->Int { return foo().field }
         Assert.assertTrue(value instanceof Value.IntegerValue integerValue
                 && integerValue.value == 42);
     }
+
+    @Test
+    public void testFunction100() {
+        String src = """
+                struct Test
+                {
+                    var field: Int
+                }
+                func foo()->Test? 
+                {
+                    return null;
+                }
+
+                """;
+        var value = compileAndRun(src, "foo");
+        Assert.assertNotNull(value);
+        Assert.assertTrue(value instanceof Value.NullValue);
+    }
+
+    @Test
+    public void testFunction101() {
+        String src = """
+                func foo()->Int 
+                {
+                    return null == null;
+                }
+
+                """;
+        var value = compileAndRun(src, "foo");
+        Assert.assertNotNull(value);
+        Assert.assertTrue(value instanceof Value.IntegerValue integerValue &&
+                integerValue.value == 1);
+    }
+
+    @Test
+    public void testFunction102() {
+        String src = """
+                struct Foo
+                {
+                    var next: Foo?
+                }
+                func foo()->Int 
+                {
+                    var f = new Foo{ next = null }
+                    return null == f.next
+                }
+
+                """;
+        var value = compileAndRun(src, "foo");
+        Assert.assertNotNull(value);
+        Assert.assertTrue(value instanceof Value.IntegerValue integerValue &&
+                integerValue.value == 1);
+    }
+
+    @Test
+    public void testFunction103() {
+        String src = """
+                struct Foo
+                {
+                    var i: Int
+                }
+                func foo()->Int 
+                {
+                    var f = new [Foo?] { new Foo{i = 1}, null }
+                    return null == f[1] && 1 == f[0].i
+                }
+
+                """;
+        var value = compileAndRun(src, "foo");
+        Assert.assertNotNull(value);
+        Assert.assertTrue(value instanceof Value.IntegerValue integerValue &&
+                integerValue.value == 1);
+    }
+
 }
diff --git a/semantic/src/main/java/com/compilerprogramming/ezlang/semantic/SemaAssignTypes.java b/semantic/src/main/java/com/compilerprogramming/ezlang/semantic/SemaAssignTypes.java
index d5504f4..f22c855 100644
--- a/semantic/src/main/java/com/compilerprogramming/ezlang/semantic/SemaAssignTypes.java
+++ b/semantic/src/main/java/com/compilerprogramming/ezlang/semantic/SemaAssignTypes.java
@@ -77,6 +77,8 @@ public ASTVisitor visit(AST.BinaryExpr binaryExpr, boolean enter) {
             else if (((binaryExpr.expr1.type instanceof Type.TypeNull &&
                      binaryExpr.expr2.type instanceof Type.TypeNullable) ||
                     (binaryExpr.expr1.type instanceof Type.TypeNullable &&
+                     binaryExpr.expr2.type instanceof Type.TypeNull) ||
+                    (binaryExpr.expr1.type instanceof Type.TypeNull &&
                      binaryExpr.expr2.type instanceof Type.TypeNull)) &&
                     (binaryExpr.op.str.equals("==") || binaryExpr.op.str.equals("!="))) {
                 binaryExpr.type = typeDictionary.INT;

From 2fd61864a7793053ef68582bd005a67f230adef1 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <dibyendu@redukti.com>
Date: Tue, 18 Feb 2025 11:51:51 +0000
Subject: [PATCH 032/119] Ensure SSA tests check output

---
 .../ezlang/compiler/TestSSATransform.java     | 1294 ++++++++++++++++-
 1 file changed, 1250 insertions(+), 44 deletions(-)

diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
index d4d1026..703ca2a 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
@@ -1077,7 +1077,39 @@ func foo()->Int {
                 }
                 """;
         String result = compileSrc(src);
-        System.out.println(result);
+        Assert.assertEquals("""
+func foo
+Before SSA
+==========
+L0:
+    a = 5
+    b = 10
+    %t3 = a+b
+    c = %t3
+    ret c
+    goto  L1
+L1:
+After SSA
+=========
+L0:
+    a_0 = 5
+    b_0 = 10
+    %t3_0 = a_0+b_0
+    c_0 = %t3_0
+    ret c_0
+    goto  L1
+L1:
+After exiting SSA
+=================
+L0:
+    a_0 = 5
+    b_0 = 10
+    %t3_0 = a_0+b_0
+    c_0 = %t3_0
+    ret c_0
+    goto  L1
+L1:
+""", result);
     }
 
     @Test
@@ -1090,7 +1122,36 @@ func foo()->Int {
                 }
                 """;
         String result = compileSrc(src);
-        System.out.println(result);
+        Assert.assertEquals("""
+func foo
+Before SSA
+==========
+L0:
+    a = 5
+    %t1 = a+1
+    a = %t1
+    ret a
+    goto  L1
+L1:
+After SSA
+=========
+L0:
+    a_0 = 5
+    %t1_0 = a_0+1
+    a_1 = %t1_0
+    ret a_1
+    goto  L1
+L1:
+After exiting SSA
+=================
+L0:
+    a_0 = 5
+    %t1_0 = a_0+1
+    a_1 = %t1_0
+    ret a_1
+    goto  L1
+L1:
+""", result);
     }
 
     @Test
@@ -1107,7 +1168,60 @@ func foo()->Int {
                 }
                 """;
         String result = compileSrc(src);
-        System.out.println(result);
+        Assert.assertEquals("""
+func foo
+Before SSA
+==========
+L0:
+    a = 5
+    %t1 = a>3
+    if %t1 goto L2 else goto L3
+L2:
+    a = 10
+    goto  L4
+L4:
+    ret a
+    goto  L1
+L1:
+L3:
+    a = 20
+    goto  L4
+After SSA
+=========
+L0:
+    a_0 = 5
+    %t1_0 = a_0>3
+    if %t1_0 goto L2 else goto L3
+L2:
+    a_2 = 10
+    goto  L4
+L4:
+    a_3 = phi(a_2, a_1)
+    ret a_3
+    goto  L1
+L1:
+L3:
+    a_1 = 20
+    goto  L4
+After exiting SSA
+=================
+L0:
+    a_0 = 5
+    %t1_0 = a_0>3
+    if %t1_0 goto L2 else goto L3
+L2:
+    a_2 = 10
+    a_3 = a_2
+    goto  L4
+L4:
+    ret a_3
+    goto  L1
+L1:
+L3:
+    a_1 = 20
+    a_3 = a_1
+    goto  L4
+""", result);
     }
 
     @Test
@@ -1122,7 +1236,60 @@ func foo()->Int {
                 }
                 """;
         String result = compileSrc(src);
-        System.out.println(result);
+        Assert.assertEquals("""
+func foo
+Before SSA
+==========
+L0:
+    a = 0
+    goto  L2
+L2:
+    %t1 = a<5
+    if %t1 goto L3 else goto L4
+L3:
+    %t2 = a+1
+    a = %t2
+    goto  L2
+L4:
+    ret a
+    goto  L1
+L1:
+After SSA
+=========
+L0:
+    a_0 = 0
+    goto  L2
+L2:
+    a_1 = phi(a_0, a_2)
+    %t1_0 = a_1<5
+    if %t1_0 goto L3 else goto L4
+L3:
+    %t2_0 = a_1+1
+    a_2 = %t2_0
+    goto  L2
+L4:
+    ret a_1
+    goto  L1
+L1:
+After exiting SSA
+=================
+L0:
+    a_0 = 0
+    a_1 = a_0
+    goto  L2
+L2:
+    %t1_0 = a_1<5
+    if %t1_0 goto L3 else goto L4
+L3:
+    %t2_0 = a_1+1
+    a_2 = %t2_0
+    a_1 = a_2
+    goto  L2
+L4:
+    ret a_1
+    goto  L1
+L1:
+""", result);
     }
 
     @Test
@@ -1144,7 +1311,90 @@ func foo()->Int {
                 }
                 """;
         String result = compileSrc(src);
-        System.out.println(result);
+        Assert.assertEquals("""
+func foo
+Before SSA
+==========
+L0:
+    a = 0
+    b = 10
+    %t2 = b>5
+    if %t2 goto L2 else goto L3
+L2:
+    %t3 = a<5
+    if %t3 goto L5 else goto L6
+L5:
+    a = 5
+    goto  L7
+L7:
+    goto  L4
+L4:
+    ret a
+    goto  L1
+L1:
+L6:
+    a = 15
+    goto  L7
+L3:
+    a = 20
+    goto  L4
+After SSA
+=========
+L0:
+    a_0 = 0
+    b_0 = 10
+    %t2_0 = b_0>5
+    if %t2_0 goto L2 else goto L3
+L2:
+    %t3_0 = a_0<5
+    if %t3_0 goto L5 else goto L6
+L5:
+    a_3 = 5
+    goto  L7
+L7:
+    a_4 = phi(a_3, a_2)
+    goto  L4
+L4:
+    a_5 = phi(a_4, a_1)
+    ret a_5
+    goto  L1
+L1:
+L6:
+    a_2 = 15
+    goto  L7
+L3:
+    a_1 = 20
+    goto  L4
+After exiting SSA
+=================
+L0:
+    a_0 = 0
+    b_0 = 10
+    %t2_0 = b_0>5
+    if %t2_0 goto L2 else goto L3
+L2:
+    %t3_0 = a_0<5
+    if %t3_0 goto L5 else goto L6
+L5:
+    a_3 = 5
+    a_4 = a_3
+    goto  L7
+L7:
+    a_5 = a_4
+    goto  L4
+L4:
+    ret a_5
+    goto  L1
+L1:
+L6:
+    a_2 = 15
+    a_4 = a_2
+    goto  L7
+L3:
+    a_1 = 20
+    a_5 = a_1
+    goto  L4
+""", result);
     }
 
     @Test
@@ -1158,7 +1408,48 @@ func foo()->Int {
                 }
                 """;
         String result = compileSrc(src);
-        System.out.println(result);
+        Assert.assertEquals("""
+func foo
+Before SSA
+==========
+L0:
+    %t2 = New([Int])
+    %t2.append(1)
+    %t2.append(2)
+    arr = %t2
+    arr[0] = 10
+    %t3 = arr[0]
+    x = %t3
+    ret x
+    goto  L1
+L1:
+After SSA
+=========
+L0:
+    %t2_0 = New([Int])
+    %t2_0.append(1)
+    %t2_0.append(2)
+    arr_0 = %t2_0
+    arr_0[0] = 10
+    %t3_0 = arr_0[0]
+    x_0 = %t3_0
+    ret x_0
+    goto  L1
+L1:
+After exiting SSA
+=================
+L0:
+    %t2_0 = New([Int])
+    %t2_0.append(1)
+    %t2_0.append(2)
+    arr_0 = %t2_0
+    arr_0[0] = 10
+    %t3_0 = arr_0[0]
+    x_0 = %t3_0
+    ret x_0
+    goto  L1
+L1:
+""", result);
     }
 
     @Test
@@ -1175,7 +1466,73 @@ func main()->Int {
         }
 """;
         String result = compileSrc(src);
-        System.out.println(result);
+        Assert.assertEquals("""
+func add
+Before SSA
+==========
+L0:
+    arg x
+    arg y
+    %t2 = x+y
+    ret %t2
+    goto  L1
+L1:
+After SSA
+=========
+L0:
+    arg x_0
+    arg y_0
+    %t2_0 = x_0+y_0
+    ret %t2_0
+    goto  L1
+L1:
+After exiting SSA
+=================
+L0:
+    arg x_0
+    arg y_0
+    %t2_0 = x_0+y_0
+    ret %t2_0
+    goto  L1
+L1:
+func main
+Before SSA
+==========
+L0:
+    a = 5
+    b = 10
+    %t3 = a
+    %t4 = b
+    %t5 = call add params %t3, %t4
+    c = %t5
+    ret c
+    goto  L1
+L1:
+After SSA
+=========
+L0:
+    a_0 = 5
+    b_0 = 10
+    %t3_0 = a_0
+    %t4_0 = b_0
+    %t5_0 = call add params %t3_0, %t4_0
+    c_0 = %t5_0
+    ret c_0
+    goto  L1
+L1:
+After exiting SSA
+=================
+L0:
+    a_0 = 5
+    b_0 = 10
+    %t3_0 = a_0
+    %t4_0 = b_0
+    %t5_0 = call add params %t3_0, %t4_0
+    c_0 = %t5_0
+    ret c_0
+    goto  L1
+L1:
+""", result);
     }
 
     @Test
@@ -1194,39 +1551,250 @@ func main()->Int {
         }
 """;
         String result = compileSrc(src);
-        System.out.println(result);
-    }
-
-    @Test
-    public void testSSA9() {
-        String src = """
-        func main()->Int {
-            var a = 0
-            var b = 0
-            var i = 0
-            var j = 0
-            while (i < 3) {
-                j = 0
-                while (j < 2) {
-                    a = a + 1
-                    i = j + 1
-                }
-                b = b + 1;
-                i = i + 1    
-            }
-            return a + b
-        }
-""";
-        String result = compileSrc(src);
-        System.out.println(result);
-    }
-
-    @Test
-    public void testSSA10() {
-        String src = """
-        func main()->Int {
-            var sum = 0
-            var i = 0
+        Assert.assertEquals("""
+func main
+Before SSA
+==========
+L0:
+    a = 0
+    b = 1
+    goto  L2
+L2:
+    %t2 = a<10
+    if %t2 goto L3 else goto L4
+L3:
+    %t3 = a+2
+    a = %t3
+    goto  L2
+L4:
+    goto  L5
+L5:
+    %t4 = b<20
+    if %t4 goto L6 else goto L7
+L6:
+    %t5 = b+3
+    b = %t5
+    goto  L5
+L7:
+    %t6 = a+b
+    ret %t6
+    goto  L1
+L1:
+After SSA
+=========
+L0:
+    a_0 = 0
+    b_0 = 1
+    goto  L2
+L2:
+    a_1 = phi(a_0, a_2)
+    %t2_0 = a_1<10
+    if %t2_0 goto L3 else goto L4
+L3:
+    %t3_0 = a_1+2
+    a_2 = %t3_0
+    goto  L2
+L4:
+    goto  L5
+L5:
+    b_1 = phi(b_0, b_2)
+    %t4_0 = b_1<20
+    if %t4_0 goto L6 else goto L7
+L6:
+    %t5_0 = b_1+3
+    b_2 = %t5_0
+    goto  L5
+L7:
+    %t6_0 = a_1+b_1
+    ret %t6_0
+    goto  L1
+L1:
+After exiting SSA
+=================
+L0:
+    a_0 = 0
+    b_0 = 1
+    a_1 = a_0
+    goto  L2
+L2:
+    %t2_0 = a_1<10
+    if %t2_0 goto L3 else goto L4
+L3:
+    %t3_0 = a_1+2
+    a_2 = %t3_0
+    a_1 = a_2
+    goto  L2
+L4:
+    b_1 = b_0
+    goto  L5
+L5:
+    %t4_0 = b_1<20
+    if %t4_0 goto L6 else goto L7
+L6:
+    %t5_0 = b_1+3
+    b_2 = %t5_0
+    b_1 = b_2
+    goto  L5
+L7:
+    %t6_0 = a_1+b_1
+    ret %t6_0
+    goto  L1
+L1:
+""", result);
+    }
+
+    @Test
+    public void testSSA9() {
+        String src = """
+        func main()->Int {
+            var a = 0
+            var b = 0
+            var i = 0
+            var j = 0
+            while (i < 3) {
+                j = 0
+                while (j < 2) {
+                    a = a + 1
+                    i = j + 1
+                }
+                b = b + 1;
+                i = i + 1    
+            }
+            return a + b
+        }
+""";
+        String result = compileSrc(src);
+        Assert.assertEquals("""
+func main
+Before SSA
+==========
+L0:
+    a = 0
+    b = 0
+    i = 0
+    j = 0
+    goto  L2
+L2:
+    %t4 = i<3
+    if %t4 goto L3 else goto L4
+L3:
+    j = 0
+    goto  L5
+L5:
+    %t5 = j<2
+    if %t5 goto L6 else goto L7
+L6:
+    %t6 = a+1
+    a = %t6
+    %t7 = j+1
+    i = %t7
+    goto  L5
+L7:
+    %t8 = b+1
+    b = %t8
+    %t9 = i+1
+    i = %t9
+    goto  L2
+L4:
+    %t10 = a+b
+    ret %t10
+    goto  L1
+L1:
+After SSA
+=========
+L0:
+    a_0 = 0
+    b_0 = 0
+    i_0 = 0
+    j_0 = 0
+    goto  L2
+L2:
+    j_1 = phi(j_0, j_2)
+    i_1 = phi(i_0, i_3)
+    b_1 = phi(b_0, b_2)
+    a_1 = phi(a_0, a_2)
+    %t4_0 = i_1<3
+    if %t4_0 goto L3 else goto L4
+L3:
+    j_2 = 0
+    goto  L5
+L5:
+    i_2 = phi(i_1, i_4)
+    a_2 = phi(a_1, a_3)
+    %t5_0 = j_2<2
+    if %t5_0 goto L6 else goto L7
+L6:
+    %t6_0 = a_2+1
+    a_3 = %t6_0
+    %t7_0 = j_2+1
+    i_4 = %t7_0
+    goto  L5
+L7:
+    %t8_0 = b_1+1
+    b_2 = %t8_0
+    %t9_0 = i_2+1
+    i_3 = %t9_0
+    goto  L2
+L4:
+    %t10_0 = a_1+b_1
+    ret %t10_0
+    goto  L1
+L1:
+After exiting SSA
+=================
+L0:
+    a_0 = 0
+    b_0 = 0
+    i_0 = 0
+    j_0 = 0
+    j_1 = j_0
+    i_1 = i_0
+    b_1 = b_0
+    a_1 = a_0
+    goto  L2
+L2:
+    %t4_0 = i_1<3
+    if %t4_0 goto L3 else goto L4
+L3:
+    j_2 = 0
+    i_2 = i_1
+    a_2 = a_1
+    goto  L5
+L5:
+    %t5_0 = j_2<2
+    if %t5_0 goto L6 else goto L7
+L6:
+    %t6_0 = a_2+1
+    a_3 = %t6_0
+    %t7_0 = j_2+1
+    i_4 = %t7_0
+    i_2 = i_4
+    a_2 = a_3
+    goto  L5
+L7:
+    %t8_0 = b_1+1
+    b_2 = %t8_0
+    %t9_0 = i_2+1
+    i_3 = %t9_0
+    j_1 = j_2
+    i_1 = i_3
+    b_1 = b_2
+    a_1 = a_2
+    goto  L2
+L4:
+    %t10_0 = a_1+b_1
+    ret %t10_0
+    goto  L1
+L1:
+""", result);
+    }
+
+    @Test
+    public void testSSA10() {
+        String src = """
+        func main()->Int {
+            var sum = 0
+            var i = 0
             var j = 0
             while (i < 5) {
                 j = 0
@@ -1241,7 +1809,135 @@ func main()->Int {
         }
 """;
         String result = compileSrc(src);
-        System.out.println(result);
+        Assert.assertEquals("""
+func main
+Before SSA
+==========
+L0:
+    sum = 0
+    i = 0
+    j = 0
+    goto  L2
+L2:
+    %t3 = i<5
+    if %t3 goto L3 else goto L4
+L3:
+    j = 0
+    goto  L5
+L5:
+    %t4 = j<5
+    if %t4 goto L6 else goto L7
+L6:
+    %t5 = j%2
+    %t6 = %t5==0
+    if %t6 goto L8 else goto L9
+L8:
+    %t7 = sum+j
+    sum = %t7
+    goto  L9
+L9:
+    %t8 = j+1
+    j = %t8
+    goto  L5
+L7:
+    %t9 = i+1
+    i = %t9
+    goto  L2
+L4:
+    ret sum
+    goto  L1
+L1:
+After SSA
+=========
+L0:
+    sum_0 = 0
+    i_0 = 0
+    j_0 = 0
+    goto  L2
+L2:
+    j_1 = phi(j_0, j_3)
+    i_1 = phi(i_0, i_2)
+    sum_1 = phi(sum_0, sum_2)
+    %t3_0 = i_1<5
+    if %t3_0 goto L3 else goto L4
+L3:
+    j_2 = 0
+    goto  L5
+L5:
+    j_3 = phi(j_2, j_4)
+    sum_2 = phi(sum_1, sum_4)
+    %t4_0 = j_3<5
+    if %t4_0 goto L6 else goto L7
+L6:
+    %t5_0 = j_3%2
+    %t6_0 = %t5_0==0
+    if %t6_0 goto L8 else goto L9
+L8:
+    %t7_0 = sum_2+j_3
+    sum_3 = %t7_0
+    goto  L9
+L9:
+    sum_4 = phi(sum_2, sum_3)
+    %t8_0 = j_3+1
+    j_4 = %t8_0
+    goto  L5
+L7:
+    %t9_0 = i_1+1
+    i_2 = %t9_0
+    goto  L2
+L4:
+    ret sum_1
+    goto  L1
+L1:
+After exiting SSA
+=================
+L0:
+    sum_0 = 0
+    i_0 = 0
+    j_0 = 0
+    j_1 = j_0
+    i_1 = i_0
+    sum_1 = sum_0
+    goto  L2
+L2:
+    %t3_0 = i_1<5
+    if %t3_0 goto L3 else goto L4
+L3:
+    j_2 = 0
+    j_3 = j_2
+    sum_2 = sum_1
+    goto  L5
+L5:
+    %t4_0 = j_3<5
+    if %t4_0 goto L6 else goto L7
+L6:
+    %t5_0 = j_3%2
+    %t6_0 = %t5_0==0
+    sum_4 = sum_2
+    if %t6_0 goto L8 else goto L9
+L8:
+    %t7_0 = sum_2+j_3
+    sum_3 = %t7_0
+    sum_4 = sum_3
+    goto  L9
+L9:
+    %t8_0 = j_3+1
+    j_4 = %t8_0
+    j_3 = j_4
+    sum_2 = sum_4
+    goto  L5
+L7:
+    %t9_0 = i_1+1
+    i_2 = %t9_0
+    j_1 = j_3
+    i_1 = i_2
+    sum_1 = sum_2
+    goto  L2
+L4:
+    ret sum_1
+    goto  L1
+L1:
+""", result);
     }
 
     @Test
@@ -1266,7 +1962,165 @@ else if (i > j)
         }
 """;
         String result = compileSrc(src);
-        System.out.println(result);
+        Assert.assertEquals("""
+func main
+Before SSA
+==========
+L0:
+    a = 0
+    i = 0
+    j = 0
+    goto  L2
+L2:
+    %t3 = i<3
+    if %t3 goto L3 else goto L4
+L3:
+    j = 0
+    goto  L5
+L5:
+    %t4 = j<3
+    if %t4 goto L6 else goto L7
+L6:
+    %t5 = i==j
+    if %t5 goto L8 else goto L9
+L8:
+    %t6 = a+i
+    %t7 = %t6+j
+    a = %t7
+    goto  L10
+L10:
+    %t10 = j+1
+    j = %t10
+    goto  L5
+L9:
+    %t8 = i>j
+    if %t8 goto L11 else goto L12
+L11:
+    %t9 = a-1
+    a = %t9
+    goto  L12
+L12:
+    goto  L10
+L7:
+    %t11 = i+1
+    i = %t11
+    goto  L2
+L4:
+    ret a
+    goto  L1
+L1:
+After SSA
+=========
+L0:
+    a_0 = 0
+    i_0 = 0
+    j_0 = 0
+    goto  L2
+L2:
+    j_1 = phi(j_0, j_3)
+    i_1 = phi(i_0, i_2)
+    a_1 = phi(a_0, a_2)
+    %t3_0 = i_1<3
+    if %t3_0 goto L3 else goto L4
+L3:
+    j_2 = 0
+    goto  L5
+L5:
+    j_3 = phi(j_2, j_4)
+    a_2 = phi(a_1, a_6)
+    %t4_0 = j_3<3
+    if %t4_0 goto L6 else goto L7
+L6:
+    %t5_0 = i_1==j_3
+    if %t5_0 goto L8 else goto L9
+L8:
+    %t6_0 = a_4+i_1
+    %t7_0 = %t6_0+j_3
+    a_5 = %t7_0
+    goto  L10
+L10:
+    a_6 = phi(a_5, a_4)
+    %t10_0 = j_3+1
+    j_4 = %t10_0
+    goto  L5
+L9:
+    %t8_0 = i_1>j_3
+    if %t8_0 goto L11 else goto L12
+L11:
+    %t9_0 = a_2-1
+    a_3 = %t9_0
+    goto  L12
+L12:
+    a_4 = phi(a_2, a_3)
+    goto  L10
+L7:
+    %t11_0 = i_1+1
+    i_2 = %t11_0
+    goto  L2
+L4:
+    ret a_1
+    goto  L1
+L1:
+After exiting SSA
+=================
+L0:
+    a_0 = 0
+    i_0 = 0
+    j_0 = 0
+    j_1 = j_0
+    i_1 = i_0
+    a_1 = a_0
+    goto  L2
+L2:
+    %t3_0 = i_1<3
+    if %t3_0 goto L3 else goto L4
+L3:
+    j_2 = 0
+    j_3 = j_2
+    a_2 = a_1
+    goto  L5
+L5:
+    %t4_0 = j_3<3
+    if %t4_0 goto L6 else goto L7
+L6:
+    %t5_0 = i_1==j_3
+    if %t5_0 goto L8 else goto L9
+L8:
+    %t6_0 = a_4+i_1
+    %t7_0 = %t6_0+j_3
+    a_5 = %t7_0
+    a_6 = a_5
+    goto  L10
+L10:
+    %t10_0 = j_3+1
+    j_4 = %t10_0
+    j_3 = j_4
+    a_2 = a_6
+    goto  L5
+L9:
+    %t8_0 = i_1>j_3
+    a_4 = a_2
+    if %t8_0 goto L11 else goto L12
+L11:
+    %t9_0 = a_2-1
+    a_3 = %t9_0
+    a_4 = a_3
+    goto  L12
+L12:
+    a_6 = a_4
+    goto  L10
+L7:
+    %t11_0 = i_1+1
+    i_2 = %t11_0
+    j_1 = j_3
+    i_1 = i_2
+    a_1 = a_2
+    goto  L2
+L4:
+    ret a_1
+    goto  L1
+L1:
+""", result);
     }
 
     @Test
@@ -1294,7 +2148,157 @@ func main()->Int {
         }
 """;
         String result = compileSrc(src);
-        System.out.println(result);
+        Assert.assertEquals("""
+func main
+Before SSA
+==========
+L0:
+    count = 0
+    i = 0
+    j = 0
+    goto  L2
+L2:
+    %t3 = i<5
+    if %t3 goto L3 else goto L4
+L3:
+    j = 0
+    goto  L5
+L5:
+    %t4 = j<5
+    if %t4 goto L6 else goto L7
+L6:
+    %t5 = i+j
+    %t6 = %t5>5
+    if %t6 goto L8 else goto L9
+L8:
+    goto  L7
+L7:
+    %t11 = i+1
+    i = %t11
+    goto  L2
+L9:
+    %t7 = i==j
+    if %t7 goto L10 else goto L11
+L10:
+    %t8 = j+1
+    j = %t8
+    goto  L5
+L11:
+    %t9 = count+1
+    count = %t9
+    %t10 = j+1
+    j = %t10
+    goto  L5
+L4:
+    ret count
+    goto  L1
+L1:
+After SSA
+=========
+L0:
+    count_0 = 0
+    i_0 = 0
+    j_0 = 0
+    goto  L2
+L2:
+    j_1 = phi(j_0, j_3)
+    i_1 = phi(i_0, i_2)
+    count_1 = phi(count_0, count_2)
+    %t3_0 = i_1<5
+    if %t3_0 goto L3 else goto L4
+L3:
+    j_2 = 0
+    goto  L5
+L5:
+    j_3 = phi(j_2, j_5, j_4)
+    count_2 = phi(count_1, count_2, count_3)
+    %t4_0 = j_3<5
+    if %t4_0 goto L6 else goto L7
+L6:
+    %t5_0 = i_1+j_3
+    %t6_0 = %t5_0>5
+    if %t6_0 goto L8 else goto L9
+L8:
+    goto  L7
+L7:
+    %t11_0 = i_1+1
+    i_2 = %t11_0
+    goto  L2
+L9:
+    %t7_0 = i_1==j_3
+    if %t7_0 goto L10 else goto L11
+L10:
+    %t8_0 = j_3+1
+    j_5 = %t8_0
+    goto  L5
+L11:
+    %t9_0 = count_2+1
+    count_3 = %t9_0
+    %t10_0 = j_3+1
+    j_4 = %t10_0
+    goto  L5
+L4:
+    ret count_1
+    goto  L1
+L1:
+After exiting SSA
+=================
+L0:
+    count_0 = 0
+    i_0 = 0
+    j_0 = 0
+    j_1 = j_0
+    i_1 = i_0
+    count_1 = count_0
+    goto  L2
+L2:
+    %t3_0 = i_1<5
+    if %t3_0 goto L3 else goto L4
+L3:
+    j_2 = 0
+    j_3 = j_2
+    count_2 = count_1
+    goto  L5
+L5:
+    count_2_35 = count_2
+    %t4_0 = j_3<5
+    if %t4_0 goto L6 else goto L7
+L6:
+    %t5_0 = i_1+j_3
+    %t6_0 = %t5_0>5
+    if %t6_0 goto L8 else goto L9
+L8:
+    goto  L7
+L7:
+    %t11_0 = i_1+1
+    i_2 = %t11_0
+    j_1 = j_3
+    i_1 = i_2
+    count_1 = count_2
+    goto  L2
+L9:
+    %t7_0 = i_1==j_3
+    if %t7_0 goto L10 else goto L11
+L10:
+    %t8_0 = j_3+1
+    j_5 = %t8_0
+    j_3 = j_5
+    count_2_34 = count_2
+    count_2 = count_2_34
+    goto  L5
+L11:
+    %t9_0 = count_2+1
+    count_3 = %t9_0
+    %t10_0 = j_3+1
+    j_4 = %t10_0
+    j_3 = j_4
+    count_2 = count_3
+    goto  L5
+L4:
+    ret count_1
+    goto  L1
+L1:
+""", result);
     }
 
     @Test
@@ -1319,7 +2323,209 @@ func main()->Int {
         }
 """;
         String result = compileSrc(src);
-        System.out.println(result);
+        Assert.assertEquals("""
+func main
+Before SSA
+==========
+L0:
+    outerSum = 0
+    innerSum = 0
+    i = 0
+    j = 0
+    goto  L2
+L2:
+    %t4 = i<4
+    if %t4 goto L3 else goto L4
+L3:
+    j = 0
+    goto  L5
+L5:
+    %t5 = j<4
+    if %t5 goto L6 else goto L7
+L6:
+    %t6 = i+j
+    %t7 = %t6%2
+    %t8 = %t7==0
+    if %t8 goto L8 else goto L9
+L8:
+    %t9 = innerSum+j
+    innerSum = %t9
+    goto  L9
+L9:
+    %t10 = j+1
+    j = %t10
+    goto  L5
+L7:
+    %t11 = outerSum+innerSum
+    outerSum = %t11
+    %t12 = i+1
+    i = %t12
+    goto  L2
+L4:
+    ret outerSum
+    goto  L1
+L1:
+After SSA
+=========
+L0:
+    outerSum_0 = 0
+    innerSum_0 = 0
+    i_0 = 0
+    j_0 = 0
+    goto  L2
+L2:
+    j_1 = phi(j_0, j_3)
+    i_1 = phi(i_0, i_2)
+    innerSum_1 = phi(innerSum_0, innerSum_2)
+    outerSum_1 = phi(outerSum_0, outerSum_2)
+    %t4_0 = i_1<4
+    if %t4_0 goto L3 else goto L4
+L3:
+    j_2 = 0
+    goto  L5
+L5:
+    j_3 = phi(j_2, j_4)
+    innerSum_2 = phi(innerSum_1, innerSum_4)
+    %t5_0 = j_3<4
+    if %t5_0 goto L6 else goto L7
+L6:
+    %t6_0 = i_1+j_3
+    %t7_0 = %t6_0%2
+    %t8_0 = %t7_0==0
+    if %t8_0 goto L8 else goto L9
+L8:
+    %t9_0 = innerSum_2+j_3
+    innerSum_3 = %t9_0
+    goto  L9
+L9:
+    innerSum_4 = phi(innerSum_2, innerSum_3)
+    %t10_0 = j_3+1
+    j_4 = %t10_0
+    goto  L5
+L7:
+    %t11_0 = outerSum_1+innerSum_2
+    outerSum_2 = %t11_0
+    %t12_0 = i_1+1
+    i_2 = %t12_0
+    goto  L2
+L4:
+    ret outerSum_1
+    goto  L1
+L1:
+After exiting SSA
+=================
+L0:
+    outerSum_0 = 0
+    innerSum_0 = 0
+    i_0 = 0
+    j_0 = 0
+    j_1 = j_0
+    i_1 = i_0
+    innerSum_1 = innerSum_0
+    outerSum_1 = outerSum_0
+    goto  L2
+L2:
+    %t4_0 = i_1<4
+    if %t4_0 goto L3 else goto L4
+L3:
+    j_2 = 0
+    j_3 = j_2
+    innerSum_2 = innerSum_1
+    goto  L5
+L5:
+    %t5_0 = j_3<4
+    if %t5_0 goto L6 else goto L7
+L6:
+    %t6_0 = i_1+j_3
+    %t7_0 = %t6_0%2
+    %t8_0 = %t7_0==0
+    innerSum_4 = innerSum_2
+    if %t8_0 goto L8 else goto L9
+L8:
+    %t9_0 = innerSum_2+j_3
+    innerSum_3 = %t9_0
+    innerSum_4 = innerSum_3
+    goto  L9
+L9:
+    %t10_0 = j_3+1
+    j_4 = %t10_0
+    j_3 = j_4
+    innerSum_2 = innerSum_4
+    goto  L5
+L7:
+    %t11_0 = outerSum_1+innerSum_2
+    outerSum_2 = %t11_0
+    %t12_0 = i_1+1
+    i_2 = %t12_0
+    j_1 = j_3
+    i_1 = i_2
+    innerSum_1 = innerSum_2
+    outerSum_1 = outerSum_2
+    goto  L2
+L4:
+    ret outerSum_1
+    goto  L1
+L1:
+""", result);
     }
 
+    @Ignore
+    @Test
+    public void testSSA14() {
+        String src = """
+                func foo()->Int 
+                {
+                    return 1 && 2
+                }
+""";
+        String result = compileSrc(src);
+        Assert.assertEquals("""
+func foo
+Before SSA
+==========
+L0:
+    if 1 goto L2 else goto L3
+L2:
+    %t0 = 2
+    goto  L4
+L4:
+    ret %t0
+    goto  L1
+L1:
+L3:
+    %t0 = 0
+    goto  L4
+After SSA
+=========
+L0:
+    if 1 goto L2 else goto L3
+L2:
+    %t0_1 = 2
+    goto  L4
+L4:
+    %t0_2 = phi(%t0_1, %t0_0)
+    ret %t0_2
+    goto  L1
+L1:
+L3:
+    %t0_0 = 0
+    goto  L4
+After exiting SSA
+=================
+L0:
+    if 1 goto L2 else goto L3
+L2:
+    %t0_1 = 2
+    %t0_2 = %t0_1
+    goto  L4
+L4:
+    ret %t0_2
+    goto  L1
+L1:
+L3:
+    %t0_0 = 0
+    %t0_2 = %t0_0
+    goto  L4
+""", result);
+    }
 }
\ No newline at end of file

From b5623aefee855cfb374bd3a422cce02887edfaf1 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <dibyendu@redukti.com>
Date: Tue, 18 Feb 2025 11:19:39 +0000
Subject: [PATCH 033/119] Fix bug in EnterSSA caused by mutable operands shared
 across instructions, replacing the phi register cause other instructions to
 change inadvertantly.

Fix bug in EnterSSA caused by mutable operands shared across instructions, replacing the phi register cause other instructions to change inadvertantly.

Fix bug in EnterSSA caused by mutable operands shared across instructions, replacing the phi register cause other instructions to change inadvertantly.
---
 .../ezlang/compiler/CompiledFunction.java     |  77 +++++++++-
 .../ezlang/compiler/Compiler.java             |   2 +-
 .../ezlang/compiler/Instruction.java          |   8 +-
 .../ezlang/compiler/Operand.java              |  19 ++-
 .../compiler/TestInterferenceGraph.java       |   6 +-
 .../ezlang/compiler/TestLiveness.java         |   2 +-
 .../ezlang/compiler/TestSSATransform.java     |   5 +-
 .../ezlang/interpreter/TestInterpreter.java   | 143 ++++++++++++++++++
 8 files changed, 239 insertions(+), 23 deletions(-)

diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
index a686e8e..418db05 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
@@ -5,6 +5,7 @@
 import com.compilerprogramming.ezlang.types.Scope;
 import com.compilerprogramming.ezlang.types.Symbol;
 import com.compilerprogramming.ezlang.types.Type;
+import com.compilerprogramming.ezlang.types.TypeDictionary;
 
 import java.util.ArrayList;
 import java.util.BitSet;
@@ -20,6 +21,7 @@ public class CompiledFunction {
     private BasicBlock currentContinueTarget;
     private Type.TypeFunction functionType;
     public final RegisterPool registerPool;
+    private final TypeDictionary typeDictionary;
 
     private int frameSlots;
 
@@ -36,7 +38,7 @@ public class CompiledFunction {
      */
     private List<Operand> virtualStack = new ArrayList<>();
 
-    public CompiledFunction(Symbol.FunctionTypeSymbol functionSymbol) {
+    public CompiledFunction(Symbol.FunctionTypeSymbol functionSymbol, TypeDictionary typeDictionary) {
         AST.FuncDecl funcDecl = (AST.FuncDecl) functionSymbol.functionDecl;
         this.functionType = (Type.TypeFunction) functionSymbol.type;
         this.registerPool = new RegisterPool();
@@ -46,13 +48,14 @@ public CompiledFunction(Symbol.FunctionTypeSymbol functionSymbol) {
         this.exit = createBlock();
         this.currentBreakTarget = null;
         this.currentContinueTarget = null;
+        this.typeDictionary = typeDictionary;
         generateArgInstructions(funcDecl.scope);
         compileStatement(funcDecl.block);
         exitBlockIfNeeded();
         this.frameSlots = registerPool.numRegisters();
     }
 
-    public CompiledFunction(Type.TypeFunction functionType) {
+    public CompiledFunction(Type.TypeFunction functionType, TypeDictionary typeDictionary) {
         this.functionType = (Type.TypeFunction) functionType;
         this.registerPool = new RegisterPool();
         this.BID = 0;
@@ -60,6 +63,7 @@ public CompiledFunction(Type.TypeFunction functionType) {
         this.exit = createBlock();
         this.currentBreakTarget = null;
         this.currentContinueTarget = null;
+        this.typeDictionary = typeDictionary;
         this.frameSlots = registerPool.numRegisters();
     }
 
@@ -437,8 +441,42 @@ private boolean compileSymbolExpr(AST.NameExpr symbolExpr) {
         return false;
     }
 
+    private boolean codeBoolean(AST.BinaryExpr binaryExpr) {
+        boolean isAnd = binaryExpr.op.str.equals("&&");
+        BasicBlock l1 = createBlock();
+        BasicBlock l2 = createBlock();
+        BasicBlock l3 = createBlock();
+        boolean indexed = compileExpr(binaryExpr.expr1);
+        if (indexed)
+            codeIndexedLoad();
+        if (isAnd) {
+            code(new Instruction.ConditionalBranch(currentBlock, pop(), l1, l2));
+        } else {
+            code(new Instruction.ConditionalBranch(currentBlock, pop(), l2, l1));
+        }
+        startBlock(l1);
+        compileExpr(binaryExpr.expr2);
+        var temp = ensureTemp();
+        jumpTo(l3);
+        startBlock(l2);
+        // Below we must write to the same temp
+        //code(new Instruction.Move(new Operand.ConstantOperand(isAnd ? 0 : 1, typeDictionary.INT), new Operand.TempRegisterOperand(temp.reg)));
+        code(new Instruction.Move(new Operand.ConstantOperand(isAnd ? 0 : 1, typeDictionary.INT), temp));
+        jumpTo(l3);
+        startBlock(l3);
+        // leave temp on virtual stack
+//        var temp2 = (Operand.TempRegisterOperand) pop();
+//        pushOperand(new Operand.TempRegisterOperand(temp2.reg));
+        return false;
+    }
+
+
     private boolean compileBinaryExpr(AST.BinaryExpr binaryExpr) {
-        String opCode = null;
+        String opCode = binaryExpr.op.str;
+        if (opCode.equals("&&") ||
+            opCode.equals("||")) {
+            return codeBoolean(binaryExpr);
+        }
         boolean indexed = compileExpr(binaryExpr.expr1);
         if (indexed)
             codeIndexedLoad();
@@ -511,6 +549,36 @@ private Operand.TempRegisterOperand createTemp(Type type) {
         return tempRegister;
     }
 
+    Type typeOfOperand(Operand operand) {
+        if (operand instanceof Operand.ConstantOperand constant)
+            return constant.type;
+//        else if (operand instanceof Operand.NullConstantOperand nullConstantOperand)
+//            return nullConstantOperand.type;
+        else if (operand instanceof Operand.RegisterOperand registerOperand)
+            return registerOperand.type;
+        else throw new CompilerException("Invalid operand");
+    }
+
+    private Operand.TempRegisterOperand createTempAndMove(Operand src) {
+        Type type = typeOfOperand(src);
+        var temp = createTemp(type);
+        code(new Instruction.Move(src, temp));
+        return temp;
+    }
+
+    private Operand.RegisterOperand ensureTemp() {
+        Operand top = top();
+        if (top instanceof Operand.ConstantOperand
+                //|| top instanceof Operand.NullConstantOperand
+                || top instanceof Operand.LocalRegisterOperand) {
+            return createTempAndMove(pop());
+        } else if (top instanceof Operand.IndexedOperand) {
+            return codeIndexedLoad();
+        } else if (top instanceof Operand.TempRegisterOperand tempRegisterOperand) {
+            return tempRegisterOperand;
+        } else throw new CompilerException("Cannot convert to temporary register");
+    }
+
     private void pushLocal(Register reg) {
         pushOperand(new Operand.LocalRegisterOperand(reg));
     }
@@ -527,7 +595,7 @@ private Operand top() {
         return virtualStack.getLast();
     }
 
-    private void codeIndexedLoad() {
+    private Operand.TempRegisterOperand codeIndexedLoad() {
         Operand indexed = pop();
         var temp = createTemp(indexed.type);
         if (indexed instanceof Operand.LoadIndexedOperand loadIndexedOperand) {
@@ -538,6 +606,7 @@ else if (indexed instanceof Operand.LoadFieldOperand loadFieldOperand) {
         }
         else
             code(new Instruction.Move(indexed, temp));
+        return temp;
     }
 
     private void codeIndexedStore() {
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java
index df64e5c..4c24bb6 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java
@@ -16,7 +16,7 @@ private void compile(TypeDictionary typeDictionary, EnumSet<Options> options) {
         for (Symbol symbol: typeDictionary.getLocalSymbols()) {
             if (symbol instanceof Symbol.FunctionTypeSymbol functionSymbol) {
                 Type.TypeFunction functionType = (Type.TypeFunction) functionSymbol.type;
-                var function = new CompiledFunction(functionSymbol);
+                var function = new CompiledFunction(functionSymbol, typeDictionary);
                 if (options.contains(Options.DUMP_INITIAL_IR))
                     function.dumpIR(false, "Initial IR");
                 functionType.code = function;
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java
index c4005a7..1651c1c 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java
@@ -67,14 +67,14 @@ public List<Register> uses() {
     }
     public void replaceDef(Register newReg) {
         if (def == null) throw new IllegalStateException();
-        def.replaceRegister(newReg);
+        def = def.copy(newReg);
     }
     public void replaceUses(Register[] newUses) {
         int j = 0;
         for (int i = 0; i < uses.length; i++) {
             Operand use = uses[i];
             if (use != null && use instanceof Operand.RegisterOperand registerOperand) {
-                registerOperand.replaceRegister(newUses[j++]);
+                uses[i] = registerOperand.copy(newUses[j++]);
             }
         }
     }
@@ -83,7 +83,7 @@ public boolean replaceUse(Register source, Register target) {
         for (int i = 0; i < uses.length; i++) {
             Operand operand = uses[i];
             if (operand != null && operand instanceof Operand.RegisterOperand registerOperand && registerOperand.reg.id == source.id) {
-                registerOperand.replaceRegister(target);
+                uses[i] = registerOperand.copy(target);
                 replaced = true;
             }
         }
@@ -362,7 +362,7 @@ public Phi(Register value, List<Register> inputs) {
             }
         }
         public void replaceInput(int i, Register newReg) {
-            uses[i].replaceRegister(newReg);
+            uses[i] =  new Operand.RegisterOperand(newReg);
         }
         /**
          * This will fail in input was replaced by a constant
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Operand.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Operand.java
index e91531e..b4bde07 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Operand.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Operand.java
@@ -6,8 +6,6 @@ public class Operand {
 
     Type type;
 
-    public void replaceRegister(Register register) {}
-
     public static class ConstantOperand extends Operand {
         public final long value;
         public ConstantOperand(long value, Type type) {
@@ -21,17 +19,16 @@ public String toString() {
     }
 
     public static class RegisterOperand extends Operand {
-        Register reg;
-        public RegisterOperand(Register reg) {
+        final Register reg;
+        protected RegisterOperand(Register reg) {
             this.reg = reg;
             if (reg == null)
                 throw new NullPointerException();
         }
         public int frameSlot() { return reg.nonSSAId(); }
 
-        @Override
-        public void replaceRegister(Register register) {
-            this.reg = register;
+        public RegisterOperand copy(Register register) {
+           return new RegisterOperand(register);
         }
 
         @Override
@@ -44,6 +41,10 @@ public static class LocalRegisterOperand extends RegisterOperand {
         public LocalRegisterOperand(Register reg) {
             super(reg);
         }
+        @Override
+        public RegisterOperand copy(Register register) {
+            return new LocalRegisterOperand(register);
+        }
     }
 
     public static class LocalFunctionOperand extends Operand {
@@ -66,6 +67,10 @@ public static class TempRegisterOperand extends RegisterOperand {
         public TempRegisterOperand(Register reg) {
             super(reg);
         }
+        @Override
+        public RegisterOperand copy(Register register) {
+            return new TempRegisterOperand(register);
+        }
     }
 
     public static class IndexedOperand extends Operand {}
diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestInterferenceGraph.java b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestInterferenceGraph.java
index 2cfe554..e5a4299 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestInterferenceGraph.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestInterferenceGraph.java
@@ -13,7 +13,7 @@ private CompiledFunction buildTest1() {
         Type.TypeFunction functionType = new Type.TypeFunction("foo");
         functionType.addArg(new Symbol.ParameterSymbol("a", typeDictionary.INT));
         functionType.setReturnType(typeDictionary.INT);
-        CompiledFunction function = new CompiledFunction(functionType);
+        CompiledFunction function = new CompiledFunction(functionType, typeDictionary);
         RegisterPool regPool = function.registerPool;
         Register a = regPool.newReg("a", typeDictionary.INT);
         Register b = regPool.newReg("b", typeDictionary.INT);
@@ -82,7 +82,7 @@ private CompiledFunction buildTest2() {
         TypeDictionary typeDictionary = new TypeDictionary();
         Type.TypeFunction functionType = new Type.TypeFunction("foo");
         functionType.setReturnType(typeDictionary.VOID);
-        CompiledFunction function = new CompiledFunction(functionType);
+        CompiledFunction function = new CompiledFunction(functionType, typeDictionary);
         RegisterPool regPool = function.registerPool;
         Register a = regPool.newReg("a", typeDictionary.INT);
         Register b = regPool.newReg("b", typeDictionary.INT);
@@ -158,7 +158,7 @@ public static CompiledFunction buildTest4() {
         TypeDictionary typeDictionary = new TypeDictionary();
         Type.TypeFunction functionType = new Type.TypeFunction("foo");
         functionType.setReturnType(typeDictionary.VOID);
-        CompiledFunction function = new CompiledFunction(functionType);
+        CompiledFunction function = new CompiledFunction(functionType, typeDictionary);
         RegisterPool regPool = function.registerPool;
         Register a = regPool.newReg("a", typeDictionary.INT);
         Register b = regPool.newReg("b", typeDictionary.INT);
diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestLiveness.java b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestLiveness.java
index b1b6222..c9c2d27 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestLiveness.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestLiveness.java
@@ -247,7 +247,7 @@ static CompiledFunction buildTest3() {
         TypeDictionary typeDictionary = new TypeDictionary();
         Type.TypeFunction functionType = new Type.TypeFunction("foo");
         functionType.setReturnType(typeDictionary.INT);
-        CompiledFunction function = new CompiledFunction(functionType);
+        CompiledFunction function = new CompiledFunction(functionType, typeDictionary);
         RegisterPool regPool = function.registerPool;
         Register i = regPool.newReg("i", typeDictionary.INT);
         Register s = regPool.newReg("s", typeDictionary.INT);
diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
index 703ca2a..3f8a052 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
@@ -640,7 +640,7 @@ static CompiledFunction buildLostCopyTest() {
         Type.TypeFunction functionType = new Type.TypeFunction("foo");
         functionType.addArg(new Symbol.ParameterSymbol("p", typeDictionary.INT));
         functionType.setReturnType(typeDictionary.INT);
-        CompiledFunction function = new CompiledFunction(functionType);
+        CompiledFunction function = new CompiledFunction(functionType, typeDictionary);
         RegisterPool regPool = function.registerPool;
         Register p = regPool.newReg("p", typeDictionary.INT);
         Register x1 = regPool.newReg("x1", typeDictionary.INT);
@@ -708,7 +708,7 @@ static CompiledFunction buildSwapTest() {
         Type.TypeFunction functionType = new Type.TypeFunction("foo");
         functionType.addArg(new Symbol.ParameterSymbol("p", typeDictionary.INT));
         functionType.setReturnType(typeDictionary.VOID);
-        CompiledFunction function = new CompiledFunction(functionType);
+        CompiledFunction function = new CompiledFunction(functionType, typeDictionary);
         RegisterPool regPool = function.registerPool;
         Register p = regPool.newReg("p", typeDictionary.INT);
         Register a1 = regPool.newReg("a1", typeDictionary.INT);
@@ -2469,7 +2469,6 @@ func main()->Int {
 """, result);
     }
 
-    @Ignore
     @Test
     public void testSSA14() {
         String src = """
diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java b/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java
index 01eb89c..e463a29 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java
@@ -445,4 +445,147 @@ func foo()->Int {
                 && integerValue.value == 5+(5*21+25/5));
     }
 
+
+//    @Test
+//    public void testFunction100() {
+//        String src = """
+//                struct Test
+//                {
+//                    var field: Int
+//                }
+//                func foo()->Test?
+//                {
+//                    return null;
+//                }
+//
+//                """;
+//        var value = compileAndRun(src, "foo", Options.OPT);
+//        Assert.assertNotNull(value);
+//        Assert.assertTrue(value instanceof Value.NullValue);
+//    }
+//
+//    @Test
+//    public void testFunction101() {
+//        String src = """
+//                func foo()->Int
+//                {
+//                    return null == null;
+//                }
+//
+//                """;
+//        var value = compileAndRun(src, "foo", Options.OPT);
+//        Assert.assertNotNull(value);
+//        Assert.assertTrue(value instanceof Value.IntegerValue integerValue &&
+//                integerValue.value == 1);
+//    }
+//
+//    @Test
+//    public void testFunction102() {
+//        String src = """
+//                struct Foo
+//                {
+//                    var next: Foo?
+//                }
+//                func foo()->Int
+//                {
+//                    var f = new Foo{ next = null }
+//                    return null == f.next
+//                }
+//
+//                """;
+//        var value = compileAndRun(src, "foo", Options.OPT);
+//        Assert.assertNotNull(value);
+//        Assert.assertTrue(value instanceof Value.IntegerValue integerValue &&
+//                integerValue.value == 1);
+//    }
+//
+//    @Test
+//    public void testFunction103() {
+//        String src = """
+//                struct Foo
+//                {
+//                    var i: Int
+//                }
+//                func foo()->Int
+//                {
+//                    var f = new [Foo?] { new Foo{i = 1}, null }
+//                    return null == f[1] && 1 == f[0].i
+//                }
+//
+//                """;
+//        var value = compileAndRun(src, "foo", Options.OPT);
+//        Assert.assertNotNull(value);
+//        Assert.assertTrue(value instanceof Value.IntegerValue integerValue &&
+//                integerValue.value == 1);
+//    }
+
+    @Test
+    public void testFunction104() {
+        String src = """
+                func foo()->Int
+                {
+                    return 1 == 1 && 2 == 2
+                }
+
+                """;
+        var value = compileAndRun(src, "foo", Options.OPT);
+        Assert.assertNotNull(value);
+        Assert.assertTrue(value instanceof Value.IntegerValue integerValue &&
+                integerValue.value == 1);
+    }
+
+    @Test
+    public void testFunction105() {
+        String src = """
+                func bar(a: Int, b: Int)->Int
+                {
+                    return a+1 == b-1 && b / a == 2
+                }
+                func foo()->Int
+                {
+                    return bar(3,5)
+                }
+                """;
+        var value = compileAndRun(src, "foo", Options.OPT);
+        Assert.assertNotNull(value);
+        Assert.assertTrue(value instanceof Value.IntegerValue integerValue &&
+                integerValue.value == 0);
+    }
+
+    @Test
+    public void testFunction106() {
+        String src = """
+                func bar(a: Int, b: Int)->Int
+                {
+                    return a+1 == b-1 || b / a == 2
+                }
+                func foo()->Int
+                {
+                    return bar(3,5)
+                }
+                """;
+        var value = compileAndRun(src, "foo", Options.OPT);
+        Assert.assertNotNull(value);
+        Assert.assertTrue(value instanceof Value.IntegerValue integerValue &&
+                integerValue.value == 1);
+    }
+
+    @Test
+    public void testFunction107() {
+        String src = """
+                func bar(a: [Int])->Int
+                {
+                    return a[0]+a[2] == a[1]-a[2] || a[1] / a[0] == 2
+                }
+                func foo()->Int
+                {
+                    return bar(new [Int] {3,5,1})
+                }
+                """;
+        var value = compileAndRun(src, "foo", Options.OPT);
+        Assert.assertNotNull(value);
+        Assert.assertTrue(value instanceof Value.IntegerValue integerValue &&
+                integerValue.value == 1);
+    }
+
 }

From 7e3632b241d4aaeb4f179cf0bb25206be9f02fab Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <dibyendu@redukti.com>
Date: Tue, 18 Feb 2025 11:51:51 +0000
Subject: [PATCH 034/119] Ensure SSA tests check output

---
 .../ezlang/compiler/TestSSATransform.java     | 1294 ++++++++++++++++-
 1 file changed, 1250 insertions(+), 44 deletions(-)

diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
index d4d1026..703ca2a 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
@@ -1077,7 +1077,39 @@ func foo()->Int {
                 }
                 """;
         String result = compileSrc(src);
-        System.out.println(result);
+        Assert.assertEquals("""
+func foo
+Before SSA
+==========
+L0:
+    a = 5
+    b = 10
+    %t3 = a+b
+    c = %t3
+    ret c
+    goto  L1
+L1:
+After SSA
+=========
+L0:
+    a_0 = 5
+    b_0 = 10
+    %t3_0 = a_0+b_0
+    c_0 = %t3_0
+    ret c_0
+    goto  L1
+L1:
+After exiting SSA
+=================
+L0:
+    a_0 = 5
+    b_0 = 10
+    %t3_0 = a_0+b_0
+    c_0 = %t3_0
+    ret c_0
+    goto  L1
+L1:
+""", result);
     }
 
     @Test
@@ -1090,7 +1122,36 @@ func foo()->Int {
                 }
                 """;
         String result = compileSrc(src);
-        System.out.println(result);
+        Assert.assertEquals("""
+func foo
+Before SSA
+==========
+L0:
+    a = 5
+    %t1 = a+1
+    a = %t1
+    ret a
+    goto  L1
+L1:
+After SSA
+=========
+L0:
+    a_0 = 5
+    %t1_0 = a_0+1
+    a_1 = %t1_0
+    ret a_1
+    goto  L1
+L1:
+After exiting SSA
+=================
+L0:
+    a_0 = 5
+    %t1_0 = a_0+1
+    a_1 = %t1_0
+    ret a_1
+    goto  L1
+L1:
+""", result);
     }
 
     @Test
@@ -1107,7 +1168,60 @@ func foo()->Int {
                 }
                 """;
         String result = compileSrc(src);
-        System.out.println(result);
+        Assert.assertEquals("""
+func foo
+Before SSA
+==========
+L0:
+    a = 5
+    %t1 = a>3
+    if %t1 goto L2 else goto L3
+L2:
+    a = 10
+    goto  L4
+L4:
+    ret a
+    goto  L1
+L1:
+L3:
+    a = 20
+    goto  L4
+After SSA
+=========
+L0:
+    a_0 = 5
+    %t1_0 = a_0>3
+    if %t1_0 goto L2 else goto L3
+L2:
+    a_2 = 10
+    goto  L4
+L4:
+    a_3 = phi(a_2, a_1)
+    ret a_3
+    goto  L1
+L1:
+L3:
+    a_1 = 20
+    goto  L4
+After exiting SSA
+=================
+L0:
+    a_0 = 5
+    %t1_0 = a_0>3
+    if %t1_0 goto L2 else goto L3
+L2:
+    a_2 = 10
+    a_3 = a_2
+    goto  L4
+L4:
+    ret a_3
+    goto  L1
+L1:
+L3:
+    a_1 = 20
+    a_3 = a_1
+    goto  L4
+""", result);
     }
 
     @Test
@@ -1122,7 +1236,60 @@ func foo()->Int {
                 }
                 """;
         String result = compileSrc(src);
-        System.out.println(result);
+        Assert.assertEquals("""
+func foo
+Before SSA
+==========
+L0:
+    a = 0
+    goto  L2
+L2:
+    %t1 = a<5
+    if %t1 goto L3 else goto L4
+L3:
+    %t2 = a+1
+    a = %t2
+    goto  L2
+L4:
+    ret a
+    goto  L1
+L1:
+After SSA
+=========
+L0:
+    a_0 = 0
+    goto  L2
+L2:
+    a_1 = phi(a_0, a_2)
+    %t1_0 = a_1<5
+    if %t1_0 goto L3 else goto L4
+L3:
+    %t2_0 = a_1+1
+    a_2 = %t2_0
+    goto  L2
+L4:
+    ret a_1
+    goto  L1
+L1:
+After exiting SSA
+=================
+L0:
+    a_0 = 0
+    a_1 = a_0
+    goto  L2
+L2:
+    %t1_0 = a_1<5
+    if %t1_0 goto L3 else goto L4
+L3:
+    %t2_0 = a_1+1
+    a_2 = %t2_0
+    a_1 = a_2
+    goto  L2
+L4:
+    ret a_1
+    goto  L1
+L1:
+""", result);
     }
 
     @Test
@@ -1144,7 +1311,90 @@ func foo()->Int {
                 }
                 """;
         String result = compileSrc(src);
-        System.out.println(result);
+        Assert.assertEquals("""
+func foo
+Before SSA
+==========
+L0:
+    a = 0
+    b = 10
+    %t2 = b>5
+    if %t2 goto L2 else goto L3
+L2:
+    %t3 = a<5
+    if %t3 goto L5 else goto L6
+L5:
+    a = 5
+    goto  L7
+L7:
+    goto  L4
+L4:
+    ret a
+    goto  L1
+L1:
+L6:
+    a = 15
+    goto  L7
+L3:
+    a = 20
+    goto  L4
+After SSA
+=========
+L0:
+    a_0 = 0
+    b_0 = 10
+    %t2_0 = b_0>5
+    if %t2_0 goto L2 else goto L3
+L2:
+    %t3_0 = a_0<5
+    if %t3_0 goto L5 else goto L6
+L5:
+    a_3 = 5
+    goto  L7
+L7:
+    a_4 = phi(a_3, a_2)
+    goto  L4
+L4:
+    a_5 = phi(a_4, a_1)
+    ret a_5
+    goto  L1
+L1:
+L6:
+    a_2 = 15
+    goto  L7
+L3:
+    a_1 = 20
+    goto  L4
+After exiting SSA
+=================
+L0:
+    a_0 = 0
+    b_0 = 10
+    %t2_0 = b_0>5
+    if %t2_0 goto L2 else goto L3
+L2:
+    %t3_0 = a_0<5
+    if %t3_0 goto L5 else goto L6
+L5:
+    a_3 = 5
+    a_4 = a_3
+    goto  L7
+L7:
+    a_5 = a_4
+    goto  L4
+L4:
+    ret a_5
+    goto  L1
+L1:
+L6:
+    a_2 = 15
+    a_4 = a_2
+    goto  L7
+L3:
+    a_1 = 20
+    a_5 = a_1
+    goto  L4
+""", result);
     }
 
     @Test
@@ -1158,7 +1408,48 @@ func foo()->Int {
                 }
                 """;
         String result = compileSrc(src);
-        System.out.println(result);
+        Assert.assertEquals("""
+func foo
+Before SSA
+==========
+L0:
+    %t2 = New([Int])
+    %t2.append(1)
+    %t2.append(2)
+    arr = %t2
+    arr[0] = 10
+    %t3 = arr[0]
+    x = %t3
+    ret x
+    goto  L1
+L1:
+After SSA
+=========
+L0:
+    %t2_0 = New([Int])
+    %t2_0.append(1)
+    %t2_0.append(2)
+    arr_0 = %t2_0
+    arr_0[0] = 10
+    %t3_0 = arr_0[0]
+    x_0 = %t3_0
+    ret x_0
+    goto  L1
+L1:
+After exiting SSA
+=================
+L0:
+    %t2_0 = New([Int])
+    %t2_0.append(1)
+    %t2_0.append(2)
+    arr_0 = %t2_0
+    arr_0[0] = 10
+    %t3_0 = arr_0[0]
+    x_0 = %t3_0
+    ret x_0
+    goto  L1
+L1:
+""", result);
     }
 
     @Test
@@ -1175,7 +1466,73 @@ func main()->Int {
         }
 """;
         String result = compileSrc(src);
-        System.out.println(result);
+        Assert.assertEquals("""
+func add
+Before SSA
+==========
+L0:
+    arg x
+    arg y
+    %t2 = x+y
+    ret %t2
+    goto  L1
+L1:
+After SSA
+=========
+L0:
+    arg x_0
+    arg y_0
+    %t2_0 = x_0+y_0
+    ret %t2_0
+    goto  L1
+L1:
+After exiting SSA
+=================
+L0:
+    arg x_0
+    arg y_0
+    %t2_0 = x_0+y_0
+    ret %t2_0
+    goto  L1
+L1:
+func main
+Before SSA
+==========
+L0:
+    a = 5
+    b = 10
+    %t3 = a
+    %t4 = b
+    %t5 = call add params %t3, %t4
+    c = %t5
+    ret c
+    goto  L1
+L1:
+After SSA
+=========
+L0:
+    a_0 = 5
+    b_0 = 10
+    %t3_0 = a_0
+    %t4_0 = b_0
+    %t5_0 = call add params %t3_0, %t4_0
+    c_0 = %t5_0
+    ret c_0
+    goto  L1
+L1:
+After exiting SSA
+=================
+L0:
+    a_0 = 5
+    b_0 = 10
+    %t3_0 = a_0
+    %t4_0 = b_0
+    %t5_0 = call add params %t3_0, %t4_0
+    c_0 = %t5_0
+    ret c_0
+    goto  L1
+L1:
+""", result);
     }
 
     @Test
@@ -1194,39 +1551,250 @@ func main()->Int {
         }
 """;
         String result = compileSrc(src);
-        System.out.println(result);
-    }
-
-    @Test
-    public void testSSA9() {
-        String src = """
-        func main()->Int {
-            var a = 0
-            var b = 0
-            var i = 0
-            var j = 0
-            while (i < 3) {
-                j = 0
-                while (j < 2) {
-                    a = a + 1
-                    i = j + 1
-                }
-                b = b + 1;
-                i = i + 1    
-            }
-            return a + b
-        }
-""";
-        String result = compileSrc(src);
-        System.out.println(result);
-    }
-
-    @Test
-    public void testSSA10() {
-        String src = """
-        func main()->Int {
-            var sum = 0
-            var i = 0
+        Assert.assertEquals("""
+func main
+Before SSA
+==========
+L0:
+    a = 0
+    b = 1
+    goto  L2
+L2:
+    %t2 = a<10
+    if %t2 goto L3 else goto L4
+L3:
+    %t3 = a+2
+    a = %t3
+    goto  L2
+L4:
+    goto  L5
+L5:
+    %t4 = b<20
+    if %t4 goto L6 else goto L7
+L6:
+    %t5 = b+3
+    b = %t5
+    goto  L5
+L7:
+    %t6 = a+b
+    ret %t6
+    goto  L1
+L1:
+After SSA
+=========
+L0:
+    a_0 = 0
+    b_0 = 1
+    goto  L2
+L2:
+    a_1 = phi(a_0, a_2)
+    %t2_0 = a_1<10
+    if %t2_0 goto L3 else goto L4
+L3:
+    %t3_0 = a_1+2
+    a_2 = %t3_0
+    goto  L2
+L4:
+    goto  L5
+L5:
+    b_1 = phi(b_0, b_2)
+    %t4_0 = b_1<20
+    if %t4_0 goto L6 else goto L7
+L6:
+    %t5_0 = b_1+3
+    b_2 = %t5_0
+    goto  L5
+L7:
+    %t6_0 = a_1+b_1
+    ret %t6_0
+    goto  L1
+L1:
+After exiting SSA
+=================
+L0:
+    a_0 = 0
+    b_0 = 1
+    a_1 = a_0
+    goto  L2
+L2:
+    %t2_0 = a_1<10
+    if %t2_0 goto L3 else goto L4
+L3:
+    %t3_0 = a_1+2
+    a_2 = %t3_0
+    a_1 = a_2
+    goto  L2
+L4:
+    b_1 = b_0
+    goto  L5
+L5:
+    %t4_0 = b_1<20
+    if %t4_0 goto L6 else goto L7
+L6:
+    %t5_0 = b_1+3
+    b_2 = %t5_0
+    b_1 = b_2
+    goto  L5
+L7:
+    %t6_0 = a_1+b_1
+    ret %t6_0
+    goto  L1
+L1:
+""", result);
+    }
+
+    @Test
+    public void testSSA9() {
+        String src = """
+        func main()->Int {
+            var a = 0
+            var b = 0
+            var i = 0
+            var j = 0
+            while (i < 3) {
+                j = 0
+                while (j < 2) {
+                    a = a + 1
+                    i = j + 1
+                }
+                b = b + 1;
+                i = i + 1    
+            }
+            return a + b
+        }
+""";
+        String result = compileSrc(src);
+        Assert.assertEquals("""
+func main
+Before SSA
+==========
+L0:
+    a = 0
+    b = 0
+    i = 0
+    j = 0
+    goto  L2
+L2:
+    %t4 = i<3
+    if %t4 goto L3 else goto L4
+L3:
+    j = 0
+    goto  L5
+L5:
+    %t5 = j<2
+    if %t5 goto L6 else goto L7
+L6:
+    %t6 = a+1
+    a = %t6
+    %t7 = j+1
+    i = %t7
+    goto  L5
+L7:
+    %t8 = b+1
+    b = %t8
+    %t9 = i+1
+    i = %t9
+    goto  L2
+L4:
+    %t10 = a+b
+    ret %t10
+    goto  L1
+L1:
+After SSA
+=========
+L0:
+    a_0 = 0
+    b_0 = 0
+    i_0 = 0
+    j_0 = 0
+    goto  L2
+L2:
+    j_1 = phi(j_0, j_2)
+    i_1 = phi(i_0, i_3)
+    b_1 = phi(b_0, b_2)
+    a_1 = phi(a_0, a_2)
+    %t4_0 = i_1<3
+    if %t4_0 goto L3 else goto L4
+L3:
+    j_2 = 0
+    goto  L5
+L5:
+    i_2 = phi(i_1, i_4)
+    a_2 = phi(a_1, a_3)
+    %t5_0 = j_2<2
+    if %t5_0 goto L6 else goto L7
+L6:
+    %t6_0 = a_2+1
+    a_3 = %t6_0
+    %t7_0 = j_2+1
+    i_4 = %t7_0
+    goto  L5
+L7:
+    %t8_0 = b_1+1
+    b_2 = %t8_0
+    %t9_0 = i_2+1
+    i_3 = %t9_0
+    goto  L2
+L4:
+    %t10_0 = a_1+b_1
+    ret %t10_0
+    goto  L1
+L1:
+After exiting SSA
+=================
+L0:
+    a_0 = 0
+    b_0 = 0
+    i_0 = 0
+    j_0 = 0
+    j_1 = j_0
+    i_1 = i_0
+    b_1 = b_0
+    a_1 = a_0
+    goto  L2
+L2:
+    %t4_0 = i_1<3
+    if %t4_0 goto L3 else goto L4
+L3:
+    j_2 = 0
+    i_2 = i_1
+    a_2 = a_1
+    goto  L5
+L5:
+    %t5_0 = j_2<2
+    if %t5_0 goto L6 else goto L7
+L6:
+    %t6_0 = a_2+1
+    a_3 = %t6_0
+    %t7_0 = j_2+1
+    i_4 = %t7_0
+    i_2 = i_4
+    a_2 = a_3
+    goto  L5
+L7:
+    %t8_0 = b_1+1
+    b_2 = %t8_0
+    %t9_0 = i_2+1
+    i_3 = %t9_0
+    j_1 = j_2
+    i_1 = i_3
+    b_1 = b_2
+    a_1 = a_2
+    goto  L2
+L4:
+    %t10_0 = a_1+b_1
+    ret %t10_0
+    goto  L1
+L1:
+""", result);
+    }
+
+    @Test
+    public void testSSA10() {
+        String src = """
+        func main()->Int {
+            var sum = 0
+            var i = 0
             var j = 0
             while (i < 5) {
                 j = 0
@@ -1241,7 +1809,135 @@ func main()->Int {
         }
 """;
         String result = compileSrc(src);
-        System.out.println(result);
+        Assert.assertEquals("""
+func main
+Before SSA
+==========
+L0:
+    sum = 0
+    i = 0
+    j = 0
+    goto  L2
+L2:
+    %t3 = i<5
+    if %t3 goto L3 else goto L4
+L3:
+    j = 0
+    goto  L5
+L5:
+    %t4 = j<5
+    if %t4 goto L6 else goto L7
+L6:
+    %t5 = j%2
+    %t6 = %t5==0
+    if %t6 goto L8 else goto L9
+L8:
+    %t7 = sum+j
+    sum = %t7
+    goto  L9
+L9:
+    %t8 = j+1
+    j = %t8
+    goto  L5
+L7:
+    %t9 = i+1
+    i = %t9
+    goto  L2
+L4:
+    ret sum
+    goto  L1
+L1:
+After SSA
+=========
+L0:
+    sum_0 = 0
+    i_0 = 0
+    j_0 = 0
+    goto  L2
+L2:
+    j_1 = phi(j_0, j_3)
+    i_1 = phi(i_0, i_2)
+    sum_1 = phi(sum_0, sum_2)
+    %t3_0 = i_1<5
+    if %t3_0 goto L3 else goto L4
+L3:
+    j_2 = 0
+    goto  L5
+L5:
+    j_3 = phi(j_2, j_4)
+    sum_2 = phi(sum_1, sum_4)
+    %t4_0 = j_3<5
+    if %t4_0 goto L6 else goto L7
+L6:
+    %t5_0 = j_3%2
+    %t6_0 = %t5_0==0
+    if %t6_0 goto L8 else goto L9
+L8:
+    %t7_0 = sum_2+j_3
+    sum_3 = %t7_0
+    goto  L9
+L9:
+    sum_4 = phi(sum_2, sum_3)
+    %t8_0 = j_3+1
+    j_4 = %t8_0
+    goto  L5
+L7:
+    %t9_0 = i_1+1
+    i_2 = %t9_0
+    goto  L2
+L4:
+    ret sum_1
+    goto  L1
+L1:
+After exiting SSA
+=================
+L0:
+    sum_0 = 0
+    i_0 = 0
+    j_0 = 0
+    j_1 = j_0
+    i_1 = i_0
+    sum_1 = sum_0
+    goto  L2
+L2:
+    %t3_0 = i_1<5
+    if %t3_0 goto L3 else goto L4
+L3:
+    j_2 = 0
+    j_3 = j_2
+    sum_2 = sum_1
+    goto  L5
+L5:
+    %t4_0 = j_3<5
+    if %t4_0 goto L6 else goto L7
+L6:
+    %t5_0 = j_3%2
+    %t6_0 = %t5_0==0
+    sum_4 = sum_2
+    if %t6_0 goto L8 else goto L9
+L8:
+    %t7_0 = sum_2+j_3
+    sum_3 = %t7_0
+    sum_4 = sum_3
+    goto  L9
+L9:
+    %t8_0 = j_3+1
+    j_4 = %t8_0
+    j_3 = j_4
+    sum_2 = sum_4
+    goto  L5
+L7:
+    %t9_0 = i_1+1
+    i_2 = %t9_0
+    j_1 = j_3
+    i_1 = i_2
+    sum_1 = sum_2
+    goto  L2
+L4:
+    ret sum_1
+    goto  L1
+L1:
+""", result);
     }
 
     @Test
@@ -1266,7 +1962,165 @@ else if (i > j)
         }
 """;
         String result = compileSrc(src);
-        System.out.println(result);
+        Assert.assertEquals("""
+func main
+Before SSA
+==========
+L0:
+    a = 0
+    i = 0
+    j = 0
+    goto  L2
+L2:
+    %t3 = i<3
+    if %t3 goto L3 else goto L4
+L3:
+    j = 0
+    goto  L5
+L5:
+    %t4 = j<3
+    if %t4 goto L6 else goto L7
+L6:
+    %t5 = i==j
+    if %t5 goto L8 else goto L9
+L8:
+    %t6 = a+i
+    %t7 = %t6+j
+    a = %t7
+    goto  L10
+L10:
+    %t10 = j+1
+    j = %t10
+    goto  L5
+L9:
+    %t8 = i>j
+    if %t8 goto L11 else goto L12
+L11:
+    %t9 = a-1
+    a = %t9
+    goto  L12
+L12:
+    goto  L10
+L7:
+    %t11 = i+1
+    i = %t11
+    goto  L2
+L4:
+    ret a
+    goto  L1
+L1:
+After SSA
+=========
+L0:
+    a_0 = 0
+    i_0 = 0
+    j_0 = 0
+    goto  L2
+L2:
+    j_1 = phi(j_0, j_3)
+    i_1 = phi(i_0, i_2)
+    a_1 = phi(a_0, a_2)
+    %t3_0 = i_1<3
+    if %t3_0 goto L3 else goto L4
+L3:
+    j_2 = 0
+    goto  L5
+L5:
+    j_3 = phi(j_2, j_4)
+    a_2 = phi(a_1, a_6)
+    %t4_0 = j_3<3
+    if %t4_0 goto L6 else goto L7
+L6:
+    %t5_0 = i_1==j_3
+    if %t5_0 goto L8 else goto L9
+L8:
+    %t6_0 = a_4+i_1
+    %t7_0 = %t6_0+j_3
+    a_5 = %t7_0
+    goto  L10
+L10:
+    a_6 = phi(a_5, a_4)
+    %t10_0 = j_3+1
+    j_4 = %t10_0
+    goto  L5
+L9:
+    %t8_0 = i_1>j_3
+    if %t8_0 goto L11 else goto L12
+L11:
+    %t9_0 = a_2-1
+    a_3 = %t9_0
+    goto  L12
+L12:
+    a_4 = phi(a_2, a_3)
+    goto  L10
+L7:
+    %t11_0 = i_1+1
+    i_2 = %t11_0
+    goto  L2
+L4:
+    ret a_1
+    goto  L1
+L1:
+After exiting SSA
+=================
+L0:
+    a_0 = 0
+    i_0 = 0
+    j_0 = 0
+    j_1 = j_0
+    i_1 = i_0
+    a_1 = a_0
+    goto  L2
+L2:
+    %t3_0 = i_1<3
+    if %t3_0 goto L3 else goto L4
+L3:
+    j_2 = 0
+    j_3 = j_2
+    a_2 = a_1
+    goto  L5
+L5:
+    %t4_0 = j_3<3
+    if %t4_0 goto L6 else goto L7
+L6:
+    %t5_0 = i_1==j_3
+    if %t5_0 goto L8 else goto L9
+L8:
+    %t6_0 = a_4+i_1
+    %t7_0 = %t6_0+j_3
+    a_5 = %t7_0
+    a_6 = a_5
+    goto  L10
+L10:
+    %t10_0 = j_3+1
+    j_4 = %t10_0
+    j_3 = j_4
+    a_2 = a_6
+    goto  L5
+L9:
+    %t8_0 = i_1>j_3
+    a_4 = a_2
+    if %t8_0 goto L11 else goto L12
+L11:
+    %t9_0 = a_2-1
+    a_3 = %t9_0
+    a_4 = a_3
+    goto  L12
+L12:
+    a_6 = a_4
+    goto  L10
+L7:
+    %t11_0 = i_1+1
+    i_2 = %t11_0
+    j_1 = j_3
+    i_1 = i_2
+    a_1 = a_2
+    goto  L2
+L4:
+    ret a_1
+    goto  L1
+L1:
+""", result);
     }
 
     @Test
@@ -1294,7 +2148,157 @@ func main()->Int {
         }
 """;
         String result = compileSrc(src);
-        System.out.println(result);
+        Assert.assertEquals("""
+func main
+Before SSA
+==========
+L0:
+    count = 0
+    i = 0
+    j = 0
+    goto  L2
+L2:
+    %t3 = i<5
+    if %t3 goto L3 else goto L4
+L3:
+    j = 0
+    goto  L5
+L5:
+    %t4 = j<5
+    if %t4 goto L6 else goto L7
+L6:
+    %t5 = i+j
+    %t6 = %t5>5
+    if %t6 goto L8 else goto L9
+L8:
+    goto  L7
+L7:
+    %t11 = i+1
+    i = %t11
+    goto  L2
+L9:
+    %t7 = i==j
+    if %t7 goto L10 else goto L11
+L10:
+    %t8 = j+1
+    j = %t8
+    goto  L5
+L11:
+    %t9 = count+1
+    count = %t9
+    %t10 = j+1
+    j = %t10
+    goto  L5
+L4:
+    ret count
+    goto  L1
+L1:
+After SSA
+=========
+L0:
+    count_0 = 0
+    i_0 = 0
+    j_0 = 0
+    goto  L2
+L2:
+    j_1 = phi(j_0, j_3)
+    i_1 = phi(i_0, i_2)
+    count_1 = phi(count_0, count_2)
+    %t3_0 = i_1<5
+    if %t3_0 goto L3 else goto L4
+L3:
+    j_2 = 0
+    goto  L5
+L5:
+    j_3 = phi(j_2, j_5, j_4)
+    count_2 = phi(count_1, count_2, count_3)
+    %t4_0 = j_3<5
+    if %t4_0 goto L6 else goto L7
+L6:
+    %t5_0 = i_1+j_3
+    %t6_0 = %t5_0>5
+    if %t6_0 goto L8 else goto L9
+L8:
+    goto  L7
+L7:
+    %t11_0 = i_1+1
+    i_2 = %t11_0
+    goto  L2
+L9:
+    %t7_0 = i_1==j_3
+    if %t7_0 goto L10 else goto L11
+L10:
+    %t8_0 = j_3+1
+    j_5 = %t8_0
+    goto  L5
+L11:
+    %t9_0 = count_2+1
+    count_3 = %t9_0
+    %t10_0 = j_3+1
+    j_4 = %t10_0
+    goto  L5
+L4:
+    ret count_1
+    goto  L1
+L1:
+After exiting SSA
+=================
+L0:
+    count_0 = 0
+    i_0 = 0
+    j_0 = 0
+    j_1 = j_0
+    i_1 = i_0
+    count_1 = count_0
+    goto  L2
+L2:
+    %t3_0 = i_1<5
+    if %t3_0 goto L3 else goto L4
+L3:
+    j_2 = 0
+    j_3 = j_2
+    count_2 = count_1
+    goto  L5
+L5:
+    count_2_35 = count_2
+    %t4_0 = j_3<5
+    if %t4_0 goto L6 else goto L7
+L6:
+    %t5_0 = i_1+j_3
+    %t6_0 = %t5_0>5
+    if %t6_0 goto L8 else goto L9
+L8:
+    goto  L7
+L7:
+    %t11_0 = i_1+1
+    i_2 = %t11_0
+    j_1 = j_3
+    i_1 = i_2
+    count_1 = count_2
+    goto  L2
+L9:
+    %t7_0 = i_1==j_3
+    if %t7_0 goto L10 else goto L11
+L10:
+    %t8_0 = j_3+1
+    j_5 = %t8_0
+    j_3 = j_5
+    count_2_34 = count_2
+    count_2 = count_2_34
+    goto  L5
+L11:
+    %t9_0 = count_2+1
+    count_3 = %t9_0
+    %t10_0 = j_3+1
+    j_4 = %t10_0
+    j_3 = j_4
+    count_2 = count_3
+    goto  L5
+L4:
+    ret count_1
+    goto  L1
+L1:
+""", result);
     }
 
     @Test
@@ -1319,7 +2323,209 @@ func main()->Int {
         }
 """;
         String result = compileSrc(src);
-        System.out.println(result);
+        Assert.assertEquals("""
+func main
+Before SSA
+==========
+L0:
+    outerSum = 0
+    innerSum = 0
+    i = 0
+    j = 0
+    goto  L2
+L2:
+    %t4 = i<4
+    if %t4 goto L3 else goto L4
+L3:
+    j = 0
+    goto  L5
+L5:
+    %t5 = j<4
+    if %t5 goto L6 else goto L7
+L6:
+    %t6 = i+j
+    %t7 = %t6%2
+    %t8 = %t7==0
+    if %t8 goto L8 else goto L9
+L8:
+    %t9 = innerSum+j
+    innerSum = %t9
+    goto  L9
+L9:
+    %t10 = j+1
+    j = %t10
+    goto  L5
+L7:
+    %t11 = outerSum+innerSum
+    outerSum = %t11
+    %t12 = i+1
+    i = %t12
+    goto  L2
+L4:
+    ret outerSum
+    goto  L1
+L1:
+After SSA
+=========
+L0:
+    outerSum_0 = 0
+    innerSum_0 = 0
+    i_0 = 0
+    j_0 = 0
+    goto  L2
+L2:
+    j_1 = phi(j_0, j_3)
+    i_1 = phi(i_0, i_2)
+    innerSum_1 = phi(innerSum_0, innerSum_2)
+    outerSum_1 = phi(outerSum_0, outerSum_2)
+    %t4_0 = i_1<4
+    if %t4_0 goto L3 else goto L4
+L3:
+    j_2 = 0
+    goto  L5
+L5:
+    j_3 = phi(j_2, j_4)
+    innerSum_2 = phi(innerSum_1, innerSum_4)
+    %t5_0 = j_3<4
+    if %t5_0 goto L6 else goto L7
+L6:
+    %t6_0 = i_1+j_3
+    %t7_0 = %t6_0%2
+    %t8_0 = %t7_0==0
+    if %t8_0 goto L8 else goto L9
+L8:
+    %t9_0 = innerSum_2+j_3
+    innerSum_3 = %t9_0
+    goto  L9
+L9:
+    innerSum_4 = phi(innerSum_2, innerSum_3)
+    %t10_0 = j_3+1
+    j_4 = %t10_0
+    goto  L5
+L7:
+    %t11_0 = outerSum_1+innerSum_2
+    outerSum_2 = %t11_0
+    %t12_0 = i_1+1
+    i_2 = %t12_0
+    goto  L2
+L4:
+    ret outerSum_1
+    goto  L1
+L1:
+After exiting SSA
+=================
+L0:
+    outerSum_0 = 0
+    innerSum_0 = 0
+    i_0 = 0
+    j_0 = 0
+    j_1 = j_0
+    i_1 = i_0
+    innerSum_1 = innerSum_0
+    outerSum_1 = outerSum_0
+    goto  L2
+L2:
+    %t4_0 = i_1<4
+    if %t4_0 goto L3 else goto L4
+L3:
+    j_2 = 0
+    j_3 = j_2
+    innerSum_2 = innerSum_1
+    goto  L5
+L5:
+    %t5_0 = j_3<4
+    if %t5_0 goto L6 else goto L7
+L6:
+    %t6_0 = i_1+j_3
+    %t7_0 = %t6_0%2
+    %t8_0 = %t7_0==0
+    innerSum_4 = innerSum_2
+    if %t8_0 goto L8 else goto L9
+L8:
+    %t9_0 = innerSum_2+j_3
+    innerSum_3 = %t9_0
+    innerSum_4 = innerSum_3
+    goto  L9
+L9:
+    %t10_0 = j_3+1
+    j_4 = %t10_0
+    j_3 = j_4
+    innerSum_2 = innerSum_4
+    goto  L5
+L7:
+    %t11_0 = outerSum_1+innerSum_2
+    outerSum_2 = %t11_0
+    %t12_0 = i_1+1
+    i_2 = %t12_0
+    j_1 = j_3
+    i_1 = i_2
+    innerSum_1 = innerSum_2
+    outerSum_1 = outerSum_2
+    goto  L2
+L4:
+    ret outerSum_1
+    goto  L1
+L1:
+""", result);
     }
 
+    @Ignore
+    @Test
+    public void testSSA14() {
+        String src = """
+                func foo()->Int 
+                {
+                    return 1 && 2
+                }
+""";
+        String result = compileSrc(src);
+        Assert.assertEquals("""
+func foo
+Before SSA
+==========
+L0:
+    if 1 goto L2 else goto L3
+L2:
+    %t0 = 2
+    goto  L4
+L4:
+    ret %t0
+    goto  L1
+L1:
+L3:
+    %t0 = 0
+    goto  L4
+After SSA
+=========
+L0:
+    if 1 goto L2 else goto L3
+L2:
+    %t0_1 = 2
+    goto  L4
+L4:
+    %t0_2 = phi(%t0_1, %t0_0)
+    ret %t0_2
+    goto  L1
+L1:
+L3:
+    %t0_0 = 0
+    goto  L4
+After exiting SSA
+=================
+L0:
+    if 1 goto L2 else goto L3
+L2:
+    %t0_1 = 2
+    %t0_2 = %t0_1
+    goto  L4
+L4:
+    ret %t0_2
+    goto  L1
+L1:
+L3:
+    %t0_0 = 0
+    %t0_2 = %t0_0
+    goto  L4
+""", result);
+    }
 }
\ No newline at end of file

From cfd9b76d9fc376e77e943ba366515a5743f9f507 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <dibyendu@redukti.com>
Date: Tue, 18 Feb 2025 11:19:39 +0000
Subject: [PATCH 035/119] Fix bug in EnterSSA caused by mutable operands shared
 across instructions, replacing the phi register cause other instructions to
 change inadvertantly.

Fix bug in EnterSSA caused by mutable operands shared across instructions, replacing the phi register cause other instructions to change inadvertantly.

Fix bug in EnterSSA caused by mutable operands shared across instructions, replacing the phi register cause other instructions to change inadvertantly.
---
 .../ezlang/compiler/CompiledFunction.java     |  77 +++++++++-
 .../ezlang/compiler/Compiler.java             |   2 +-
 .../ezlang/compiler/Instruction.java          |   8 +-
 .../ezlang/compiler/Operand.java              |  19 ++-
 .../compiler/TestInterferenceGraph.java       |   6 +-
 .../ezlang/compiler/TestLiveness.java         |   2 +-
 .../ezlang/compiler/TestSSATransform.java     |   5 +-
 .../ezlang/interpreter/TestInterpreter.java   | 143 ++++++++++++++++++
 8 files changed, 239 insertions(+), 23 deletions(-)

diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
index a686e8e..418db05 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
@@ -5,6 +5,7 @@
 import com.compilerprogramming.ezlang.types.Scope;
 import com.compilerprogramming.ezlang.types.Symbol;
 import com.compilerprogramming.ezlang.types.Type;
+import com.compilerprogramming.ezlang.types.TypeDictionary;
 
 import java.util.ArrayList;
 import java.util.BitSet;
@@ -20,6 +21,7 @@ public class CompiledFunction {
     private BasicBlock currentContinueTarget;
     private Type.TypeFunction functionType;
     public final RegisterPool registerPool;
+    private final TypeDictionary typeDictionary;
 
     private int frameSlots;
 
@@ -36,7 +38,7 @@ public class CompiledFunction {
      */
     private List<Operand> virtualStack = new ArrayList<>();
 
-    public CompiledFunction(Symbol.FunctionTypeSymbol functionSymbol) {
+    public CompiledFunction(Symbol.FunctionTypeSymbol functionSymbol, TypeDictionary typeDictionary) {
         AST.FuncDecl funcDecl = (AST.FuncDecl) functionSymbol.functionDecl;
         this.functionType = (Type.TypeFunction) functionSymbol.type;
         this.registerPool = new RegisterPool();
@@ -46,13 +48,14 @@ public CompiledFunction(Symbol.FunctionTypeSymbol functionSymbol) {
         this.exit = createBlock();
         this.currentBreakTarget = null;
         this.currentContinueTarget = null;
+        this.typeDictionary = typeDictionary;
         generateArgInstructions(funcDecl.scope);
         compileStatement(funcDecl.block);
         exitBlockIfNeeded();
         this.frameSlots = registerPool.numRegisters();
     }
 
-    public CompiledFunction(Type.TypeFunction functionType) {
+    public CompiledFunction(Type.TypeFunction functionType, TypeDictionary typeDictionary) {
         this.functionType = (Type.TypeFunction) functionType;
         this.registerPool = new RegisterPool();
         this.BID = 0;
@@ -60,6 +63,7 @@ public CompiledFunction(Type.TypeFunction functionType) {
         this.exit = createBlock();
         this.currentBreakTarget = null;
         this.currentContinueTarget = null;
+        this.typeDictionary = typeDictionary;
         this.frameSlots = registerPool.numRegisters();
     }
 
@@ -437,8 +441,42 @@ private boolean compileSymbolExpr(AST.NameExpr symbolExpr) {
         return false;
     }
 
+    private boolean codeBoolean(AST.BinaryExpr binaryExpr) {
+        boolean isAnd = binaryExpr.op.str.equals("&&");
+        BasicBlock l1 = createBlock();
+        BasicBlock l2 = createBlock();
+        BasicBlock l3 = createBlock();
+        boolean indexed = compileExpr(binaryExpr.expr1);
+        if (indexed)
+            codeIndexedLoad();
+        if (isAnd) {
+            code(new Instruction.ConditionalBranch(currentBlock, pop(), l1, l2));
+        } else {
+            code(new Instruction.ConditionalBranch(currentBlock, pop(), l2, l1));
+        }
+        startBlock(l1);
+        compileExpr(binaryExpr.expr2);
+        var temp = ensureTemp();
+        jumpTo(l3);
+        startBlock(l2);
+        // Below we must write to the same temp
+        //code(new Instruction.Move(new Operand.ConstantOperand(isAnd ? 0 : 1, typeDictionary.INT), new Operand.TempRegisterOperand(temp.reg)));
+        code(new Instruction.Move(new Operand.ConstantOperand(isAnd ? 0 : 1, typeDictionary.INT), temp));
+        jumpTo(l3);
+        startBlock(l3);
+        // leave temp on virtual stack
+//        var temp2 = (Operand.TempRegisterOperand) pop();
+//        pushOperand(new Operand.TempRegisterOperand(temp2.reg));
+        return false;
+    }
+
+
     private boolean compileBinaryExpr(AST.BinaryExpr binaryExpr) {
-        String opCode = null;
+        String opCode = binaryExpr.op.str;
+        if (opCode.equals("&&") ||
+            opCode.equals("||")) {
+            return codeBoolean(binaryExpr);
+        }
         boolean indexed = compileExpr(binaryExpr.expr1);
         if (indexed)
             codeIndexedLoad();
@@ -511,6 +549,36 @@ private Operand.TempRegisterOperand createTemp(Type type) {
         return tempRegister;
     }
 
+    Type typeOfOperand(Operand operand) {
+        if (operand instanceof Operand.ConstantOperand constant)
+            return constant.type;
+//        else if (operand instanceof Operand.NullConstantOperand nullConstantOperand)
+//            return nullConstantOperand.type;
+        else if (operand instanceof Operand.RegisterOperand registerOperand)
+            return registerOperand.type;
+        else throw new CompilerException("Invalid operand");
+    }
+
+    private Operand.TempRegisterOperand createTempAndMove(Operand src) {
+        Type type = typeOfOperand(src);
+        var temp = createTemp(type);
+        code(new Instruction.Move(src, temp));
+        return temp;
+    }
+
+    private Operand.RegisterOperand ensureTemp() {
+        Operand top = top();
+        if (top instanceof Operand.ConstantOperand
+                //|| top instanceof Operand.NullConstantOperand
+                || top instanceof Operand.LocalRegisterOperand) {
+            return createTempAndMove(pop());
+        } else if (top instanceof Operand.IndexedOperand) {
+            return codeIndexedLoad();
+        } else if (top instanceof Operand.TempRegisterOperand tempRegisterOperand) {
+            return tempRegisterOperand;
+        } else throw new CompilerException("Cannot convert to temporary register");
+    }
+
     private void pushLocal(Register reg) {
         pushOperand(new Operand.LocalRegisterOperand(reg));
     }
@@ -527,7 +595,7 @@ private Operand top() {
         return virtualStack.getLast();
     }
 
-    private void codeIndexedLoad() {
+    private Operand.TempRegisterOperand codeIndexedLoad() {
         Operand indexed = pop();
         var temp = createTemp(indexed.type);
         if (indexed instanceof Operand.LoadIndexedOperand loadIndexedOperand) {
@@ -538,6 +606,7 @@ else if (indexed instanceof Operand.LoadFieldOperand loadFieldOperand) {
         }
         else
             code(new Instruction.Move(indexed, temp));
+        return temp;
     }
 
     private void codeIndexedStore() {
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java
index df64e5c..4c24bb6 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java
@@ -16,7 +16,7 @@ private void compile(TypeDictionary typeDictionary, EnumSet<Options> options) {
         for (Symbol symbol: typeDictionary.getLocalSymbols()) {
             if (symbol instanceof Symbol.FunctionTypeSymbol functionSymbol) {
                 Type.TypeFunction functionType = (Type.TypeFunction) functionSymbol.type;
-                var function = new CompiledFunction(functionSymbol);
+                var function = new CompiledFunction(functionSymbol, typeDictionary);
                 if (options.contains(Options.DUMP_INITIAL_IR))
                     function.dumpIR(false, "Initial IR");
                 functionType.code = function;
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java
index c4005a7..1651c1c 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java
@@ -67,14 +67,14 @@ public List<Register> uses() {
     }
     public void replaceDef(Register newReg) {
         if (def == null) throw new IllegalStateException();
-        def.replaceRegister(newReg);
+        def = def.copy(newReg);
     }
     public void replaceUses(Register[] newUses) {
         int j = 0;
         for (int i = 0; i < uses.length; i++) {
             Operand use = uses[i];
             if (use != null && use instanceof Operand.RegisterOperand registerOperand) {
-                registerOperand.replaceRegister(newUses[j++]);
+                uses[i] = registerOperand.copy(newUses[j++]);
             }
         }
     }
@@ -83,7 +83,7 @@ public boolean replaceUse(Register source, Register target) {
         for (int i = 0; i < uses.length; i++) {
             Operand operand = uses[i];
             if (operand != null && operand instanceof Operand.RegisterOperand registerOperand && registerOperand.reg.id == source.id) {
-                registerOperand.replaceRegister(target);
+                uses[i] = registerOperand.copy(target);
                 replaced = true;
             }
         }
@@ -362,7 +362,7 @@ public Phi(Register value, List<Register> inputs) {
             }
         }
         public void replaceInput(int i, Register newReg) {
-            uses[i].replaceRegister(newReg);
+            uses[i] =  new Operand.RegisterOperand(newReg);
         }
         /**
          * This will fail in input was replaced by a constant
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Operand.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Operand.java
index e91531e..b4bde07 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Operand.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Operand.java
@@ -6,8 +6,6 @@ public class Operand {
 
     Type type;
 
-    public void replaceRegister(Register register) {}
-
     public static class ConstantOperand extends Operand {
         public final long value;
         public ConstantOperand(long value, Type type) {
@@ -21,17 +19,16 @@ public String toString() {
     }
 
     public static class RegisterOperand extends Operand {
-        Register reg;
-        public RegisterOperand(Register reg) {
+        final Register reg;
+        protected RegisterOperand(Register reg) {
             this.reg = reg;
             if (reg == null)
                 throw new NullPointerException();
         }
         public int frameSlot() { return reg.nonSSAId(); }
 
-        @Override
-        public void replaceRegister(Register register) {
-            this.reg = register;
+        public RegisterOperand copy(Register register) {
+           return new RegisterOperand(register);
         }
 
         @Override
@@ -44,6 +41,10 @@ public static class LocalRegisterOperand extends RegisterOperand {
         public LocalRegisterOperand(Register reg) {
             super(reg);
         }
+        @Override
+        public RegisterOperand copy(Register register) {
+            return new LocalRegisterOperand(register);
+        }
     }
 
     public static class LocalFunctionOperand extends Operand {
@@ -66,6 +67,10 @@ public static class TempRegisterOperand extends RegisterOperand {
         public TempRegisterOperand(Register reg) {
             super(reg);
         }
+        @Override
+        public RegisterOperand copy(Register register) {
+            return new TempRegisterOperand(register);
+        }
     }
 
     public static class IndexedOperand extends Operand {}
diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestInterferenceGraph.java b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestInterferenceGraph.java
index 2cfe554..e5a4299 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestInterferenceGraph.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestInterferenceGraph.java
@@ -13,7 +13,7 @@ private CompiledFunction buildTest1() {
         Type.TypeFunction functionType = new Type.TypeFunction("foo");
         functionType.addArg(new Symbol.ParameterSymbol("a", typeDictionary.INT));
         functionType.setReturnType(typeDictionary.INT);
-        CompiledFunction function = new CompiledFunction(functionType);
+        CompiledFunction function = new CompiledFunction(functionType, typeDictionary);
         RegisterPool regPool = function.registerPool;
         Register a = regPool.newReg("a", typeDictionary.INT);
         Register b = regPool.newReg("b", typeDictionary.INT);
@@ -82,7 +82,7 @@ private CompiledFunction buildTest2() {
         TypeDictionary typeDictionary = new TypeDictionary();
         Type.TypeFunction functionType = new Type.TypeFunction("foo");
         functionType.setReturnType(typeDictionary.VOID);
-        CompiledFunction function = new CompiledFunction(functionType);
+        CompiledFunction function = new CompiledFunction(functionType, typeDictionary);
         RegisterPool regPool = function.registerPool;
         Register a = regPool.newReg("a", typeDictionary.INT);
         Register b = regPool.newReg("b", typeDictionary.INT);
@@ -158,7 +158,7 @@ public static CompiledFunction buildTest4() {
         TypeDictionary typeDictionary = new TypeDictionary();
         Type.TypeFunction functionType = new Type.TypeFunction("foo");
         functionType.setReturnType(typeDictionary.VOID);
-        CompiledFunction function = new CompiledFunction(functionType);
+        CompiledFunction function = new CompiledFunction(functionType, typeDictionary);
         RegisterPool regPool = function.registerPool;
         Register a = regPool.newReg("a", typeDictionary.INT);
         Register b = regPool.newReg("b", typeDictionary.INT);
diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestLiveness.java b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestLiveness.java
index b1b6222..c9c2d27 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestLiveness.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestLiveness.java
@@ -247,7 +247,7 @@ static CompiledFunction buildTest3() {
         TypeDictionary typeDictionary = new TypeDictionary();
         Type.TypeFunction functionType = new Type.TypeFunction("foo");
         functionType.setReturnType(typeDictionary.INT);
-        CompiledFunction function = new CompiledFunction(functionType);
+        CompiledFunction function = new CompiledFunction(functionType, typeDictionary);
         RegisterPool regPool = function.registerPool;
         Register i = regPool.newReg("i", typeDictionary.INT);
         Register s = regPool.newReg("s", typeDictionary.INT);
diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
index 703ca2a..3f8a052 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
@@ -640,7 +640,7 @@ static CompiledFunction buildLostCopyTest() {
         Type.TypeFunction functionType = new Type.TypeFunction("foo");
         functionType.addArg(new Symbol.ParameterSymbol("p", typeDictionary.INT));
         functionType.setReturnType(typeDictionary.INT);
-        CompiledFunction function = new CompiledFunction(functionType);
+        CompiledFunction function = new CompiledFunction(functionType, typeDictionary);
         RegisterPool regPool = function.registerPool;
         Register p = regPool.newReg("p", typeDictionary.INT);
         Register x1 = regPool.newReg("x1", typeDictionary.INT);
@@ -708,7 +708,7 @@ static CompiledFunction buildSwapTest() {
         Type.TypeFunction functionType = new Type.TypeFunction("foo");
         functionType.addArg(new Symbol.ParameterSymbol("p", typeDictionary.INT));
         functionType.setReturnType(typeDictionary.VOID);
-        CompiledFunction function = new CompiledFunction(functionType);
+        CompiledFunction function = new CompiledFunction(functionType, typeDictionary);
         RegisterPool regPool = function.registerPool;
         Register p = regPool.newReg("p", typeDictionary.INT);
         Register a1 = regPool.newReg("a1", typeDictionary.INT);
@@ -2469,7 +2469,6 @@ func main()->Int {
 """, result);
     }
 
-    @Ignore
     @Test
     public void testSSA14() {
         String src = """
diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java b/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java
index 01eb89c..e463a29 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java
@@ -445,4 +445,147 @@ func foo()->Int {
                 && integerValue.value == 5+(5*21+25/5));
     }
 
+
+//    @Test
+//    public void testFunction100() {
+//        String src = """
+//                struct Test
+//                {
+//                    var field: Int
+//                }
+//                func foo()->Test?
+//                {
+//                    return null;
+//                }
+//
+//                """;
+//        var value = compileAndRun(src, "foo", Options.OPT);
+//        Assert.assertNotNull(value);
+//        Assert.assertTrue(value instanceof Value.NullValue);
+//    }
+//
+//    @Test
+//    public void testFunction101() {
+//        String src = """
+//                func foo()->Int
+//                {
+//                    return null == null;
+//                }
+//
+//                """;
+//        var value = compileAndRun(src, "foo", Options.OPT);
+//        Assert.assertNotNull(value);
+//        Assert.assertTrue(value instanceof Value.IntegerValue integerValue &&
+//                integerValue.value == 1);
+//    }
+//
+//    @Test
+//    public void testFunction102() {
+//        String src = """
+//                struct Foo
+//                {
+//                    var next: Foo?
+//                }
+//                func foo()->Int
+//                {
+//                    var f = new Foo{ next = null }
+//                    return null == f.next
+//                }
+//
+//                """;
+//        var value = compileAndRun(src, "foo", Options.OPT);
+//        Assert.assertNotNull(value);
+//        Assert.assertTrue(value instanceof Value.IntegerValue integerValue &&
+//                integerValue.value == 1);
+//    }
+//
+//    @Test
+//    public void testFunction103() {
+//        String src = """
+//                struct Foo
+//                {
+//                    var i: Int
+//                }
+//                func foo()->Int
+//                {
+//                    var f = new [Foo?] { new Foo{i = 1}, null }
+//                    return null == f[1] && 1 == f[0].i
+//                }
+//
+//                """;
+//        var value = compileAndRun(src, "foo", Options.OPT);
+//        Assert.assertNotNull(value);
+//        Assert.assertTrue(value instanceof Value.IntegerValue integerValue &&
+//                integerValue.value == 1);
+//    }
+
+    @Test
+    public void testFunction104() {
+        String src = """
+                func foo()->Int
+                {
+                    return 1 == 1 && 2 == 2
+                }
+
+                """;
+        var value = compileAndRun(src, "foo", Options.OPT);
+        Assert.assertNotNull(value);
+        Assert.assertTrue(value instanceof Value.IntegerValue integerValue &&
+                integerValue.value == 1);
+    }
+
+    @Test
+    public void testFunction105() {
+        String src = """
+                func bar(a: Int, b: Int)->Int
+                {
+                    return a+1 == b-1 && b / a == 2
+                }
+                func foo()->Int
+                {
+                    return bar(3,5)
+                }
+                """;
+        var value = compileAndRun(src, "foo", Options.OPT);
+        Assert.assertNotNull(value);
+        Assert.assertTrue(value instanceof Value.IntegerValue integerValue &&
+                integerValue.value == 0);
+    }
+
+    @Test
+    public void testFunction106() {
+        String src = """
+                func bar(a: Int, b: Int)->Int
+                {
+                    return a+1 == b-1 || b / a == 2
+                }
+                func foo()->Int
+                {
+                    return bar(3,5)
+                }
+                """;
+        var value = compileAndRun(src, "foo", Options.OPT);
+        Assert.assertNotNull(value);
+        Assert.assertTrue(value instanceof Value.IntegerValue integerValue &&
+                integerValue.value == 1);
+    }
+
+    @Test
+    public void testFunction107() {
+        String src = """
+                func bar(a: [Int])->Int
+                {
+                    return a[0]+a[2] == a[1]-a[2] || a[1] / a[0] == 2
+                }
+                func foo()->Int
+                {
+                    return bar(new [Int] {3,5,1})
+                }
+                """;
+        var value = compileAndRun(src, "foo", Options.OPT);
+        Assert.assertNotNull(value);
+        Assert.assertTrue(value instanceof Value.IntegerValue integerValue &&
+                integerValue.value == 1);
+    }
+
 }

From 0c82836788495d07a1401e1c24065e05cd5aae11 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <dibyendu@redukti.com>
Date: Mon, 17 Feb 2025 22:45:40 +0000
Subject: [PATCH 036/119] more work on boolean binary expressions

Fix bug in EnterSSA caused by mutable operands shared across instructions, replacing the phi register cause other instructions to change inadvertantly.

Ensure SSA tests check output
---
 .../ezlang/compiler/CompiledFunction.java     | 67 +++++++++++-------
 .../ezlang/interpreter/TestInterpreter.java   | 68 +++++++++++++++++++
 2 files changed, 110 insertions(+), 25 deletions(-)

diff --git a/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java b/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
index ef5a842..9cd5112 100644
--- a/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
+++ b/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
@@ -412,42 +412,28 @@ private boolean compileSymbolExpr(AST.NameExpr symbolExpr) {
     }
 
     private boolean codeBoolean(AST.BinaryExpr binaryExpr) {
-        BasicBlock l1;
-        BasicBlock l2;
-        BasicBlock l3;
-        boolean indexed;
-        boolean isAnd;
-
-        l1 = createBlock();
-        l2 = createBlock();
-        l3 = createBlock();
-        indexed = compileExpr(binaryExpr.expr1);
-        if (indexed) {
+        boolean isAnd = binaryExpr.op.str.equals("&&");
+        BasicBlock l1 = createBlock();
+        BasicBlock l2 = createBlock();
+        BasicBlock l3 = createBlock();
+        boolean indexed = compileExpr(binaryExpr.expr1);
+        if (indexed)
             codeIndexedLoad();
-        }
-        isAnd = binaryExpr.op.str.equals("&&");
-        // FIXME ensure temp
-        var operand = pop();
-        var temp = createTemp(typeDictionary.INT);
-        code(new Instruction.Move(operand, temp));
         if (isAnd) {
             code(new Instruction.ConditionalBranch(currentBlock, pop(), l1, l2));
         } else {
             code(new Instruction.ConditionalBranch(currentBlock, pop(), l2, l1));
         }
         startBlock(l1);
-        indexed = compileExpr(binaryExpr.expr2);
-        if (indexed) {
-            codeIndexedLoad();
-        }
-        operand = pop();
-        temp = createTemp(typeDictionary.INT);
-        code(new Instruction.Move(operand, temp));
+        compileExpr(binaryExpr.expr2);
+        var temp = ensureTemp();
         jumpTo(l3);
         startBlock(l2);
+        // Below we must write to the same temp
         code(new Instruction.Move(new Operand.ConstantOperand(isAnd ? 0 : 1, typeDictionary.INT), temp));
         jumpTo(l3);
         startBlock(l3);
+        // leave temp on virtual stack
         return false;
     }
 
@@ -549,6 +535,36 @@ private Operand.TempRegisterOperand createTemp(Type type) {
         return tempRegister;
     }
 
+    Type typeOfOperand(Operand operand) {
+        if (operand instanceof Operand.ConstantOperand constant)
+            return constant.type;
+        else if (operand instanceof Operand.NullConstantOperand nullConstantOperand)
+            return nullConstantOperand.type;
+        else if (operand instanceof Operand.RegisterOperand registerOperand)
+            return registerOperand.type;
+        else throw new CompilerException("Invalid operand");
+    }
+
+    private Operand.TempRegisterOperand createTempAndMove(Operand src) {
+        Type type = typeOfOperand(src);
+        var temp = createTemp(type);
+        code(new Instruction.Move(src, temp));
+        return temp;
+    }
+
+    private Operand.RegisterOperand ensureTemp() {
+        Operand top = top();
+        if (top instanceof Operand.ConstantOperand
+                || top instanceof Operand.NullConstantOperand
+                || top instanceof Operand.LocalRegisterOperand) {
+            return createTempAndMove(pop());
+        } else if (top instanceof Operand.IndexedOperand) {
+            return codeIndexedLoad();
+        } else if (top instanceof Operand.TempRegisterOperand tempRegisterOperand) {
+            return tempRegisterOperand;
+        } else throw new CompilerException("Cannot convert to temporary register");
+    }
+
     private void pushLocal(int regnum, String varName) {
         pushOperand(new Operand.LocalRegisterOperand(regnum, varName));
     }
@@ -565,7 +581,7 @@ private Operand top() {
         return virtualStack.getLast();
     }
 
-    private void codeIndexedLoad() {
+    private Operand.TempRegisterOperand codeIndexedLoad() {
         Operand indexed = pop();
         var temp = createTemp(indexed.type);
         if (indexed instanceof Operand.LoadIndexedOperand loadIndexedOperand) {
@@ -576,6 +592,7 @@ else if (indexed instanceof Operand.LoadFieldOperand loadFieldOperand) {
         }
         else
             code(new Instruction.Move(indexed, temp));
+        return temp;
     }
 
     private void codeIndexedStore() {
diff --git a/registervm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java b/registervm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java
index c99ce22..fcb93e2 100644
--- a/registervm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java
+++ b/registervm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java
@@ -176,4 +176,72 @@ func foo()->Int
                 integerValue.value == 1);
     }
 
+    @Test
+    public void testFunction104() {
+        String src = """
+                func foo()->Int 
+                {
+                    return 1 == 1 && 2 == 2
+                }
+
+                """;
+        var value = compileAndRun(src, "foo");
+        Assert.assertNotNull(value);
+        Assert.assertTrue(value instanceof Value.IntegerValue integerValue &&
+                integerValue.value == 1);
+    }
+
+    @Test
+    public void testFunction105() {
+        String src = """
+                func bar(a: Int, b: Int)->Int 
+                {
+                    return a+1 == b-1 && b / a == 2
+                }
+                func foo()->Int
+                {
+                    return bar(3,5)
+                }
+                """;
+        var value = compileAndRun(src, "foo");
+        Assert.assertNotNull(value);
+        Assert.assertTrue(value instanceof Value.IntegerValue integerValue &&
+                integerValue.value == 0);
+    }
+
+    @Test
+    public void testFunction106() {
+        String src = """
+                func bar(a: Int, b: Int)->Int 
+                {
+                    return a+1 == b-1 || b / a == 2
+                }
+                func foo()->Int
+                {
+                    return bar(3,5)
+                }
+                """;
+        var value = compileAndRun(src, "foo");
+        Assert.assertNotNull(value);
+        Assert.assertTrue(value instanceof Value.IntegerValue integerValue &&
+                integerValue.value == 1);
+    }
+
+    @Test
+    public void testFunction107() {
+        String src = """
+                func bar(a: [Int])->Int 
+                {
+                    return a[0]+a[2] == a[1]-a[2] || a[1] / a[0] == 2
+                }
+                func foo()->Int
+                {
+                    return bar(new [Int] {3,5,1})
+                }
+                """;
+        var value = compileAndRun(src, "foo");
+        Assert.assertNotNull(value);
+        Assert.assertTrue(value instanceof Value.IntegerValue integerValue &&
+                integerValue.value == 1);
+    }
 }

From f666febd54b59f6a8c8541620005fcf8ae09c9ec Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <dibyendu@redukti.com>
Date: Tue, 18 Feb 2025 14:09:03 +0000
Subject: [PATCH 037/119] Initial support for nulls in optvm - but we are
 missing null check at runtime for scenarios where T is assigned T? - this is
 allowed by compiler but must be null checked at runtime

---
 .../ezlang/compiler/CompiledFunction.java     |  32 ++--
 .../ezlang/compiler/InterferenceGraph.java    |   2 +-
 .../ezlang/compiler/Operand.java              |  10 ++
 .../SparseConditionalConstantPropagation.java |  47 +++---
 .../ezlang/interpreter/Interpreter.java       |  95 +++++++++---
 .../ezlang/interpreter/Value.java             |   3 +
 .../ezlang/compiler/TestSSATransform.java     |  94 ++++++++++++
 .../ezlang/interpreter/TestInterpreter.java   | 144 +++++++++---------
 8 files changed, 300 insertions(+), 127 deletions(-)

diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
index 418db05..06c42b5 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
@@ -460,13 +460,10 @@ private boolean codeBoolean(AST.BinaryExpr binaryExpr) {
         jumpTo(l3);
         startBlock(l2);
         // Below we must write to the same temp
-        //code(new Instruction.Move(new Operand.ConstantOperand(isAnd ? 0 : 1, typeDictionary.INT), new Operand.TempRegisterOperand(temp.reg)));
         code(new Instruction.Move(new Operand.ConstantOperand(isAnd ? 0 : 1, typeDictionary.INT), temp));
         jumpTo(l3);
         startBlock(l3);
         // leave temp on virtual stack
-//        var temp2 = (Operand.TempRegisterOperand) pop();
-//        pushOperand(new Operand.TempRegisterOperand(temp2.reg));
         return false;
     }
 
@@ -483,10 +480,19 @@ private boolean compileBinaryExpr(AST.BinaryExpr binaryExpr) {
         indexed = compileExpr(binaryExpr.expr2);
         if (indexed)
             codeIndexedLoad();
-        opCode = binaryExpr.op.str;
         Operand right = pop();
         Operand left = pop();
-        if (left instanceof Operand.ConstantOperand leftconstant &&
+        if (left instanceof Operand.NullConstantOperand &&
+            right instanceof Operand.NullConstantOperand) {
+            long value = 0;
+            switch (opCode) {
+                case "==": value = 1; break;
+                case "!=": value = 0; break;
+                default: throw new CompilerException("Invalid binary op");
+            }
+            pushConstant(value, typeDictionary.INT);
+        }
+        else if (left instanceof Operand.ConstantOperand leftconstant &&
                 right instanceof Operand.ConstantOperand rightconstant) {
             long value = 0;
             switch (opCode) {
@@ -535,7 +541,11 @@ private boolean compileUnaryExpr(AST.UnaryExpr unaryExpr) {
     }
 
     private boolean compileConstantExpr(AST.LiteralExpr constantExpr) {
-        pushConstant(constantExpr.value.num.intValue(), constantExpr.type);
+        if (constantExpr.type instanceof Type.TypeInteger)
+            pushConstant(constantExpr.value.num.intValue(), constantExpr.type);
+        else if (constantExpr.type instanceof Type.TypeNull)
+            pushNullConstant(constantExpr.type);
+        else throw new CompilerException("Invalid constant type");
         return false;
     }
 
@@ -543,6 +553,10 @@ private void pushConstant(long value, Type type) {
         pushOperand(new Operand.ConstantOperand(value, type));
     }
 
+    private void pushNullConstant(Type type) {
+        pushOperand(new Operand.NullConstantOperand(type));
+    }
+
     private Operand.TempRegisterOperand createTemp(Type type) {
         var tempRegister = new Operand.TempRegisterOperand(registerPool.newTempReg(type));
         pushOperand(tempRegister);
@@ -552,8 +566,8 @@ private Operand.TempRegisterOperand createTemp(Type type) {
     Type typeOfOperand(Operand operand) {
         if (operand instanceof Operand.ConstantOperand constant)
             return constant.type;
-//        else if (operand instanceof Operand.NullConstantOperand nullConstantOperand)
-//            return nullConstantOperand.type;
+        else if (operand instanceof Operand.NullConstantOperand nullConstantOperand)
+            return nullConstantOperand.type;
         else if (operand instanceof Operand.RegisterOperand registerOperand)
             return registerOperand.type;
         else throw new CompilerException("Invalid operand");
@@ -569,7 +583,7 @@ private Operand.TempRegisterOperand createTempAndMove(Operand src) {
     private Operand.RegisterOperand ensureTemp() {
         Operand top = top();
         if (top instanceof Operand.ConstantOperand
-                //|| top instanceof Operand.NullConstantOperand
+                || top instanceof Operand.NullConstantOperand
                 || top instanceof Operand.LocalRegisterOperand) {
             return createTempAndMove(pop());
         } else if (top instanceof Operand.IndexedOperand) {
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/InterferenceGraph.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/InterferenceGraph.java
index 1714f6b..1749d49 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/InterferenceGraph.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/InterferenceGraph.java
@@ -65,7 +65,7 @@ public void rename(Integer source, Integer target) {
         var toSet = edges.get(target);
         if (toSet == null) {
             //throw new RuntimeException("Cannot find edge " + target + " from " + source);
-            return; // FIXME this is workaround to handle sceanrio where target is arg register but we need a better way
+            return; // FIXME this is workaround to handle scenario where target is arg register but we need a better way
         }
         toSet.addAll(fromSet);
         // If any node interfered with from
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Operand.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Operand.java
index b4bde07..57a691e 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Operand.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Operand.java
@@ -18,6 +18,16 @@ public String toString() {
         }
     }
 
+    public static class NullConstantOperand extends Operand {
+        public NullConstantOperand(Type type) {
+            this.type = type;
+        }
+        @Override
+        public String toString() {
+            return "null";
+        }
+    }
+
     public static class RegisterOperand extends Operand {
         final Register reg;
         protected RegisterOperand(Register reg) {
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SparseConditionalConstantPropagation.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SparseConditionalConstantPropagation.java
index 73a04fc..c83ee31 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SparseConditionalConstantPropagation.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SparseConditionalConstantPropagation.java
@@ -377,35 +377,40 @@ private boolean evalInstruction(Instruction instruction) {
             }
             case Instruction.Binary binaryInst -> {
                 var cell = valueLattice.get(binaryInst.result().reg);
-                LatticeElement left, right;
+                LatticeElement left = null;
+                LatticeElement right = null;
+                // TODO we cannot yet evaluate null in comparisons
                 if (binaryInst.left() instanceof Operand.ConstantOperand constant)
                     left = new LatticeElement(V_CONSTANT, constant.value);
                 else if (binaryInst.left() instanceof Operand.RegisterOperand registerOperand)
                     left = valueLattice.get(registerOperand.reg);
-                else throw new IllegalStateException();
                 if (binaryInst.right() instanceof Operand.ConstantOperand constant)
                     right = new LatticeElement(V_CONSTANT, constant.value);
                 else if (binaryInst.right() instanceof Operand.RegisterOperand registerOperand)
                     right = valueLattice.get(registerOperand.reg);
-                else throw new IllegalStateException();
-                switch (binaryInst.binOp) {
-                    case "+":
-                    case "-":
-                    case "*":
-                    case "/":
-                    case "%":
-                        changed = evalArith(cell, left, right, binaryInst.binOp);
-                        break;
-                    case "==":
-                    case "!=":
-                    case "<":
-                    case ">":
-                    case "<=":
-                    case ">=":
-                        changed = evalLogical(cell, left, right, binaryInst.binOp);
-                        break;
-                    default:
-                        throw new IllegalStateException();
+                if (left != null && right != null) {
+                    switch (binaryInst.binOp) {
+                        case "+":
+                        case "-":
+                        case "*":
+                        case "/":
+                        case "%":
+                            changed = evalArith(cell, left, right, binaryInst.binOp);
+                            break;
+                        case "==":
+                        case "!=":
+                        case "<":
+                        case ">":
+                        case "<=":
+                        case ">=":
+                            changed = evalLogical(cell, left, right, binaryInst.binOp);
+                            break;
+                        default:
+                            throw new IllegalStateException();
+                    }
+                }
+                else {
+                    cell.setKind(V_VARYING);
                 }
             }
             case Instruction.NewArray newArrayInst -> {
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/interpreter/Interpreter.java b/optvm/src/main/java/com/compilerprogramming/ezlang/interpreter/Interpreter.java
index 6aec1b8..d59e1f0 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/interpreter/Interpreter.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/interpreter/Interpreter.java
@@ -48,6 +48,9 @@ public Value interpret(ExecutionStack execStack, Frame frame) {
                     if (retInst.value() instanceof Operand.ConstantOperand constantOperand) {
                         execStack.stack[base] = new Value.IntegerValue(constantOperand.value);
                     }
+                    else if (retInst.value() instanceof Operand.NullConstantOperand) {
+                        execStack.stack[base] = new Value.NullValue();
+                    }
                     else if (retInst.value() instanceof Operand.RegisterOperand registerOperand) {
                         execStack.stack[base] = execStack.stack[base+registerOperand.frameSlot()];
                     }
@@ -63,6 +66,9 @@ else if (retInst.value() instanceof Operand.RegisterOperand registerOperand) {
                         else if (moveInst.from() instanceof Operand.ConstantOperand constantOperand) {
                             execStack.stack[base + toReg.frameSlot()] = new Value.IntegerValue(constantOperand.value);
                         }
+                        else if (moveInst.from() instanceof Operand.NullConstantOperand) {
+                            execStack.stack[base + toReg.frameSlot()] = new Value.NullValue();
+                        }
                         else throw new IllegalStateException();
                     }
                     else throw new IllegalStateException();
@@ -107,6 +113,9 @@ else if (cbrInst.condition() instanceof Operand.ConstantOperand constantOperand)
                         else if (arg instanceof Operand.ConstantOperand constantOperand) {
                             execStack.stack[base + reg] = new Value.IntegerValue(constantOperand.value);
                         }
+                        else if (arg instanceof Operand.NullConstantOperand) {
+                            execStack.stack[base + reg] = new Value.NullValue();
+                        }
                         reg += 1;
                     }
                     // Call function
@@ -135,31 +144,60 @@ else if (arg instanceof Operand.ConstantOperand constantOperand) {
                 case Instruction.Binary binaryInst -> {
                     long x, y;
                     long value = 0;
-                    if (binaryInst.left() instanceof Operand.ConstantOperand constant)
-                        x = constant.value;
-                    else if (binaryInst.left() instanceof Operand.RegisterOperand registerOperand)
-                        x = ((Value.IntegerValue) execStack.stack[base + registerOperand.frameSlot()]).value;
-                    else throw new IllegalStateException();
-                    if (binaryInst.right() instanceof Operand.ConstantOperand constant)
-                        y = constant.value;
-                    else if (binaryInst.right() instanceof Operand.RegisterOperand registerOperand)
-                        y = ((Value.IntegerValue) execStack.stack[base + registerOperand.frameSlot()]).value;
-                    else throw new IllegalStateException();
-                    switch (binaryInst.binOp) {
-                        case "+": value = x + y; break;
-                        case "-": value = x - y; break;
-                        case "*": value = x * y; break;
-                        case "/": value = x / y; break;
-                        case "%": value = x % y; break;
-                        case "==": value = x == y ? 1 : 0; break;
-                        case "!=": value = x != y ? 1 : 0; break;
-                        case "<": value = x < y ? 1: 0; break;
-                        case ">": value = x > y ? 1 : 0; break;
-                        case "<=": value = x <= y ? 1 : 0; break;
-                        case ">=": value = x <= y ? 1 : 0; break;
-                        default: throw new IllegalStateException();
+                    boolean intOp = true;
+                    if (binaryInst.binOp.equals("==") || binaryInst.binOp.equals("!=")) {
+                        Operand.RegisterOperand nonNullLitOperand = null;
+                        if (binaryInst.left() instanceof Operand.NullConstantOperand) {
+                            nonNullLitOperand = (Operand.RegisterOperand)binaryInst.right();
+                        }
+                        else if (binaryInst.right() instanceof Operand.NullConstantOperand) {
+                            nonNullLitOperand = (Operand.RegisterOperand)binaryInst.left();
+                        }
+                        if (nonNullLitOperand != null) {
+                            intOp = false;
+                            Value otherValue = execStack.stack[base + nonNullLitOperand.frameSlot()];
+                            switch (binaryInst.binOp) {
+                                case "==": {
+                                    value = otherValue instanceof Value.NullValue ? 1 : 0;
+                                    break;
+                                }
+                                case "!=": {
+                                    value = otherValue instanceof Value.NullValue ? 0 : 1;
+                                    break;
+                                }
+                                default:
+                                    throw new IllegalStateException();
+                            }
+                            execStack.stack[base + binaryInst.result().frameSlot()] = new Value.IntegerValue(value);
+                        }
+                    }
+                    if (intOp) {
+                        if (binaryInst.left() instanceof Operand.ConstantOperand constant)
+                            x = constant.value;
+                        else if (binaryInst.left() instanceof Operand.RegisterOperand registerOperand)
+                            x = ((Value.IntegerValue) execStack.stack[base + registerOperand.frameSlot()]).value;
+                        else throw new IllegalStateException();
+                        if (binaryInst.right() instanceof Operand.ConstantOperand constant)
+                            y = constant.value;
+                        else if (binaryInst.right() instanceof Operand.RegisterOperand registerOperand)
+                            y = ((Value.IntegerValue) execStack.stack[base + registerOperand.frameSlot()]).value;
+                        else throw new IllegalStateException();
+                        switch (binaryInst.binOp) {
+                            case "+": value = x + y; break;
+                            case "-": value = x - y; break;
+                            case "*": value = x * y; break;
+                            case "/": value = x / y; break;
+                            case "%": value = x % y; break;
+                            case "==": value = x == y ? 1 : 0; break;
+                            case "!=": value = x != y ? 1 : 0; break;
+                            case "<": value = x < y ? 1: 0; break;
+                            case ">": value = x > y ? 1 : 0; break;
+                            case "<=": value = x <= y ? 1 : 0; break;
+                            case ">=": value = x <= y ? 1 : 0; break;
+                            default: throw new IllegalStateException();
+                        }
+                        execStack.stack[base + binaryInst.result().frameSlot()] = new Value.IntegerValue(value);
                     }
-                    execStack.stack[base + binaryInst.result().frameSlot()] = new Value.IntegerValue(value);
                 }
                 case Instruction.NewArray newArrayInst -> {
                     execStack.stack[base + newArrayInst.destOperand().frameSlot()] = new Value.ArrayValue(newArrayInst.type);
@@ -172,6 +210,9 @@ else if (binaryInst.right() instanceof Operand.RegisterOperand registerOperand)
                     if (arrayAppendInst.value() instanceof Operand.ConstantOperand constant) {
                         arrayValue.values.add(new Value.IntegerValue(constant.value));
                     }
+                    else if (arrayAppendInst.value() instanceof Operand.NullConstantOperand) {
+                        arrayValue.values.add(new Value.NullValue());
+                    }
                     else if (arrayAppendInst.value() instanceof Operand.RegisterOperand registerOperand) {
                         arrayValue.values.add(execStack.stack[base + registerOperand.frameSlot()]);
                     }
@@ -193,6 +234,9 @@ else if (arrayStoreInst.indexOperand() instanceof Operand.RegisterOperand regist
                         if (arrayStoreInst.sourceOperand() instanceof Operand.ConstantOperand constantOperand) {
                             value = new Value.IntegerValue(constantOperand.value);
                         }
+                        else if (arrayStoreInst.sourceOperand() instanceof Operand.NullConstantOperand) {
+                            value = new Value.NullValue();
+                        }
                         else if (arrayStoreInst.sourceOperand() instanceof Operand.RegisterOperand registerOperand) {
                             value = execStack.stack[base + registerOperand.frameSlot()];
                         }
@@ -221,6 +265,9 @@ else if (arrayLoadInst.indexOperand() instanceof Operand.RegisterOperand registe
                         if (setFieldInst.sourceOperand() instanceof Operand.ConstantOperand constant) {
                             value = new Value.IntegerValue(constant.value);
                         }
+                        else if (setFieldInst.sourceOperand() instanceof Operand.NullConstantOperand) {
+                            value = new Value.NullValue();
+                        }
                         else if (setFieldInst.sourceOperand() instanceof Operand.RegisterOperand registerOperand) {
                             value = execStack.stack[base + registerOperand.frameSlot()];
                         }
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/interpreter/Value.java b/optvm/src/main/java/com/compilerprogramming/ezlang/interpreter/Value.java
index e03fb80..7f914a6 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/interpreter/Value.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/interpreter/Value.java
@@ -11,6 +11,9 @@ public IntegerValue(long value) {
         }
         public final long value;
     }
+    static public class NullValue extends Value {
+        public NullValue() {}
+    }
     static public class ArrayValue extends Value {
         public final Type.TypeArray arrayType;
         public final ArrayList<Value> values;
diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
index 3f8a052..1c4fc5b 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
@@ -2525,6 +2525,100 @@ func foo()->Int
     %t0_0 = 0
     %t0_2 = %t0_0
     goto  L4
+""", result);
+    }
+
+    @Test
+    public void testSSA15() {
+        String src = """
+                struct Foo
+                {
+                    var i: Int
+                }
+                func foo()->Int
+                {
+                    var f = new [Foo?] { new Foo{i = 1}, null }
+                    return null == f[1] && 1 == f[0].i
+                }
+""";
+        String result = compileSrc(src);
+        Assert.assertEquals("""
+func foo
+Before SSA
+==========
+L0:
+    %t1 = New([Foo?])
+    %t2 = New(Foo)
+    %t2.i = 1
+    %t1.append(%t2)
+    %t1.append(null)
+    f = %t1
+    %t3 = f[1]
+    %t4 = null==%t3
+    if %t4 goto L2 else goto L3
+L2:
+    %t5 = f[0]
+    %t6 = %t5.i
+    %t7 = 1==%t6
+    goto  L4
+L4:
+    ret %t7
+    goto  L1
+L1:
+L3:
+    %t7 = 0
+    goto  L4
+After SSA
+=========
+L0:
+    %t1_0 = New([Foo?])
+    %t2_0 = New(Foo)
+    %t2_0.i = 1
+    %t1_0.append(%t2_0)
+    %t1_0.append(null)
+    f_0 = %t1_0
+    %t3_0 = f_0[1]
+    %t4_0 = null==%t3_0
+    if %t4_0 goto L2 else goto L3
+L2:
+    %t5_0 = f_0[0]
+    %t6_0 = %t5_0.i
+    %t7_1 = 1==%t6_0
+    goto  L4
+L4:
+    %t7_2 = phi(%t7_1, %t7_0)
+    ret %t7_2
+    goto  L1
+L1:
+L3:
+    %t7_0 = 0
+    goto  L4
+After exiting SSA
+=================
+L0:
+    %t1_0 = New([Foo?])
+    %t2_0 = New(Foo)
+    %t2_0.i = 1
+    %t1_0.append(%t2_0)
+    %t1_0.append(null)
+    f_0 = %t1_0
+    %t3_0 = f_0[1]
+    %t4_0 = null==%t3_0
+    if %t4_0 goto L2 else goto L3
+L2:
+    %t5_0 = f_0[0]
+    %t6_0 = %t5_0.i
+    %t7_1 = 1==%t6_0
+    %t7_2 = %t7_1
+    goto  L4
+L4:
+    ret %t7_2
+    goto  L1
+L1:
+L3:
+    %t7_0 = 0
+    %t7_2 = %t7_0
+    goto  L4
 """, result);
     }
 }
\ No newline at end of file
diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java b/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java
index e463a29..4f8c0f0 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java
@@ -446,78 +446,78 @@ func foo()->Int {
     }
 
 
-//    @Test
-//    public void testFunction100() {
-//        String src = """
-//                struct Test
-//                {
-//                    var field: Int
-//                }
-//                func foo()->Test?
-//                {
-//                    return null;
-//                }
-//
-//                """;
-//        var value = compileAndRun(src, "foo", Options.OPT);
-//        Assert.assertNotNull(value);
-//        Assert.assertTrue(value instanceof Value.NullValue);
-//    }
-//
-//    @Test
-//    public void testFunction101() {
-//        String src = """
-//                func foo()->Int
-//                {
-//                    return null == null;
-//                }
-//
-//                """;
-//        var value = compileAndRun(src, "foo", Options.OPT);
-//        Assert.assertNotNull(value);
-//        Assert.assertTrue(value instanceof Value.IntegerValue integerValue &&
-//                integerValue.value == 1);
-//    }
-//
-//    @Test
-//    public void testFunction102() {
-//        String src = """
-//                struct Foo
-//                {
-//                    var next: Foo?
-//                }
-//                func foo()->Int
-//                {
-//                    var f = new Foo{ next = null }
-//                    return null == f.next
-//                }
-//
-//                """;
-//        var value = compileAndRun(src, "foo", Options.OPT);
-//        Assert.assertNotNull(value);
-//        Assert.assertTrue(value instanceof Value.IntegerValue integerValue &&
-//                integerValue.value == 1);
-//    }
-//
-//    @Test
-//    public void testFunction103() {
-//        String src = """
-//                struct Foo
-//                {
-//                    var i: Int
-//                }
-//                func foo()->Int
-//                {
-//                    var f = new [Foo?] { new Foo{i = 1}, null }
-//                    return null == f[1] && 1 == f[0].i
-//                }
-//
-//                """;
-//        var value = compileAndRun(src, "foo", Options.OPT);
-//        Assert.assertNotNull(value);
-//        Assert.assertTrue(value instanceof Value.IntegerValue integerValue &&
-//                integerValue.value == 1);
-//    }
+    @Test
+    public void testFunction100() {
+        String src = """
+                struct Test
+                {
+                    var field: Int
+                }
+                func foo()->Test?
+                {
+                    return null;
+                }
+
+                """;
+        var value = compileAndRun(src, "foo", Options.OPT);
+        Assert.assertNotNull(value);
+        Assert.assertTrue(value instanceof Value.NullValue);
+    }
+
+    @Test
+    public void testFunction101() {
+        String src = """
+                func foo()->Int
+                {
+                    return null == null;
+                }
+
+                """;
+        var value = compileAndRun(src, "foo", Options.OPT);
+        Assert.assertNotNull(value);
+        Assert.assertTrue(value instanceof Value.IntegerValue integerValue &&
+                integerValue.value == 1);
+    }
+
+    @Test
+    public void testFunction102() {
+        String src = """
+                struct Foo
+                {
+                    var next: Foo?
+                }
+                func foo()->Int
+                {
+                    var f = new Foo{ next = null }
+                    return null == f.next
+                }
+
+                """;
+        var value = compileAndRun(src, "foo", Options.OPT);
+        Assert.assertNotNull(value);
+        Assert.assertTrue(value instanceof Value.IntegerValue integerValue &&
+                integerValue.value == 1);
+    }
+
+    @Test
+    public void testFunction103() {
+        String src = """
+                struct Foo
+                {
+                    var i: Int
+                }
+                func foo()->Int
+                {
+                    var f = new [Foo?] { new Foo{i = 1}, null }
+                    return null == f[1] && 1 == f[0].i
+                }
+
+                """;
+        var value = compileAndRun(src, "foo", Options.OPT);
+        Assert.assertNotNull(value);
+        Assert.assertTrue(value instanceof Value.IntegerValue integerValue &&
+                integerValue.value == 1);
+    }
 
     @Test
     public void testFunction104() {

From edc687bfa8e1b6989267b35045112065ed7e68d7 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <dibyendu@redukti.com>
Date: Fri, 21 Feb 2025 15:22:10 +0000
Subject: [PATCH 038/119] Add test for simple SSA liveness example, also add
 comments and reorg the liveness calculation a bit for clarity

---
 .../ezlang/compiler/BasicBlock.java           |   5 +-
 .../ezlang/compiler/Liveness.java             |  59 ++++++---
 .../ezlang/compiler/TestLiveness.java         | 116 ++++++++++++++++++
 3 files changed, 164 insertions(+), 16 deletions(-)

diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/BasicBlock.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/BasicBlock.java
index 80c8193..a57fc2b 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/BasicBlock.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/BasicBlock.java
@@ -67,11 +67,12 @@ public class BasicBlock {
     LiveSet liveOut;
 
     /**
-     * Inputs to successor block's phi function
+     * phiUses(B) is the set of variables used in a phi-operation at entry of a block successor of the block B
+     * That is, inputs to successor block's phi functions.
      */
     LiveSet phiUses;
     /**
-     * Phi definitions in this block
+     * phiDefs(B) the variables defined by phi-operations at entry of the block B
      */
     LiveSet phiDefs;
     /**
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Liveness.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Liveness.java
index f5b2802..4eb56a3 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Liveness.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Liveness.java
@@ -9,11 +9,15 @@
  * pages 446-447.
  *
  * It turned out that this dataflow implementation cannot correctly handle
- * phis, because with phis, the inputs are live at the predecessor blocks.
- * We have to look at alternative approaches when input is SSA form.
- * Surprisingly even with this approach, the lost copy and swap problems
+ * phis. Therefore, we have to look at alternative approaches when input is SSA form.
+ * Surprisingly even with the flawed approach, the lost copy and swap problems
  * appeared to work correctly.
  *
+ * Phis need special considerations:
+ *
+ * Phi inputs are live out at the predecessor blocks, but not live in for phi block.
+ * Phi def is live in at phi block but not in live out at predecessor blocks.
+ *
  * The new approach is based on formula described in
  * Computing Liveness Sets for SSA-Form Programs
  * Florian Brandner, Benoit Boissinot, Alain Darte, Benoît Dupont de Dinechin, Fabrice Rastello
@@ -48,25 +52,29 @@ private void initBlocks(RegisterPool regPool, List<BasicBlock> blocks) {
 
     private void init(List<BasicBlock> blocks) {
         for (BasicBlock block : blocks) {
-            // We st up phiDefs first because when we
+            // Any vars created by phi instructions are added to phiDefs
+            // for this block; these vars will be live on entry to block
+            // but not live out from predecessor blocks.
+            //
+            // We set up phiDefs first because when we
             // look at phi uses we need to refer back here
             // see comments on phi cycles below
             for (Instruction instruction : block.instructions) {
                 if (instruction instanceof Instruction.Phi phi) {
                     block.phiDefs.add(phi.value());
                 }
+                // There is a scenario where other instructions can appear
+                // between phi instructions - this happens during the SSA deconstruction
+                // using Brigg's method. But we don't calculate liveness
+                // in the middle of that process, so assuming all phis are together
+                // at the top of the block is okay
                 else break;
             }
             for (Instruction instruction : block.instructions) {
-                for (Register use : instruction.uses()) {
-                    if (!block.varKill.contains(use))
-                        block.UEVar.add(use);
-                }
-                if (instruction.definesVar() && !(instruction instanceof Instruction.Phi)) {
-                    Register def = instruction.def();
-                    block.varKill.add(def);
-                }
                 if (instruction instanceof Instruction.Phi phi) {
+                    // Any uses in a Phi are added to the phiUses of predecessor
+                    // block. These uses will be in live out of predecessor block but
+                    // not live in for current block.
                     for (int i = 0; i < block.predecessors.size(); i++) {
                         BasicBlock pred = block.predecessors.get(i);
                         if (!phi.isRegisterInput(i))
@@ -76,11 +84,24 @@ private void init(List<BasicBlock> blocks) {
                         // if there is loop back and there are cycles
                         // such as e.g. the swap copy problem
                         if (pred == block &&
-                            block.phiDefs.contains(use))
+                                block.phiDefs.contains(use))
                             continue;
                         pred.phiUses.add(use);
                     }
                 }
+                else {
+                    // Non phi instructions follow regular
+                    // logic. Any var that is used before being defined
+                    // is added to upward expose set.
+                    for (Register use : instruction.uses()) {
+                        if (!block.varKill.contains(use))
+                            block.UEVar.add(use);
+                    }
+                    if (instruction.definesVar()) {
+                        Register def = instruction.def();
+                        block.varKill.add(def);
+                    }
+                }
             }
         }
     }
@@ -89,6 +110,7 @@ private void computeLiveness(List<BasicBlock> blocks) {
         boolean changed = true;
         while (changed) {
             changed = false;
+            // TODO we should process in RPO order
             for (BasicBlock block : blocks) {
                 if (recomputeLiveOut(block))
                     changed = true;
@@ -97,8 +119,17 @@ private void computeLiveness(List<BasicBlock> blocks) {
     }
 
     // See 'Computing Liveness Sets for SSA-Form Programs'
+    //
     // LiveIn(B) = PhiDefs(B) U UpwardExposed(B) U (LiveOut(B) \ Defs(B))
-    // LiveOut(B) = U all S  (LiveIn(S) \ PhiDefs(S)) U PhiUses(B)
+    // LiveOut(B) = U all S  (LiveIn(S) \ PhiDefs(S)) U PhiUses
+    //
+    // For a phi-function a_0 = phi(a_1, ..., a_n ) in block B_0, where a_i comes from block B_i, then:
+    // * a_0 is considered to be live-in for B_0, but, with respect to this phi-function, it is
+    //   not live-out for B_i, i > 0.
+    // * a_i, i > 0, is considered to be live-out of B_i , but, with respect to this phi-function,
+    //   it is not live-in for B_0.
+    // This corresponds to placing a copy of a_i to a_0 on each edge from B_i to B_0.
+    //
     private boolean recomputeLiveOut(BasicBlock block) {
         LiveSet oldLiveOut = block.liveOut.dup();
         LiveSet t = block.liveOut.dup().subtract(block.varKill);
diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestLiveness.java b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestLiveness.java
index c9c2d27..8e632d7 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestLiveness.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestLiveness.java
@@ -444,4 +444,120 @@ func foo(p: Int)->Int
 """, actual);
     }
 
+    @Test
+    public void testSimpleCase() {
+        String src = """
+                func foo()->Int 
+                {
+                    return 1 && 2
+                }
+                """;
+        var typeDict = compileSrc(src);
+        var funcSymbol = typeDict.lookup("foo");
+        CompiledFunction func = (CompiledFunction) ((Symbol.FunctionTypeSymbol) funcSymbol).code();
+        func.livenessAnalysis();
+        StringBuilder result = new StringBuilder();
+        result.append("Pre-SSA\n");
+        func.toStr(result, true);
+        new EnterSSA(func, Options.NONE);
+        func.livenessAnalysis();
+        result.append("Post-SSA\n");
+        func.toStr(result, true);
+        Assert.assertEquals("""
+Pre-SSA
+func foo()->Int
+Reg #0 %t0
+L0:
+    if 1 goto L2 else goto L3
+    #PHIDEFS = {}
+    #PHIUSES = {}
+    #UEVAR   = {}
+    #VARKILL = {}
+    #LIVEIN  = {}
+    #LIVEOUT = {}
+L2:
+    %t0 = 2
+    goto  L4
+    #PHIDEFS = {}
+    #PHIUSES = {}
+    #UEVAR   = {}
+    #VARKILL = {0}
+    #LIVEIN  = {}
+    #LIVEOUT = {0}
+L4:
+    ret %t0
+    goto  L1
+    #PHIDEFS = {}
+    #PHIUSES = {}
+    #UEVAR   = {0}
+    #VARKILL = {}
+    #LIVEIN  = {0}
+    #LIVEOUT = {}
+L1:
+    #PHIDEFS = {}
+    #PHIUSES = {}
+    #UEVAR   = {}
+    #VARKILL = {}
+    #LIVEIN  = {}
+    #LIVEOUT = {}
+L3:
+    %t0 = 0
+    goto  L4
+    #PHIDEFS = {}
+    #PHIUSES = {}
+    #UEVAR   = {}
+    #VARKILL = {0}
+    #LIVEIN  = {}
+    #LIVEOUT = {0}
+Post-SSA
+func foo()->Int
+Reg #0 %t0
+Reg #1 %t0_0
+Reg #2 %t0_1
+Reg #3 %t0_2
+L0:
+    if 1 goto L2 else goto L3
+    #PHIDEFS = {}
+    #PHIUSES = {}
+    #UEVAR   = {}
+    #VARKILL = {}
+    #LIVEIN  = {}
+    #LIVEOUT = {}
+L2:
+    %t0_1 = 2
+    goto  L4
+    #PHIDEFS = {}
+    #PHIUSES = {2}
+    #UEVAR   = {}
+    #VARKILL = {2}
+    #LIVEIN  = {}
+    #LIVEOUT = {2}
+L4:
+    %t0_2 = phi(%t0_1, %t0_0)
+    ret %t0_2
+    goto  L1
+    #PHIDEFS = {3}
+    #PHIUSES = {}
+    #UEVAR   = {3}
+    #VARKILL = {}
+    #LIVEIN  = {3}
+    #LIVEOUT = {}
+L1:
+    #PHIDEFS = {}
+    #PHIUSES = {}
+    #UEVAR   = {}
+    #VARKILL = {}
+    #LIVEIN  = {}
+    #LIVEOUT = {}
+L3:
+    %t0_0 = 0
+    goto  L4
+    #PHIDEFS = {}
+    #PHIUSES = {1}
+    #UEVAR   = {}
+    #VARKILL = {1}
+    #LIVEIN  = {}
+    #LIVEOUT = {1}
+""", result.toString());
+    }
 }

From 16e7a7973657e8f0d43123bd5accefc9fbcb9a86 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <dibyendu@redukti.com>
Date: Sat, 22 Feb 2025 12:54:53 +0000
Subject: [PATCH 039/119] Small refactor to help with the incremental SSA diff

---
 .../ezlang/compiler/CompiledFunction.java     | 34 +++++++++----------
 1 file changed, 17 insertions(+), 17 deletions(-)

diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
index 06c42b5..2eaf92b 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
@@ -393,23 +393,6 @@ private boolean compileSetFieldExpr(AST.SetFieldExpr setFieldExpr) {
         return false;
     }
 
-    private void codeNew(Type type) {
-        var temp = createTemp(type);
-        if (type instanceof Type.TypeArray typeArray) {
-            code(new Instruction.NewArray(typeArray, temp));
-        }
-        else if (type instanceof Type.TypeStruct typeStruct) {
-            code(new Instruction.NewStruct(typeStruct, temp));
-        }
-        else
-            throw new CompilerException("Unexpected type: " + type);
-    }
-
-    private void codeStoreAppend() {
-        var operand = pop();
-        code(new Instruction.AStoreAppend((Operand.RegisterOperand) top(), operand));
-    }
-
     private boolean compileNewExpr(AST.NewExpr newExpr) {
         codeNew(newExpr.type);
         if (newExpr.initExprList != null && !newExpr.initExprList.isEmpty()) {
@@ -636,6 +619,23 @@ else if (indexed instanceof Operand.LoadFieldOperand loadFieldOperand) {
             code(new Instruction.Move(value, indexed));
     }
 
+    private void codeNew(Type type) {
+        var temp = createTemp(type);
+        if (type instanceof Type.TypeArray typeArray) {
+            code(new Instruction.NewArray(typeArray, temp));
+        }
+        else if (type instanceof Type.TypeStruct typeStruct) {
+            code(new Instruction.NewStruct(typeStruct, temp));
+        }
+        else
+            throw new CompilerException("Unexpected type: " + type);
+    }
+
+    private void codeStoreAppend() {
+        var operand = pop();
+        code(new Instruction.AStoreAppend((Operand.RegisterOperand) top(), operand));
+    }
+
     private boolean vstackEmpty() {
         return virtualStack.isEmpty();
     }

From 02edd460c48424f4a3983b202dd1c488ae8d41a6 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <dibyendu@redukti.com>
Date: Sat, 22 Feb 2025 01:18:02 +0000
Subject: [PATCH 040/119] WIP implement incremental SSA construction using
 Braun's method

WIP implement incremental SSA construction using Braun's method

WIP implement incremental SSA construction using Braun's method

WIP implement incremental SSA construction using Braun's method

WIP implement incremental SSA construction using Braun's method

Small refactor to help with the incremental SSA diff

Add option to control ISSA

Add missing use replacement in Phis
Some other cleanup and comments

Bug fix

Some prep work on enabling more tests

Consider temp registers too as part of SSA because in boolean expressions, a temp is assigned value more than once so although generally temps are SSA already, in this case they are not.

phi vars must also be SSA values
When temps are used for creating arrays and structs ensure they get processed in the same way as all temps.

Delete some unused code

bug fixes
Have to be seal BBs as soon as possible - will need to review this carefully

WIP pending issue with while stmt body - if we seal it at the start we have an issue

Dump out the non SSA ID with the register pool data so that we can see what slots were assigned

Seal the body block immediately after the condition in while stmt

Interpreter was using wrong regnum for SSA vars - this was okay until now because luckily the SSA vars did not survive, but with incremental SSA implementation, the bug became evident
---
 .../ezlang/compiler/BasicBlock.java           |   8 +-
 .../ezlang/compiler/CompiledFunction.java     | 553 ++++++++++++++++--
 .../ezlang/compiler/Compiler.java             |   2 +-
 .../ezlang/compiler/Instruction.java          |  18 +-
 .../ezlang/compiler/Operand.java              |   9 +-
 .../ezlang/compiler/Optimizer.java            |   3 +-
 .../ezlang/compiler/Options.java              |   4 +-
 .../ezlang/compiler/Register.java             |   4 +
 .../ezlang/compiler/RegisterPool.java         |   2 +-
 .../ezlang/compiler/SSAEdges.java             |  10 +-
 .../ezlang/compiler/TestIncrementalSSA.java   | 132 +++++
 .../compiler/TestInterferenceGraph.java       |   7 +-
 .../ezlang/compiler/TestLiveness.java         |  66 ++-
 .../ezlang/compiler/TestSSATransform.java     |  14 +-
 .../ezlang/interpreter/TestInterpreter.java   |   1 +
 15 files changed, 724 insertions(+), 109 deletions(-)
 create mode 100644 optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestIncrementalSSA.java

diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/BasicBlock.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/BasicBlock.java
index a57fc2b..ccb8b09 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/BasicBlock.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/BasicBlock.java
@@ -111,10 +111,10 @@ public void deleteInstruction(Instruction instruction) {
         instructions.remove(instruction);
     }
     public void addSuccessor(BasicBlock successor) {
-        assert successors.contains(successor) == false;
-        successors.add(successor);
-        assert successor.predecessors.contains(this) == false;
-        successor.predecessors.add(this);
+        if (!successors.contains(successor)) {
+            successors.add(successor);
+            successor.predecessors.add(this);
+        }
     }
     public void removeSuccessor(BasicBlock successor) {
         successors.remove(successor);
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
index 2eaf92b..0d577d2 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
@@ -7,9 +7,7 @@
 import com.compilerprogramming.ezlang.types.Type;
 import com.compilerprogramming.ezlang.types.TypeDictionary;
 
-import java.util.ArrayList;
-import java.util.BitSet;
-import java.util.List;
+import java.util.*;
 
 public class CompiledFunction {
 
@@ -27,6 +25,7 @@ public class CompiledFunction {
 
     public boolean isSSA;
     public boolean hasLiveness;
+    private final IncrementalSSA issa;
 
     /**
      * We essentially do a form of abstract interpretation as we generate
@@ -38,10 +37,11 @@ public class CompiledFunction {
      */
     private List<Operand> virtualStack = new ArrayList<>();
 
-    public CompiledFunction(Symbol.FunctionTypeSymbol functionSymbol, TypeDictionary typeDictionary) {
+    public CompiledFunction(Symbol.FunctionTypeSymbol functionSymbol, TypeDictionary typeDictionary, EnumSet<Options> options) {
         AST.FuncDecl funcDecl = (AST.FuncDecl) functionSymbol.functionDecl;
         this.functionType = (Type.TypeFunction) functionSymbol.type;
         this.registerPool = new RegisterPool();
+        this.issa = (options != null && options.contains(Options.ISSA)) ? new IncrementalSSABraun(this) : new NoopIncrementalSSA();
         setVirtualRegisters(funcDecl.scope);
         this.BID = 0;
         this.entry = this.currentBlock = createBlock();
@@ -52,18 +52,22 @@ public CompiledFunction(Symbol.FunctionTypeSymbol functionSymbol, TypeDictionary
         generateArgInstructions(funcDecl.scope);
         compileStatement(funcDecl.block);
         exitBlockIfNeeded();
+        issa.finish(options);
         this.frameSlots = registerPool.numRegisters();
     }
-
+    public CompiledFunction(Symbol.FunctionTypeSymbol functionSymbol, TypeDictionary typeDictionary) {
+        this(functionSymbol,typeDictionary,null);
+    }
     public CompiledFunction(Type.TypeFunction functionType, TypeDictionary typeDictionary) {
         this.functionType = (Type.TypeFunction) functionType;
         this.registerPool = new RegisterPool();
-        this.BID = 0;
+        this.issa = new NoopIncrementalSSA();        this.BID = 0;
         this.entry = this.currentBlock = createBlock();
         this.exit = createBlock();
         this.currentBreakTarget = null;
         this.currentContinueTarget = null;
         this.typeDictionary = typeDictionary;
+        issa.finish(null);
         this.frameSlots = registerPool.numRegisters();
     }
 
@@ -71,7 +75,7 @@ private void generateArgInstructions(Scope scope) {
         if (scope.isFunctionParameterScope) {
             for (Symbol symbol: scope.getLocalSymbols()) {
                 if (symbol instanceof Symbol.ParameterSymbol parameterSymbol) {
-                    code(new Instruction.ArgInstruction(new Operand.LocalRegisterOperand(registerPool.getReg(parameterSymbol.regNumber))));
+                    codeArg(new Operand.LocalRegisterOperand(registerPool.getReg(parameterSymbol.regNumber), parameterSymbol));
                 }
             }
         }
@@ -122,7 +126,7 @@ private void compileReturn(AST.ReturnStmt returnStmt) {
             if (isIndexed)
                 codeIndexedLoad();
             if (virtualStack.size() == 1)
-                code(new Instruction.Ret(pop()));
+                codeReturn(pop());
             else if (virtualStack.size() > 1)
                 throw new CompilerException("Virtual stack has more than one item at return");
         }
@@ -179,7 +183,7 @@ private void compileAssign(AST.AssignStmt assignStmt) {
             codeIndexedStore();
         else if (assignStmt.lhs instanceof AST.NameExpr symbolExpr) {
             Symbol.VarSymbol varSymbol = (Symbol.VarSymbol) symbolExpr.symbol;
-            code(new Instruction.Move(pop(), new Operand.LocalRegisterOperand(registerPool.getReg(varSymbol.regNumber))));
+            codeMove(pop(), new Operand.LocalRegisterOperand(registerPool.getReg(varSymbol.regNumber), varSymbol));
         }
         else
             throw new CompilerException("Invalid assignment expression: " + assignStmt.lhs);
@@ -196,34 +200,37 @@ private void compileExprStmt(AST.ExprStmt exprStmt) {
     private void compileContinue(AST.ContinueStmt continueStmt) {
         if (currentContinueTarget == null)
             throw new CompilerException("No continue target found");
+        assert !issa.isSealed(currentContinueTarget);
         jumpTo(currentContinueTarget);
     }
 
     private void compileBreak(AST.BreakStmt breakStmt) {
         if (currentBreakTarget == null)
             throw new CompilerException("No break target found");
+        assert !issa.isSealed(currentBreakTarget);
         jumpTo(currentBreakTarget);
     }
 
     private void compileWhile(AST.WhileStmt whileStmt) {
-        BasicBlock loopBlock = createLoopHead();
+        BasicBlock loopHead = createLoopHead();
         BasicBlock bodyBlock = createBlock();
         BasicBlock exitBlock = createBlock();
         BasicBlock savedBreakTarget = currentBreakTarget;
         BasicBlock savedContinueTarget = currentContinueTarget;
         currentBreakTarget = exitBlock;
-        currentContinueTarget = loopBlock;
-        startBlock(loopBlock);
+        currentContinueTarget = loopHead;
+        startBlock(loopHead);   // ISSA cannot seal until all back edges done
         boolean indexed = compileExpr(whileStmt.condition);
         if (indexed)
             codeIndexedLoad();
-        code(new Instruction.ConditionalBranch(currentBlock, pop(), bodyBlock, exitBlock));
+        codeCBR(currentBlock, pop(), bodyBlock, exitBlock);
         assert vstackEmpty();
-        startBlock(bodyBlock);
+        startSealedBlock(bodyBlock);  // ISSA If we seal this here fib test fails, wrong code generated, why?
         compileStatement(whileStmt.stmt);
         if (!isBlockTerminated(currentBlock))
-            jumpTo(loopBlock);
-        startBlock(exitBlock);
+            jumpTo(loopHead);
+        issa.sealBlock(loopHead);
+        startSealedBlock(exitBlock);
         currentContinueTarget = savedContinueTarget;
         currentBreakTarget = savedBreakTarget;
     }
@@ -235,6 +242,7 @@ private boolean isBlockTerminated(BasicBlock block) {
 
     public void jumpTo(BasicBlock block) {
         assert !isBlockTerminated(currentBlock);
+        assert !issa.isSealed(block);
         currentBlock.add(new Instruction.Jump(block));
         currentBlock.addSuccessor(block);
     }
@@ -245,28 +253,32 @@ public void startBlock(BasicBlock block) {
         }
         currentBlock = block;
     }
-
+    public void startSealedBlock(BasicBlock block) {
+        startBlock(block);
+        assert !issa.isSealed(currentBlock);
+        issa.sealBlock(currentBlock);
+    }
     private void compileIf(AST.IfElseStmt ifElseStmt) {
-        BasicBlock ifBlock = createBlock();
+        BasicBlock thenBlock = createBlock();
         boolean needElse = ifElseStmt.elseStmt != null;
         BasicBlock elseBlock = needElse ? createBlock() : null;
         BasicBlock exitBlock = createBlock();
         boolean indexed = compileExpr(ifElseStmt.condition);
         if (indexed)
             codeIndexedLoad();
-        code(new Instruction.ConditionalBranch(currentBlock, pop(), ifBlock, needElse ? elseBlock : exitBlock));
+        codeCBR(currentBlock, pop(), thenBlock, needElse ? elseBlock : exitBlock);
         assert vstackEmpty();
-        startBlock(ifBlock);
+        startSealedBlock(thenBlock);        // ISSA seal immediately
         compileStatement(ifElseStmt.ifStmt);
         if (!isBlockTerminated(currentBlock))
             jumpTo(exitBlock);
         if (elseBlock != null) {
-            startBlock(elseBlock);
+            startSealedBlock(elseBlock);    // ISSA seal immediately
             compileStatement(ifElseStmt.elseStmt);
             if (!isBlockTerminated(currentBlock))
                 jumpTo(exitBlock);
         }
-        startBlock(exitBlock);
+        startSealedBlock(exitBlock);        // ISSA seal immediately
     }
 
     private void compileLet(AST.VarStmt letStmt) {
@@ -274,7 +286,7 @@ private void compileLet(AST.VarStmt letStmt) {
             boolean indexed = compileExpr(letStmt.expr);
             if (indexed)
                 codeIndexedLoad();
-            code(new Instruction.Move(pop(), new Operand.LocalRegisterOperand(registerPool.getReg(letStmt.symbol.regNumber))));
+            codeMove(pop(), new Operand.LocalRegisterOperand(registerPool.getReg(letStmt.symbol.regNumber), letStmt.symbol));
         }
     }
 
@@ -328,7 +340,7 @@ private boolean compileCallExpr(AST.CallExpr callExpr) {
             if (!(arg instanceof Operand.TempRegisterOperand) ) {
                 var origArg = pop();
                 arg = createTemp(origArg.type);
-                code(new Instruction.Move(origArg, arg));
+                codeMove(origArg, arg);
             }
             args.add((Operand.RegisterOperand) arg);
         }
@@ -339,9 +351,8 @@ private boolean compileCallExpr(AST.CallExpr callExpr) {
         if (callExpr.callee.type instanceof Type.TypeFunction tf &&
                 !(tf.returnType instanceof Type.TypeVoid)) {
             ret = createTemp(tf.returnType);
-            //assert ret.regnum-maxLocalReg == returnStackPos;
         }
-        code(new Instruction.Call(returnStackPos, ret, calleeType, args.toArray(new Operand.RegisterOperand[args.size()])));
+        codeCall(returnStackPos, ret, calleeType, args.toArray(new Operand.RegisterOperand[args.size()]));
         return false;
     }
 
@@ -419,7 +430,7 @@ private boolean compileSymbolExpr(AST.NameExpr symbolExpr) {
             pushOperand(new Operand.LocalFunctionOperand(functionType));
         else {
             Symbol.VarSymbol varSymbol = (Symbol.VarSymbol) symbolExpr.symbol;
-            pushLocal(registerPool.getReg(varSymbol.regNumber));
+            pushLocal(registerPool.getReg(varSymbol.regNumber), varSymbol);
         }
         return false;
     }
@@ -433,28 +444,27 @@ private boolean codeBoolean(AST.BinaryExpr binaryExpr) {
         if (indexed)
             codeIndexedLoad();
         if (isAnd) {
-            code(new Instruction.ConditionalBranch(currentBlock, pop(), l1, l2));
+            codeCBR(currentBlock, pop(), l1, l2);
         } else {
-            code(new Instruction.ConditionalBranch(currentBlock, pop(), l2, l1));
+            codeCBR(currentBlock, pop(), l2, l1);
         }
-        startBlock(l1);
+        startSealedBlock(l1);       // ISSA seal immediately
         compileExpr(binaryExpr.expr2);
         var temp = ensureTemp();
         jumpTo(l3);
-        startBlock(l2);
+        startSealedBlock(l2);       // ISSA seal immediately
         // Below we must write to the same temp
-        code(new Instruction.Move(new Operand.ConstantOperand(isAnd ? 0 : 1, typeDictionary.INT), temp));
+        codeMove(new Operand.ConstantOperand(isAnd ? 0 : 1, typeDictionary.INT), temp);
         jumpTo(l3);
-        startBlock(l3);
+        startSealedBlock(l3);       // ISSA seal immediately
         // leave temp on virtual stack
         return false;
     }
 
-
     private boolean compileBinaryExpr(AST.BinaryExpr binaryExpr) {
         String opCode = binaryExpr.op.str;
         if (opCode.equals("&&") ||
-            opCode.equals("||")) {
+                opCode.equals("||")) {
             return codeBoolean(binaryExpr);
         }
         boolean indexed = compileExpr(binaryExpr.expr1);
@@ -466,7 +476,7 @@ private boolean compileBinaryExpr(AST.BinaryExpr binaryExpr) {
         Operand right = pop();
         Operand left = pop();
         if (left instanceof Operand.NullConstantOperand &&
-            right instanceof Operand.NullConstantOperand) {
+                right instanceof Operand.NullConstantOperand) {
             long value = 0;
             switch (opCode) {
                 case "==": value = 1; break;
@@ -496,7 +506,7 @@ else if (left instanceof Operand.ConstantOperand leftconstant &&
         }
         else {
             var temp = createTemp(binaryExpr.type);
-            code(new Instruction.Binary(opCode, temp, left, right));
+            codeBinary(opCode, temp, left, right);
         }
         return false;
     }
@@ -518,7 +528,7 @@ private boolean compileUnaryExpr(AST.UnaryExpr unaryExpr) {
         }
         else {
             var temp = createTemp(unaryExpr.type);
-            code(new Instruction.Unary(opCode, temp, top));
+            codeUnary(opCode, temp, top);
         }
         return false;
     }
@@ -559,7 +569,7 @@ else if (operand instanceof Operand.RegisterOperand registerOperand)
     private Operand.TempRegisterOperand createTempAndMove(Operand src) {
         Type type = typeOfOperand(src);
         var temp = createTemp(type);
-        code(new Instruction.Move(src, temp));
+        codeMove(src, temp);
         return temp;
     }
 
@@ -576,8 +586,8 @@ private Operand.RegisterOperand ensureTemp() {
         } else throw new CompilerException("Cannot convert to temporary register");
     }
 
-    private void pushLocal(Register reg) {
-        pushOperand(new Operand.LocalRegisterOperand(reg));
+    private void pushLocal(Register reg, Symbol.VarSymbol varSymbol) {
+        pushOperand(new Operand.LocalRegisterOperand(reg, varSymbol));
     }
 
     private void pushOperand(Operand operand) {
@@ -596,13 +606,13 @@ private Operand.TempRegisterOperand codeIndexedLoad() {
         Operand indexed = pop();
         var temp = createTemp(indexed.type);
         if (indexed instanceof Operand.LoadIndexedOperand loadIndexedOperand) {
-            code(new Instruction.ArrayLoad(loadIndexedOperand, temp));
+            codeArrayLoad(loadIndexedOperand, temp);
         }
         else if (indexed instanceof Operand.LoadFieldOperand loadFieldOperand) {
-            code(new Instruction.GetField(loadFieldOperand, temp));
+            codeGetField(loadFieldOperand, temp);
         }
         else
-            code(new Instruction.Move(indexed, temp));
+            codeMove(indexed, temp);
         return temp;
     }
 
@@ -610,30 +620,158 @@ private void codeIndexedStore() {
         Operand value = pop();
         Operand indexed = pop();
         if (indexed instanceof Operand.LoadIndexedOperand loadIndexedOperand) {
-            code(new Instruction.ArrayStore(value, loadIndexedOperand));
+            codeArrayStore(value, loadIndexedOperand);
         }
         else if (indexed instanceof Operand.LoadFieldOperand loadFieldOperand) {
-            code(new Instruction.SetField(value, loadFieldOperand));
+            codeSetField(value, loadFieldOperand);
         }
         else
-            code(new Instruction.Move(value, indexed));
+            codeMove(value, indexed);
     }
 
     private void codeNew(Type type) {
-        var temp = createTemp(type);
-        if (type instanceof Type.TypeArray typeArray) {
-            code(new Instruction.NewArray(typeArray, temp));
-        }
-        else if (type instanceof Type.TypeStruct typeStruct) {
-            code(new Instruction.NewStruct(typeStruct, temp));
-        }
+        if (type instanceof Type.TypeArray typeArray)
+            codeNewArray(typeArray);
+        else if (type instanceof Type.TypeStruct typeStruct)
+            codeNewStruct(typeStruct);
         else
             throw new CompilerException("Unexpected type: " + type);
     }
 
+    private void codeNewArray(Type.TypeArray typeArray) {
+        var temp = createTemp(typeArray);
+        var target = (Operand.RegisterOperand) issa.write(temp);
+        var insn = new Instruction.NewArray(typeArray, target);
+        issa.recordDef(target, insn);
+        code(insn);
+    }
+
+    private void codeNewStruct(Type.TypeStruct typeStruct) {
+        var temp = createTemp(typeStruct);
+        var target = (Operand.RegisterOperand) issa.write(temp);
+        var insn = new Instruction.NewStruct(typeStruct, target);
+        issa.recordDef(target, insn);
+        code(insn);
+    }
+
     private void codeStoreAppend() {
-        var operand = pop();
-        code(new Instruction.AStoreAppend((Operand.RegisterOperand) top(), operand));
+        var operand = issa.read(pop());
+        Operand.RegisterOperand arrayOperand = (Operand.RegisterOperand) issa.read(top());
+        var insn = new Instruction.AStoreAppend(arrayOperand, operand);
+        issa.recordUse(arrayOperand, insn);
+        issa.recordUse(operand, insn);
+        code(insn);
+    }
+
+    private void codeArg(Operand.LocalRegisterOperand target) {
+        var newtarget = (Operand.RegisterOperand) issa.write(target);
+        var insn = new Instruction.ArgInstruction(newtarget);
+        issa.recordDef(newtarget, insn);
+        code(insn);
+    }
+
+    private void codeMove(Operand srcOperand, Operand destOperand) {
+        srcOperand = issa.read(srcOperand);
+        destOperand = issa.write(destOperand);
+        var insn = new Instruction.Move(srcOperand, destOperand);
+        issa.recordDef(destOperand, insn);
+        issa.recordUse(srcOperand, insn);
+        code(insn);
+    }
+
+    private void codeReturn(Operand resultOperand) {
+        resultOperand = issa.read(resultOperand);
+        var insn = new Instruction.Ret(resultOperand);
+        issa.recordUse(resultOperand, insn);
+        code(insn);
+    }
+
+    private void codeCBR(BasicBlock block, Operand condition, BasicBlock trueBlock, BasicBlock falseBlock) {
+        condition = issa.read(condition);
+        var insn = new Instruction.ConditionalBranch(block, condition, trueBlock, falseBlock);
+        assert !issa.isSealed(trueBlock);
+        assert !issa.isSealed(falseBlock);
+        block.addSuccessor(trueBlock);
+        block.addSuccessor(falseBlock);
+        issa.recordUse(condition, insn);
+        code(insn);
+    }
+
+    private void codeCall(int newBase,
+                          Operand.RegisterOperand targetOperand,
+                          Type.TypeFunction calleeType,
+                          Operand.RegisterOperand ...arguments) {
+        if (targetOperand != null)
+            targetOperand = (Operand.RegisterOperand) issa.write(targetOperand);
+        Operand.RegisterOperand args[] = new Operand.RegisterOperand[arguments.length];
+        for (int i = 0; i < args.length; i++) {
+            args[i] = (Operand.RegisterOperand) issa.read(arguments[i]);
+        }
+        var insn = new Instruction.Call(newBase, targetOperand, calleeType, args);
+        for (int i = 0; i < args.length; i++) {
+            issa.recordUse(args[i], insn);
+        }
+        if (targetOperand != null)
+            issa.recordDef(targetOperand, insn);
+        code(insn);
+    }
+
+    private void codeUnary(String opCode, Operand.RegisterOperand result, Operand operand) {
+        operand = issa.read(operand);
+        result = (Operand.RegisterOperand) issa.write(result);
+        var insn = new Instruction.Unary(opCode, result, operand);
+        issa.recordDef(result, insn);
+        issa.recordUse(operand, insn);
+        code(insn);
+    }
+
+    private void codeBinary(String opCode, Operand.RegisterOperand result, Operand left, Operand right) {
+        left = issa.read(left);
+        right = issa.read(right);
+        result = (Operand.RegisterOperand) issa.write(result);
+        var insn = new Instruction.Binary(opCode, result, left, right);
+        issa.recordDef(result, insn);
+        issa.recordUse(left, insn);
+        issa.recordUse(right, insn);
+        code(insn);
+    }
+
+    private void codeArrayLoad(Operand.LoadIndexedOperand loadIndexedOperand, Operand.RegisterOperand target) {
+        loadIndexedOperand = new Operand.LoadIndexedOperand(issa.read(loadIndexedOperand.arrayOperand), issa.read(loadIndexedOperand.indexOperand));
+        target = (Operand.RegisterOperand) issa.write(target);
+        var insn = new Instruction.ArrayLoad(loadIndexedOperand, target);
+        issa.recordDef(target, insn);
+        issa.recordUse(loadIndexedOperand.arrayOperand, insn);
+        issa.recordUse(loadIndexedOperand.indexOperand, insn);
+        code(insn);
+    }
+
+    private void codeGetField(Operand.LoadFieldOperand loadFieldOperand, Operand.RegisterOperand target) {
+        loadFieldOperand = new Operand.LoadFieldOperand(issa.read(loadFieldOperand.structOperand), loadFieldOperand.fieldName, loadFieldOperand.fieldIndex);
+        target = (Operand.RegisterOperand) issa.write(target);
+        var insn = new Instruction.GetField(loadFieldOperand, target);
+        issa.recordDef(target, insn);
+        issa.recordUse(loadFieldOperand.structOperand, insn);
+        code(insn);
+    }
+
+    private void codeArrayStore(Operand value, Operand.LoadIndexedOperand loadIndexedOperand) {
+        loadIndexedOperand = new Operand.LoadIndexedOperand(issa.read(loadIndexedOperand.arrayOperand), issa.read(loadIndexedOperand.indexOperand));
+        value = issa.read(value);
+        var insn = new Instruction.ArrayStore(value, loadIndexedOperand);
+        issa.recordUse(loadIndexedOperand.arrayOperand, insn);
+        issa.recordUse(loadIndexedOperand.indexOperand, insn);
+        issa.recordUse(value, insn);
+        code(insn);
+    }
+
+    private void codeSetField(Operand value, Operand.LoadFieldOperand loadFieldOperand) {
+        loadFieldOperand = new Operand.LoadFieldOperand(issa.read(loadFieldOperand.structOperand), loadFieldOperand.fieldName, loadFieldOperand.fieldIndex);
+        value = issa.read(value);
+        var insn = new Instruction.SetField(value, loadFieldOperand);
+        issa.recordUse(loadFieldOperand.structOperand, insn);
+        issa.recordUse(value, insn);
+        code(insn);
     }
 
     private boolean vstackEmpty() {
@@ -677,4 +815,307 @@ public StringBuilder toDot(StringBuilder sb, boolean verbose) {
         sb.append("}\n");
         return sb;
     }
+
+    interface IncrementalSSA {
+        Operand read(Operand operand);
+        Operand write(Operand operand);
+        void recordUse(Operand operand, Instruction instruction);
+        void recordDef(Register reg, Instruction instruction);
+        void recordDef(Operand operand, Instruction instruction);
+        void sealBlock(BasicBlock block);
+        boolean isSealed(BasicBlock block);
+        void finish(EnumSet<Options> options);
+    }
+
+    /**
+     * Support for AST to SSA IR using Braun's method.
+     * See Simple and Efficient Construction of Static Single Assignment Form, 2013
+     * Matthias Braun, Sebastian Buchwald, Sebastian Hack, Roland Leißa
+     */
+    static final class IncrementalSSABraun implements IncrementalSSA {
+
+        CompiledFunction function;
+
+        // For each unique variable (variable with same name in different scopes must be distinct)
+        // a mapping is maintained for the name to SSA value (virtual register) in each block.
+        // we could add this mapping to the BB itself but it seems nicer to keep it separate
+        // at least for now.
+        // Note that we use the nonSSAId as proxy for variable name
+        // because this id is unique for non-SSA variables, but for SSA versions this refers
+        // back to the original ID
+        Map<Integer, Map<BasicBlock, Register>> currentDef = new HashMap<>();
+        // Flags blocks that are completed in terms of instruction generation
+        BitSet sealedBlocks = new BitSet();
+        // Tracks Phis that are not finalized because the basic block is not yet sealed
+        Map<BasicBlock, Map<Register, Instruction.Phi>> incompletePhis = new HashMap<>();
+
+        // Not explicitly stated in the paper but implicit in the algo is
+        // the availability of Def-use chains. We have to main this incrementally as we
+        // generate code - used when eliminating trivial phis
+        Map<Register, SSAEdges.SSADef> ssaDefUses = new HashMap<>();
+
+        // This is not part of the spec, it is just an implementation detail
+        // We pre-assign registers to local declared vars, but then the SSA part
+        // creates new ones. However, the first time a variable is versioned we could just use the
+        // original regnum.
+        // This set tracks whether a version is being created first time
+        // It also helps us maintain version numbers similar to traditional SSA
+        Map<Integer,Integer> versioned = new HashMap<>();
+
+        private IncrementalSSABraun(CompiledFunction function) {
+            this.function = function;
+        }
+
+        /**
+         * Associates a new definition (value) to a variable name within a basic block
+         */
+        private void writeVariable(Register variable, BasicBlock block, Register value) {
+            currentDef.computeIfAbsent(variable.nonSSAId(), k -> new HashMap<>()).put(block, value);
+        }
+
+        /**
+         * Looks up the current SSA value (virtual register) associated with a name, inside a block.
+         * If no mapping is found, processing depends on status of the block.
+         * @see #readVariableRecursive(Register, BasicBlock)
+         */
+        private Register readVariable(Register variable, BasicBlock block) {
+            Map<BasicBlock, Register> defs = currentDef.get(variable.nonSSAId());
+            if (defs != null && defs.containsKey(block)) {
+                // local value numbering
+                return defs.get(block);
+            }
+            // global value numbering
+            return readVariableRecursive(variable, block);
+        }
+
+        /**
+         * Called when a block does not have a mapping for a variable.
+         * If the block is still under construction, then a Phi is inserted into the
+         * block - and the phi is marked as incomplete.
+         * If block is constructed, then we look at predecessors for definitions;
+         * in case of more than 1 predecessor a phi is created with input
+         * obtained recursively via each predecessor block.
+         * In case of 1 predecessor the value is read recursively from that predecessor.
+         */
+        private Register readVariableRecursive(Register variable, BasicBlock block) {
+            Register val;
+            if (!isSealed(block)) {
+                // incomplete CFG
+                val = makeVersion(variable);
+                Instruction.Phi phi = makePhi(val, block);
+                incompletePhis.computeIfAbsent(block, k -> new HashMap<>()).put(variable, phi);
+            }
+            else if (block.predecessors.size() == 1) {
+                // Optimize the common case of one predecessor: No phi needed
+                val = readVariable(variable, block.predecessors.get(0));
+            }
+            else {
+                // Break potential cycles with operandless phis
+                val = makeVersion(variable);
+                Instruction.Phi phi = makePhi(val, block);
+                writeVariable(variable, block, val);
+                val = addPhiOperands(variable,phi);
+            }
+            writeVariable(variable, block, val);
+            return val;
+        }
+
+        private Instruction.Phi makePhi(Register val, BasicBlock block) {
+            Instruction.Phi phi = new Instruction.Phi(val, new ArrayList<>());
+            recordDef(val, phi);
+            block.add(0, phi);
+            return phi;
+        }
+
+        /**
+         * Populate the members of a phi instruction
+         */
+        private Register addPhiOperands(Register variable, Instruction.Phi phi) {
+            assert phi.numInputs() == 0;
+            // Determine operands from predecessors
+            for (BasicBlock pred: phi.block.predecessors) {
+                phi.addInput(readVariable(variable,pred));
+            }
+            return tryRemovingPhi(phi);
+        }
+
+        private Register tryRemovingPhi(Instruction.Phi phi) {
+            Register same = null;
+            // Check if phi has distinct inputs
+            for (int i = 0; i < phi.numInputs(); i++) {
+                if (!phi.isRegisterInput(i))
+                    // Cannot happen?
+                    throw new IllegalStateException();
+                var use = phi.inputAsRegister(i);
+                if (use.equals(same) || use.equals(phi.value()))
+                    continue; // Unique value or self reference
+                if (same != null)
+                    // More than 1 distinct value, so keep phi
+                    return phi.value();
+                same = use;
+            }
+            if (same == null) {
+                // phi is unreachable or in the start block
+                // Paper suggests we create an Undef, but we throw an exception
+                // same = function.registerPool.newReg("Undef", null);
+                throw new CompilerException("Undefined value for phi " + phi.value());
+            }
+            // remember uses except phi
+            var users = getUsesExcept(phi);
+            // remove all uses of phi to same and remove phi
+            replacePhiValueAndUsers(phi, same);
+            phi.block.deleteInstruction(phi);
+            // try to recursively remove all phi users, which might have become trivial
+            for (var use: users) {
+                if (use instanceof Instruction.Phi phiuser)
+                    tryRemovingPhi(phiuser);
+            }
+            return same;
+        }
+
+        /**
+         * Reroute all uses of phi to new value
+         */
+        private void replacePhiValueAndUsers(Instruction.Phi phi, Register newValue) {
+            var oldDefUseChain = ssaDefUses.get(phi.value());
+            var newDefUseChain = ssaDefUses.get(newValue);
+            if (newDefUseChain == null) {
+                // Can be null because this may be existing def
+                newDefUseChain = SSAEdges.addDef(ssaDefUses, newValue, phi);
+            }
+            if (oldDefUseChain != null) {
+                for (Instruction instruction: oldDefUseChain.useList) {
+                    if (instruction instanceof Instruction.Phi somePhi) {
+                        somePhi.replaceInput(phi.value(), newValue);
+                    }
+                    else {
+                        instruction.replaceUse(phi.value(), newValue);
+                    }
+                }
+                // Users of phi old value become users of the new value
+                newDefUseChain.useList.addAll(oldDefUseChain.useList);
+                oldDefUseChain.useList.clear();
+                // FIXME remove old def from def-use chains
+            }
+        }
+
+        private List<Instruction> getUsesExcept(Instruction.Phi phi) {
+            var oldDefUseChain = ssaDefUses.get(phi.value());
+            if (oldDefUseChain == null) {
+                return new ArrayList<>();
+            }
+            var useList = new ArrayList<>(oldDefUseChain.useList);
+            useList.remove(phi);
+            return useList;
+        }
+
+        @Override
+        public Operand read(Operand operand) {
+            // We have to consider temps too because of boolean expressions
+            // where temps are not SSA
+            if (operand instanceof Operand.RegisterOperand localRegisterOperand) {
+                var reg = readVariable(localRegisterOperand.reg, function.currentBlock);
+                operand = new Operand.RegisterOperand(reg);
+            }
+            return operand;
+        }
+        @Override
+        public Operand write(Operand operand) {
+            // We have to consider temps too because of boolean expressions
+            // where temps are not SSA
+            if (operand instanceof Operand.RegisterOperand localRegisterOperand) {
+                var variable = localRegisterOperand.reg;
+                Register newValue = makeVersion(variable);
+                writeVariable(variable, function.currentBlock, newValue);
+                operand = new Operand.RegisterOperand(newValue);
+            }
+            return operand;
+        }
+
+        private Register makeVersion(Register variable) {
+            Register newValue;
+            Integer version = versioned.get(variable.nonSSAId());
+            // Avoid creating a new value first time because we already
+            // have a pre-created register we can use
+            if (version == null) {
+                newValue = variable;
+                versioned.put(variable.nonSSAId(), 1);
+            }
+            else {
+                versioned.put(variable.nonSSAId(), version + 1);
+                newValue = function.registerPool.ssaReg(variable, version);
+            }
+            return newValue;
+        }
+
+        @Override
+        public void recordUse(Operand operand, Instruction instruction) {
+            if (operand instanceof Operand.RegisterOperand registerOperand) {
+                SSAEdges.recordUse(ssaDefUses, instruction, registerOperand.reg);
+            }
+        }
+        @Override
+        public void recordDef(Register reg, Instruction instruction) {
+            SSAEdges.recordDef(ssaDefUses, reg, instruction);
+        }
+        @Override
+        public void recordDef(Operand operand, Instruction instruction) {
+            if (operand instanceof Operand.RegisterOperand registerOperand) {
+                SSAEdges.recordDef(ssaDefUses, registerOperand.reg, instruction);
+            }
+        }
+        @Override
+        public void sealBlock(BasicBlock block) {
+            if (isSealed(block))
+                return;
+            var pendingPhis = incompletePhis.remove(block);
+            if (pendingPhis != null) {
+                for (var variable : pendingPhis.keySet()) {
+                    addPhiOperands(variable, pendingPhis.get(variable));
+                }
+            }
+            sealedBlocks.set(block.bid);
+        }
+        @Override
+        public boolean isSealed(BasicBlock block) {
+            return sealedBlocks.get(block.bid);
+        }
+        @Override
+        public void finish(EnumSet<Options> options) {
+            function.isSSA = true;
+            if (options != null && options.contains(Options.DUMP_SSA_IR)) {
+                function.dumpIR(false, "Post SSA IR");
+            }
+        }
+    }
+
+    static final class NoopIncrementalSSA implements IncrementalSSA {
+        @Override
+        public Operand read(Operand operand) {
+            return operand;
+        }
+        @Override
+        public Operand write(Operand operand) {
+            return operand;
+        }
+        @Override
+        public void recordUse(Operand operand, Instruction instruction) {
+        }
+        @Override
+        public void recordDef(Register reg, Instruction instruction) {
+        }
+        @Override
+        public void recordDef(Operand operand, Instruction instruction) {
+        }
+        @Override
+        public void sealBlock(BasicBlock block) {
+        }
+        @Override
+        public boolean isSealed(BasicBlock block) {
+            return false;
+        }
+        @Override
+        public void finish(EnumSet<Options> options) {
+        }
+    }
 }
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java
index 4c24bb6..b15a157 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java
@@ -16,7 +16,7 @@ private void compile(TypeDictionary typeDictionary, EnumSet<Options> options) {
         for (Symbol symbol: typeDictionary.getLocalSymbols()) {
             if (symbol instanceof Symbol.FunctionTypeSymbol functionSymbol) {
                 Type.TypeFunction functionType = (Type.TypeFunction) functionSymbol.type;
-                var function = new CompiledFunction(functionSymbol, typeDictionary);
+                var function = new CompiledFunction(functionSymbol, typeDictionary, options);
                 if (options.contains(Options.DUMP_INITIAL_IR))
                     function.dumpIR(false, "Initial IR");
                 functionType.code = function;
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java
index 1651c1c..7bb016b 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java
@@ -290,8 +290,6 @@ public ConditionalBranch(BasicBlock currentBlock, Operand condition, BasicBlock
             super(I_CBR, (Operand.RegisterOperand) null, condition);
             this.trueBlock = trueBlock;
             this.falseBlock = falseBlock;
-            currentBlock.addSuccessor(trueBlock);
-            currentBlock.addSuccessor(falseBlock);
         }
         public Operand condition() { return uses[0]; }
         @Override
@@ -426,6 +424,22 @@ public StringBuilder toStr(StringBuilder sb) {
             sb.append(")");
             return sb;
         }
+        public void addInput(Register register) {
+            var newUses = new Operand[uses.length + 1];
+            System.arraycopy(uses, 0, newUses, 0, uses.length);
+            newUses[newUses.length-1] = new Operand.RegisterOperand(register);
+            this.uses = newUses;
+        }
+        public void replaceInput(Register oldReg, Register newReg) {
+            for (int i = 0; i < numInputs(); i++) {
+                if (isRegisterInput(i)) {
+                    Register in = inputAsRegister(i);
+                    if (in.equals(oldReg)) {
+                        replaceInput(i, newReg);
+                    }
+                }
+            }
+        }
     }
 
     public static class ArgInstruction extends Instruction {
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Operand.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Operand.java
index 57a691e..8ff84e4 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Operand.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Operand.java
@@ -1,5 +1,6 @@
 package com.compilerprogramming.ezlang.compiler;
 
+import com.compilerprogramming.ezlang.types.Symbol;
 import com.compilerprogramming.ezlang.types.Type;
 
 public class Operand {
@@ -35,7 +36,7 @@ protected RegisterOperand(Register reg) {
             if (reg == null)
                 throw new NullPointerException();
         }
-        public int frameSlot() { return reg.nonSSAId(); }
+        public int frameSlot() { return reg.frameSlot(); }
 
         public RegisterOperand copy(Register register) {
            return new RegisterOperand(register);
@@ -48,12 +49,14 @@ public String toString() {
     }
 
     public static class LocalRegisterOperand extends RegisterOperand {
-        public LocalRegisterOperand(Register reg) {
+        Symbol.VarSymbol variable;
+        public LocalRegisterOperand(Register reg, Symbol.VarSymbol variable) {
             super(reg);
+            this.variable = variable;
         }
         @Override
         public RegisterOperand copy(Register register) {
-            return new LocalRegisterOperand(register);
+            return new LocalRegisterOperand(register, variable);
         }
     }
 
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Optimizer.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Optimizer.java
index c9e21d8..1856e74 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Optimizer.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Optimizer.java
@@ -6,7 +6,8 @@ public class Optimizer {
 
     public void optimize(CompiledFunction function, EnumSet<Options> options) {
         if (options.contains(Options.OPTIMIZE)) {
-            new EnterSSA(function, options);
+            if (!function.isSSA)
+                new EnterSSA(function, options);
             if (options.contains(Options.SCCP)) {
                 new SparseConditionalConstantPropagation().constantPropagation(function).apply(options);
                 if (new ConstantComparisonPropagation(function).apply(options)) {
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Options.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Options.java
index 56280e2..b49d9b5 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Options.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Options.java
@@ -3,6 +3,7 @@
 import java.util.EnumSet;
 
 public enum Options {
+    ISSA, // Incremental SSA
     OPTIMIZE,
     SCCP,
     CCP, // constant comparison propagation
@@ -22,5 +23,6 @@ public enum Options {
 
     public static final EnumSet<Options> NONE = EnumSet.noneOf(Options.class);
     public static final EnumSet<Options> OPT = EnumSet.of(Options.OPTIMIZE,Options.SCCP,Options.CCP,Options.REGALLOC);
-    public static final EnumSet<Options> OPT_VERBOSE = EnumSet.allOf(Options.class);
+    public static final EnumSet<Options> VERBOSE = EnumSet.range(DUMP_INITIAL_IR, DUMP_POST_CHAITIN_IR);
+    public static final EnumSet<Options> OPT_VERBOSE = EnumSet.range(OPTIMIZE, DUMP_POST_CHAITIN_IR);
 }
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Register.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Register.java
index 6ad3258..3376e61 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Register.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Register.java
@@ -83,11 +83,13 @@ public String name() {
      * During SSA form this is not valid for registers that are instances of SSARegister.
      */
     public int nonSSAId() {
+        //assert frameSlot >= 0; // assert inteferes with verbose display
         return frameSlot;
     }
     public void updateSlot(int slot) {
         this.frameSlot = slot;
     }
+    public int frameSlot() { return frameSlot; }
 
     /**
      * An SSA Register retains a reference to the original
@@ -106,4 +108,6 @@ public int nonSSAId() {
             return originalRegNumber;
         }
     }
+
+
 }
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/RegisterPool.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/RegisterPool.java
index 5cb9e93..88469af 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/RegisterPool.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/RegisterPool.java
@@ -49,7 +49,7 @@ public int numRegisters() {
     }
     public void toStr(StringBuilder sb) {
         for (Register reg : registers) {
-            sb.append("Reg #").append(reg.id).append(" ").append(reg.name()).append("\n");
+            sb.append("Reg #").append(reg.id).append(" ").append(reg.name()).append(" ").append(reg.nonSSAId()).append("\n");
         }
     }
 }
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SSAEdges.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SSAEdges.java
index e6fcdcb..4e81e91 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SSAEdges.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SSAEdges.java
@@ -83,12 +83,16 @@ private static void recordUses(CompiledFunction function, Map<Register, SSADef>
 
     private static void recordUses(Map<Register, SSADef> defUseChains, Register[] inputs, BasicBlock block, Instruction instruction) {
         for (Register register : inputs) {
-            SSADef def = defUseChains.get(register);
-            def.useList.add(instruction);
+            recordUse(defUseChains, instruction, register);
         }
     }
 
-    private static void recordDef(Map<Register, SSADef> defUseChains, Register value, Instruction instruction) {
+    public static void recordUse(Map<Register, SSADef> defUseChains, Instruction instruction, Register register) {
+        SSADef def = defUseChains.get(register);
+        def.useList.add(instruction);
+    }
+
+    public static void recordDef(Map<Register, SSADef> defUseChains, Register value, Instruction instruction) {
         if (defUseChains.containsKey(value))
             throw new CompilerException("Register already defined, invalid multiple definition in SSA");
         defUseChains.put(value, new SSADef(instruction));
diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestIncrementalSSA.java b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestIncrementalSSA.java
new file mode 100644
index 0000000..864cb42
--- /dev/null
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestIncrementalSSA.java
@@ -0,0 +1,132 @@
+package com.compilerprogramming.ezlang.compiler;
+
+import org.junit.Test;
+
+import java.util.EnumSet;
+
+public class TestIncrementalSSA {
+    String compileSrc(String src) {
+        var compiler = new Compiler();
+        var typeDict = compiler.compileSrc(src, EnumSet.of(Options.ISSA,Options.DUMP_SSA_IR));
+        return compiler.dumpIR(typeDict,true);
+    }
+
+    @Test
+    public void test1() {
+        String src = """
+                func foo(d: Int) {
+                    var a = 42;
+                    var b = a;
+                    var c = a + b;
+                    a = c + 23;
+                    c = a + d;
+                }
+                """;
+        String result = compileSrc(src);
+        System.out.println(result);
+    }
+
+    @Test
+    public void test2() {
+        String src = """
+                func foo(d: Int)->Int {
+                    var a = 42
+                    if (d)
+                    {
+                      a = a + 1
+                    }
+                    else
+                    {
+                      a = a - 1
+                    }
+                    return a
+                }
+                """;
+        String result = compileSrc(src);
+        System.out.println(result);
+    }
+
+    @Test
+    public void test3() {
+        String src = """
+                func factorial(num: Int)->Int {
+                    var result = 1
+                    while (num > 1)
+                    {
+                      result = result * num
+                      num = num - 1
+                    }
+                    return result
+                }
+                """;
+        String result = compileSrc(src);
+        System.out.println(result);
+    }
+
+
+    @Test
+    public void test4() {
+        String src = """
+                func print(a: Int, b: Int, c:Int, d:Int) {}
+                func example14_66(p: Int, q: Int, r: Int, s: Int, t: Int) {
+                    var i = 1
+                    var j = 1
+                    var k = 1
+                    var l = 1
+                    while (1) {
+                        if (p) {
+                            j = i
+                            if (q) {
+                                l = 2
+                            }
+                            else {
+                                l = 3
+                            }
+                            k = k + 1
+                        }
+                        else {
+                            k = k + 2
+                        }
+                        print(i,j,k,l)
+                        while (1) {
+                            if (r) {
+                                l = l + 4
+                            }
+                            if (!s)
+                                break
+                        }
+                        i = i + 6
+                        if (!t)
+                            break
+                    }
+                }
+                """;
+        String result = compileSrc(src);
+        System.out.println(result);
+    }
+
+    @Test
+    public void test5() {
+        String src = """
+                func fib(n: Int)->Int {
+                    var i: Int;
+                    var temp: Int;
+                    var f1=1;
+                    var f2=1;
+                    i=n;
+                    while( i>1 ){
+                        temp = f1+f2;
+                        f1=f2;
+                        f2=temp;
+                        i=i-1;
+                    }
+                    return f2;
+                }
+                func foo()->Int {
+                    return fib(10);
+                }
+                """;
+        String result = compileSrc(src);
+        System.out.println(result);
+    }
+}
\ No newline at end of file
diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestInterferenceGraph.java b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestInterferenceGraph.java
index e5a4299..8018cba 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestInterferenceGraph.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestInterferenceGraph.java
@@ -11,7 +11,8 @@ public class TestInterferenceGraph {
     private CompiledFunction buildTest1() {
         TypeDictionary typeDictionary = new TypeDictionary();
         Type.TypeFunction functionType = new Type.TypeFunction("foo");
-        functionType.addArg(new Symbol.ParameterSymbol("a", typeDictionary.INT));
+        var argSymbol = new Symbol.ParameterSymbol("a", typeDictionary.INT);
+        functionType.addArg(argSymbol);
         functionType.setReturnType(typeDictionary.INT);
         CompiledFunction function = new CompiledFunction(functionType, typeDictionary);
         RegisterPool regPool = function.registerPool;
@@ -19,7 +20,7 @@ private CompiledFunction buildTest1() {
         Register b = regPool.newReg("b", typeDictionary.INT);
         Register c = regPool.newReg("c", typeDictionary.INT);
         Register d = regPool.newReg("d", typeDictionary.INT);
-        function.code(new Instruction.ArgInstruction(new Operand.LocalRegisterOperand(a)));
+        function.code(new Instruction.ArgInstruction(new Operand.LocalRegisterOperand(a, argSymbol)));
         function.code(new Instruction.Binary(
                 "+",
                 new Operand.RegisterOperand(a),
@@ -100,6 +101,8 @@ private CompiledFunction buildTest2() {
                 function.currentBlock,
                 new Operand.RegisterOperand(a),
                 b1, b2));
+        function.currentBlock.addSuccessor(b1);
+        function.currentBlock.addSuccessor(b2);
         function.startBlock(b1);
         function.code(new Instruction.Move(
                 new Operand.ConstantOperand(2, typeDictionary.INT),
diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestLiveness.java b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestLiveness.java
index 8e632d7..7a324b2 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestLiveness.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestLiveness.java
@@ -39,19 +39,19 @@ func foo() {
         String output = Compiler.dumpIR(typeDict, true);
         Assert.assertEquals("""
 func print(n: Int)
-Reg #0 n
+Reg #0 n 0
 L0:
     arg n
     goto  L1
 L1:
 func foo()
-Reg #0 i
-Reg #1 s
-Reg #2 %t2
-Reg #3 %t3
-Reg #4 %t4
-Reg #5 %t5
-Reg #6 %t6
+Reg #0 i 0
+Reg #1 s 1
+Reg #2 %t2 2
+Reg #3 %t3 3
+Reg #4 %t4 4
+Reg #5 %t5 5
+Reg #6 %t6 6
 L0:
     i = 1
     s = 1
@@ -159,13 +159,13 @@ func foo(a: Int, b: Int) {
         String output = Compiler.dumpIR(typeDict, true);
         Assert.assertEquals("""
 func foo(a: Int,b: Int)
-Reg #0 a
-Reg #1 b
-Reg #2 %t2
-Reg #3 %t3
-Reg #4 %t4
-Reg #5 %t5
-Reg #6 %t6
+Reg #0 a 0
+Reg #1 b 1
+Reg #2 %t2 2
+Reg #3 %t3 3
+Reg #4 %t4 4
+Reg #5 %t5 5
+Reg #6 %t6 6
 L0:
     arg a
     arg b
@@ -264,6 +264,8 @@ static CompiledFunction buildTest3() {
                 function.currentBlock,
                 new Operand.RegisterOperand(i),
                 b2, b3));
+        function.currentBlock.addSuccessor(b2);
+        function.currentBlock.addSuccessor(b3);
         function.startBlock(b2);
         function.code(new Instruction.Move(
                 new Operand.ConstantOperand(0, typeDictionary.INT),
@@ -284,6 +286,8 @@ static CompiledFunction buildTest3() {
                 function.currentBlock,
                 new Operand.RegisterOperand(i),
                 b1, b4));
+        function.currentBlock.addSuccessor(b1);
+        function.currentBlock.addSuccessor(b4);
         function.startBlock(b4);
         function.code(new Instruction.Ret(new Operand.RegisterOperand(s)));
         function.startBlock(function.exit);
@@ -301,8 +305,8 @@ public void test3() {
         String actual = function.toStr(new StringBuilder(), true).toString();
         Assert.assertEquals("""
 func foo()->Int
-Reg #0 i
-Reg #1 s
+Reg #0 i 0
+Reg #1 s 1
 L0:
     i = 1
     goto  L2
@@ -366,11 +370,11 @@ public void testSwapProblem() {
         String actual = function.toStr(new StringBuilder(), true).toString();
         Assert.assertEquals("""
 func foo(p: Int)
-Reg #0 p
-Reg #1 a1
-Reg #2 a2
-Reg #3 b1
-Reg #4 b2
+Reg #0 p 0
+Reg #1 a1 1
+Reg #2 a2 2
+Reg #3 b1 3
+Reg #4 b2 4
 L0:
     arg p
     a1 = 42
@@ -409,10 +413,10 @@ public void testLostCopyProblem() {
         String actual = function.toStr(new StringBuilder(), true).toString();
         Assert.assertEquals("""
 func foo(p: Int)->Int
-Reg #0 p
-Reg #1 x1
-Reg #2 x3
-Reg #3 x2
+Reg #0 p 0
+Reg #1 x1 1
+Reg #2 x3 2
+Reg #3 x2 3
 L0:
     arg p
     x1 = 1
@@ -466,7 +470,7 @@ func foo()->Int
         Assert.assertEquals("""
 Pre-SSA
 func foo()->Int
-Reg #0 %t0
+Reg #0 %t0 0
 L0:
     if 1 goto L2 else goto L3
     #PHIDEFS = {}
@@ -511,10 +515,10 @@ func foo()->Int
     #LIVEOUT = {0}
 Post-SSA
 func foo()->Int
-Reg #0 %t0
-Reg #1 %t0_0
-Reg #2 %t0_1
-Reg #3 %t0_2
+Reg #0 %t0 0
+Reg #1 %t0_0 0
+Reg #2 %t0_1 0
+Reg #3 %t0_2 0
 L0:
     if 1 goto L2 else goto L3
     #PHIDEFS = {}
diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
index 1c4fc5b..2a91a12 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
@@ -638,13 +638,14 @@ func bar(arg: Int)->Int {
     static CompiledFunction buildLostCopyTest() {
         TypeDictionary typeDictionary = new TypeDictionary();
         Type.TypeFunction functionType = new Type.TypeFunction("foo");
-        functionType.addArg(new Symbol.ParameterSymbol("p", typeDictionary.INT));
+        var argSymbol = new Symbol.ParameterSymbol("p", typeDictionary.INT);
+        functionType.addArg(argSymbol);
         functionType.setReturnType(typeDictionary.INT);
         CompiledFunction function = new CompiledFunction(functionType, typeDictionary);
         RegisterPool regPool = function.registerPool;
         Register p = regPool.newReg("p", typeDictionary.INT);
         Register x1 = regPool.newReg("x1", typeDictionary.INT);
-        function.code(new Instruction.ArgInstruction(new Operand.LocalRegisterOperand(p)));
+        function.code(new Instruction.ArgInstruction(new Operand.LocalRegisterOperand(p, argSymbol)));
         function.code(new Instruction.Move(
                 new Operand.ConstantOperand(1, typeDictionary.INT),
                 new Operand.RegisterOperand(x1)));
@@ -659,6 +660,8 @@ static CompiledFunction buildLostCopyTest() {
                 new Operand.ConstantOperand(1, typeDictionary.INT)));
         function.code(new Instruction.ConditionalBranch(B2,
                 new Operand.RegisterOperand(p), B2, function.exit));
+        function.currentBlock.addSuccessor(B2);
+        function.currentBlock.addSuccessor(function.exit);
         function.startBlock(function.exit);
         function.code(new Instruction.Ret(new Operand.RegisterOperand(x2)));
         function.isSSA = true;
@@ -706,7 +709,8 @@ public void testLostCopyProblem() {
     static CompiledFunction buildSwapTest() {
         TypeDictionary typeDictionary = new TypeDictionary();
         Type.TypeFunction functionType = new Type.TypeFunction("foo");
-        functionType.addArg(new Symbol.ParameterSymbol("p", typeDictionary.INT));
+        var argSymbol = new Symbol.ParameterSymbol("p", typeDictionary.INT);
+        functionType.addArg(argSymbol);
         functionType.setReturnType(typeDictionary.VOID);
         CompiledFunction function = new CompiledFunction(functionType, typeDictionary);
         RegisterPool regPool = function.registerPool;
@@ -715,7 +719,7 @@ static CompiledFunction buildSwapTest() {
         Register a2 = regPool.newReg("a2", typeDictionary.INT);
         Register b1 = regPool.newReg("b1", typeDictionary.INT);
         Register b2 = regPool.newReg("b2", typeDictionary.INT);
-        function.code(new Instruction.ArgInstruction(new Operand.LocalRegisterOperand(p)));
+        function.code(new Instruction.ArgInstruction(new Operand.LocalRegisterOperand(p, argSymbol)));
         function.code(new Instruction.Move(
                 new Operand.ConstantOperand(42, typeDictionary.INT),
                 new Operand.RegisterOperand(a1)));
@@ -728,6 +732,8 @@ static CompiledFunction buildSwapTest() {
         function.code(new Instruction.Phi(b2, Arrays.asList(b1, a2)));
         function.code(new Instruction.ConditionalBranch(B2,
                 new Operand.RegisterOperand(p), B2, function.exit));
+        function.currentBlock.addSuccessor(B2);
+        function.currentBlock.addSuccessor(function.exit);
         function.startBlock(function.exit);
         function.isSSA = true;
         return function;
diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java b/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java
index 4f8c0f0..fcfe0f5 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java
@@ -13,6 +13,7 @@ Value compileAndRun(String src, String mainFunction) {
         return compileAndRun(src, mainFunction, Options.NONE);
     }
     Value compileAndRun(String src, String mainFunction, EnumSet<Options> options) {
+        //options.add(Options.ISSA);
         var compiler = new Compiler();
         var typeDict = compiler.compileSrc(src, options);
         var compiled = compiler.dumpIR(typeDict);

From 91a3631cf6739115f8f84ecbac742f9a6280e9b3 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <dibyendu@redukti.com>
Date: Mon, 24 Feb 2025 17:14:16 +0000
Subject: [PATCH 041/119] Improve tests Comments added

Fix EnterSSA to not put phi nodes in exit block as exit block will never have any instructions

Some code adjustments to make comparison easy between regvm and optvm compiler
---
 .../ezlang/compiler/CompiledFunction.java     |  52 ++-
 .../ezlang/compiler/EnterSSA.java             |   2 +
 .../ezlang/compiler/Options.java              |   1 +
 .../ezlang/compiler/TestIncrementalSSA.java   | 404 +++++++++++++++++-
 .../ezlang/compiler/TestSSATransform.java     | 190 +++++++-
 .../ezlang/interpreter/TestInterpreter.java   |  23 +-
 .../ezlang/compiler/CompiledFunction.java     |  65 ++-
 7 files changed, 663 insertions(+), 74 deletions(-)

diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
index 0d577d2..4eb3bf3 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
@@ -41,6 +41,7 @@ public CompiledFunction(Symbol.FunctionTypeSymbol functionSymbol, TypeDictionary
         AST.FuncDecl funcDecl = (AST.FuncDecl) functionSymbol.functionDecl;
         this.functionType = (Type.TypeFunction) functionSymbol.type;
         this.registerPool = new RegisterPool();
+        // Incremental SSA is an optional feature
         this.issa = (options != null && options.contains(Options.ISSA)) ? new IncrementalSSABraun(this) : new NoopIncrementalSSA();
         setVirtualRegisters(funcDecl.scope);
         this.BID = 0;
@@ -49,6 +50,7 @@ public CompiledFunction(Symbol.FunctionTypeSymbol functionSymbol, TypeDictionary
         this.currentBreakTarget = null;
         this.currentContinueTarget = null;
         this.typeDictionary = typeDictionary;
+        issa.sealBlock(entry);          // Incremental SSA is an optional feature
         generateArgInstructions(funcDecl.scope);
         compileStatement(funcDecl.block);
         exitBlockIfNeeded();
@@ -135,7 +137,7 @@ else if (virtualStack.size() > 1)
 
     public void code(Instruction instruction) {
         currentBlock.add(instruction);
-        instruction.block = currentBlock;
+        assert instruction.block == currentBlock;
     }
 
     private void compileStatement(AST.Stmt statement) {
@@ -225,12 +227,12 @@ private void compileWhile(AST.WhileStmt whileStmt) {
             codeIndexedLoad();
         codeCBR(currentBlock, pop(), bodyBlock, exitBlock);
         assert vstackEmpty();
-        startSealedBlock(bodyBlock);  // ISSA If we seal this here fib test fails, wrong code generated, why?
+        startSealedBlock(bodyBlock);  // ISSA Body is immediately sealed as no new predecessors possible
         compileStatement(whileStmt.stmt);
         if (!isBlockTerminated(currentBlock))
             jumpTo(loopHead);
         issa.sealBlock(loopHead);
-        startSealedBlock(exitBlock);
+        startSealedBlock(exitBlock);    // ISSA seal exit block (breaks already done)
         currentContinueTarget = savedContinueTarget;
         currentBreakTarget = savedBreakTarget;
     }
@@ -450,7 +452,7 @@ private boolean codeBoolean(AST.BinaryExpr binaryExpr) {
         }
         startSealedBlock(l1);       // ISSA seal immediately
         compileExpr(binaryExpr.expr2);
-        var temp = ensureTemp();
+        var temp = ensureTemp();    // Normally temps are SSA but this temp gets two assignments, thus must be included during SSA conversion
         jumpTo(l3);
         startSealedBlock(l2);       // ISSA seal immediately
         // Below we must write to the same temp
@@ -464,7 +466,7 @@ private boolean codeBoolean(AST.BinaryExpr binaryExpr) {
     private boolean compileBinaryExpr(AST.BinaryExpr binaryExpr) {
         String opCode = binaryExpr.op.str;
         if (opCode.equals("&&") ||
-                opCode.equals("||")) {
+            opCode.equals("||")) {
             return codeBoolean(binaryExpr);
         }
         boolean indexed = compileExpr(binaryExpr.expr1);
@@ -476,7 +478,7 @@ private boolean compileBinaryExpr(AST.BinaryExpr binaryExpr) {
         Operand right = pop();
         Operand left = pop();
         if (left instanceof Operand.NullConstantOperand &&
-                right instanceof Operand.NullConstantOperand) {
+            right instanceof Operand.NullConstantOperand) {
             long value = 0;
             switch (opCode) {
                 case "==": value = 1; break;
@@ -602,15 +604,17 @@ private Operand top() {
         return virtualStack.getLast();
     }
 
+    private boolean vstackEmpty() {
+        return virtualStack.isEmpty();
+    }
+
     private Operand.TempRegisterOperand codeIndexedLoad() {
         Operand indexed = pop();
         var temp = createTemp(indexed.type);
-        if (indexed instanceof Operand.LoadIndexedOperand loadIndexedOperand) {
+        if (indexed instanceof Operand.LoadIndexedOperand loadIndexedOperand)
             codeArrayLoad(loadIndexedOperand, temp);
-        }
-        else if (indexed instanceof Operand.LoadFieldOperand loadFieldOperand) {
+        else if (indexed instanceof Operand.LoadFieldOperand loadFieldOperand)
             codeGetField(loadFieldOperand, temp);
-        }
         else
             codeMove(indexed, temp);
         return temp;
@@ -619,12 +623,10 @@ else if (indexed instanceof Operand.LoadFieldOperand loadFieldOperand) {
     private void codeIndexedStore() {
         Operand value = pop();
         Operand indexed = pop();
-        if (indexed instanceof Operand.LoadIndexedOperand loadIndexedOperand) {
+        if (indexed instanceof Operand.LoadIndexedOperand loadIndexedOperand)
             codeArrayStore(value, loadIndexedOperand);
-        }
-        else if (indexed instanceof Operand.LoadFieldOperand loadFieldOperand) {
+        else if (indexed instanceof Operand.LoadFieldOperand loadFieldOperand)
             codeSetField(value, loadFieldOperand);
-        }
         else
             codeMove(value, indexed);
     }
@@ -638,6 +640,15 @@ else if (type instanceof Type.TypeStruct typeStruct)
             throw new CompilerException("Unexpected type: " + type);
     }
 
+    private void codeStoreAppend() {
+        var operand = issa.read(pop());
+        Operand.RegisterOperand arrayOperand = (Operand.RegisterOperand) issa.read(top());
+        var insn = new Instruction.AStoreAppend(arrayOperand, operand);
+        issa.recordUse(arrayOperand, insn);
+        issa.recordUse(operand, insn);
+        code(insn);
+    }
+
     private void codeNewArray(Type.TypeArray typeArray) {
         var temp = createTemp(typeArray);
         var target = (Operand.RegisterOperand) issa.write(temp);
@@ -654,15 +665,6 @@ private void codeNewStruct(Type.TypeStruct typeStruct) {
         code(insn);
     }
 
-    private void codeStoreAppend() {
-        var operand = issa.read(pop());
-        Operand.RegisterOperand arrayOperand = (Operand.RegisterOperand) issa.read(top());
-        var insn = new Instruction.AStoreAppend(arrayOperand, operand);
-        issa.recordUse(arrayOperand, insn);
-        issa.recordUse(operand, insn);
-        code(insn);
-    }
-
     private void codeArg(Operand.LocalRegisterOperand target) {
         var newtarget = (Operand.RegisterOperand) issa.write(target);
         var insn = new Instruction.ArgInstruction(newtarget);
@@ -774,10 +776,6 @@ private void codeSetField(Operand value, Operand.LoadFieldOperand loadFieldOpera
         code(insn);
     }
 
-    private boolean vstackEmpty() {
-        return virtualStack.isEmpty();
-    }
-
     public StringBuilder toStr(StringBuilder sb, boolean verbose) {
         if (verbose) {
             sb.append(this.functionType.describe()).append("\n");
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/EnterSSA.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/EnterSSA.java
index 13a8556..fc1d275 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/EnterSSA.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/EnterSSA.java
@@ -94,6 +94,8 @@ void insertPhis() {
                 while (b != null) {
                     visited.set(b.bid);
                     for (BasicBlock d: b.dominationFrontier) {
+                        if (d == function.exit) // The exit block does not need any phis as it has no instructions
+                            continue;
                         // insert phi for x in d
                         d.insertPhiFor(x);
                         if (!visited.get(d.bid))
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Options.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Options.java
index b49d9b5..e71988c 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Options.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Options.java
@@ -23,6 +23,7 @@ public enum Options {
 
     public static final EnumSet<Options> NONE = EnumSet.noneOf(Options.class);
     public static final EnumSet<Options> OPT = EnumSet.of(Options.OPTIMIZE,Options.SCCP,Options.CCP,Options.REGALLOC);
+    public static final EnumSet<Options> OPT_ISSA = EnumSet.of(Options.OPTIMIZE,Options.ISSA,Options.SCCP,Options.CCP,Options.REGALLOC);
     public static final EnumSet<Options> VERBOSE = EnumSet.range(DUMP_INITIAL_IR, DUMP_POST_CHAITIN_IR);
     public static final EnumSet<Options> OPT_VERBOSE = EnumSet.range(OPTIMIZE, DUMP_POST_CHAITIN_IR);
 }
diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestIncrementalSSA.java b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestIncrementalSSA.java
index 864cb42..546425d 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestIncrementalSSA.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestIncrementalSSA.java
@@ -1,5 +1,6 @@
 package com.compilerprogramming.ezlang.compiler;
 
+import org.junit.Assert;
 import org.junit.Test;
 
 import java.util.EnumSet;
@@ -7,8 +8,8 @@
 public class TestIncrementalSSA {
     String compileSrc(String src) {
         var compiler = new Compiler();
-        var typeDict = compiler.compileSrc(src, EnumSet.of(Options.ISSA,Options.DUMP_SSA_IR));
-        return compiler.dumpIR(typeDict,true);
+        var typeDict = compiler.compileSrc(src, EnumSet.of(Options.ISSA));
+        return compiler.dumpIR(typeDict, true);
     }
 
     @Test
@@ -23,7 +24,30 @@ func foo(d: Int) {
                 }
                 """;
         String result = compileSrc(src);
-        System.out.println(result);
+        Assert.assertEquals("""
+                func foo(d: Int)
+                Reg #0 d 0
+                Reg #1 a 1
+                Reg #2 b 2
+                Reg #3 c 3
+                Reg #4 %t4 4
+                Reg #5 %t5 5
+                Reg #6 a_1 1
+                Reg #7 %t7 7
+                Reg #8 c_1 3
+                L0:
+                    arg d
+                    a = 42
+                    b = a
+                    %t4 = a+b
+                    c = %t4
+                    %t5 = c+23
+                    a_1 = %t5
+                    %t7 = a_1+d
+                    c_1 = %t7
+                    goto  L1
+                L1:
+                """, result);
     }
 
     @Test
@@ -43,7 +67,33 @@ func foo(d: Int)->Int {
                 }
                 """;
         String result = compileSrc(src);
-        System.out.println(result);
+        Assert.assertEquals("""
+                func foo(d: Int)->Int
+                Reg #0 d 0
+                Reg #1 a 1
+                Reg #2 %t2 2
+                Reg #3 a_1 1
+                Reg #4 %t4 4
+                Reg #5 a_2 1
+                Reg #6 a_3 1
+                L0:
+                    arg d
+                    a = 42
+                    if d goto L2 else goto L3
+                L2:
+                    %t2 = a+1
+                    a_1 = %t2
+                    goto  L4
+                L4:
+                    a_3 = phi(a_1, a_2)
+                    ret a_3
+                    goto  L1
+                L1:
+                L3:
+                    %t4 = a-1
+                    a_2 = %t4
+                    goto  L4
+                """, result);
     }
 
     @Test
@@ -60,7 +110,37 @@ func factorial(num: Int)->Int {
                 }
                 """;
         String result = compileSrc(src);
-        System.out.println(result);
+        Assert.assertEquals("""
+                func factorial(num: Int)->Int
+                Reg #0 num 0
+                Reg #1 result 1
+                Reg #2 %t2 2
+                Reg #3 num_1 0
+                Reg #4 %t4 4
+                Reg #5 result_1 1
+                Reg #6 result_2 1
+                Reg #7 %t7 7
+                Reg #8 num_2 0
+                L0:
+                    arg num
+                    result = 1
+                    goto  L2
+                L2:
+                    result_1 = phi(result, result_2)
+                    num_1 = phi(num, num_2)
+                    %t2 = num_1>1
+                    if %t2 goto L3 else goto L4
+                L3:
+                    %t4 = result_1*num_1
+                    result_2 = %t4
+                    %t7 = num_1-1
+                    num_2 = %t7
+                    goto  L2
+                L4:
+                    ret result_1
+                    goto  L1
+                L1:
+                """, result);
     }
 
 
@@ -102,7 +182,189 @@ func example14_66(p: Int, q: Int, r: Int, s: Int, t: Int) {
                 }
                 """;
         String result = compileSrc(src);
-        System.out.println(result);
+        Assert.assertEquals("""
+                func print(a: Int,b: Int,c: Int,d: Int)
+                Reg #0 a 0
+                Reg #1 b 1
+                Reg #2 c 2
+                Reg #3 d 3
+                L0:
+                    arg a
+                    arg b
+                    arg c
+                    arg d
+                    goto  L1
+                L1:
+                func example14_66(p: Int,q: Int,r: Int,s: Int,t: Int)
+                Reg #0 p 0
+                Reg #1 q 1
+                Reg #2 r 2
+                Reg #3 s 3
+                Reg #4 t 4
+                Reg #5 i 5
+                Reg #6 j 6
+                Reg #7 k 7
+                Reg #8 l 8
+                Reg #9 p_1 0
+                Reg #10 i_1 5
+                Reg #11 j_1 6
+                Reg #12 q_1 1
+                Reg #13 l_1 8
+                Reg #14 l_2 8
+                Reg #15 %t15 15
+                Reg #16 k_1 7
+                Reg #17 k_2 7
+                Reg #18 k_3 7
+                Reg #19 %t19 19
+                Reg #20 k_4 7
+                Reg #21 %t21 21
+                Reg #22 i_2 5
+                Reg #23 i_3 5
+                Reg #24 %t24 24
+                Reg #25 j_2 6
+                Reg #26 j_3 6
+                Reg #27 j_4 6
+                Reg #28 %t28 28
+                Reg #29 k_5 7
+                Reg #30 %t30 30
+                Reg #31 l_3 8
+                Reg #32 l_4 8
+                Reg #33 l_5 8
+                Reg #34 r_1 2
+                Reg #35 %t35 35
+                Reg #36 l_6 8
+                Reg #37 l_7 8
+                Reg #38 %t38 38
+                Reg #39 s_1 3
+                Reg #40 s_2 3
+                Reg #41 r_2 2
+                Reg #42 r_3 2
+                Reg #43 r_4 2
+                Reg #44 r_5 2
+                Reg #45 s_3 3
+                Reg #46 s_4 3
+                Reg #47 s_5 3
+                Reg #48 l_8 8
+                Reg #49 %t49 49
+                Reg #50 i_4 5
+                Reg #51 i_5 5
+                Reg #52 i_6 5
+                Reg #53 i_7 5
+                Reg #54 %t54 54
+                Reg #55 t_1 4
+                Reg #56 t_2 4
+                Reg #57 t_3 4
+                Reg #58 t_4 4
+                Reg #59 t_5 4
+                Reg #60 t_6 4
+                Reg #61 p_2 0
+                Reg #62 p_3 0
+                Reg #63 p_4 0
+                Reg #64 p_5 0
+                Reg #65 p_6 0
+                Reg #66 q_2 1
+                Reg #67 q_3 1
+                Reg #68 q_4 1
+                Reg #69 q_5 1
+                Reg #70 q_6 1
+                Reg #71 r_6 2
+                Reg #72 s_6 3
+                Reg #73 j_5 6
+                Reg #74 j_6 6
+                Reg #75 j_7 6
+                Reg #76 k_6 7
+                Reg #77 k_7 7
+                Reg #78 k_8 7
+                Reg #79 l_9 8
+                L0:
+                    arg p
+                    arg q
+                    arg r
+                    arg s
+                    arg t
+                    i = 1
+                    j = 1
+                    k = 1
+                    l = 1
+                    goto  L2
+                L2:
+                    t_5 = phi(t, t_1)
+                    s_5 = phi(s, s_2)
+                    r_4 = phi(r, r_1)
+                    l_5 = phi(l, l_9)
+                    j_4 = phi(j, j_5)
+                    k_2 = phi(k, k_6)
+                    q_1 = phi(q, q_2)
+                    i_1 = phi(i, i_7)
+                    p_1 = phi(p, p_2)
+                    if 1 goto L3 else goto L4
+                L3:
+                    if p_1 goto L5 else goto L6
+                L5:
+                    j_1 = i_1
+                    if q_1 goto L8 else goto L9
+                L8:
+                    l_1 = 2
+                    goto  L10
+                L10:
+                    l_4 = phi(l_1, l_2)
+                    %t15 = k_2+1
+                    k_3 = %t15
+                    goto  L7
+                L7:
+                    l_3 = phi(l_4, l_5)
+                    k_5 = phi(k_3, k_4)
+                    j_2 = phi(j_1, j_4)
+                    %t21 = i_1
+                    %t24 = j_2
+                    %t28 = k_5
+                    %t30 = l_3
+                    call print params %t21, %t24, %t28, %t30
+                    goto  L11
+                L11:
+                    l_6 = phi(l_3, l_8)
+                    if 1 goto L12 else goto L13
+                L12:
+                    if r_4 goto L14 else goto L15
+                L14:
+                    %t35 = l_6+4
+                    l_7 = %t35
+                    goto  L15
+                L15:
+                    l_8 = phi(l_6, l_7)
+                    %t38 = !s_5
+                    if %t38 goto L16 else goto L17
+                L16:
+                    goto  L13
+                L13:
+                    l_9 = phi(l_6, l_8)
+                    k_6 = phi(k_5, k_7)
+                    j_5 = phi(j_2, j_6)
+                    q_2 = phi(q_1, q_3)
+                    p_2 = phi(p_1, p_3)
+                    t_1 = phi(t_5, t_2)
+                    i_4 = phi(i_1, i_5)
+                    %t49 = i_4+6
+                    i_7 = %t49
+                    %t54 = !t_1
+                    if %t54 goto L18 else goto L19
+                L18:
+                    goto  L4
+                L4:
+                    goto  L1
+                L1:
+                L19:
+                    goto  L2
+                L17:
+                    goto  L11
+                L9:
+                    l_2 = 3
+                    goto  L10
+                L6:
+                    %t19 = k_2+2
+                    k_4 = %t19
+                    goto  L7
+                """, result);
     }
 
     @Test
@@ -127,6 +389,136 @@ func foo()->Int {
                 }
                 """;
         String result = compileSrc(src);
+        Assert.assertEquals("""
+                func fib(n: Int)->Int
+                Reg #0 n 0
+                Reg #1 i 1
+                Reg #2 temp 2
+                Reg #3 f1 3
+                Reg #4 f2 4
+                Reg #5 %t5 5
+                Reg #6 i_1 1
+                Reg #7 %t7 7
+                Reg #8 f1_1 3
+                Reg #9 f2_1 4
+                Reg #10 f1_2 3
+                Reg #11 f2_2 4
+                Reg #12 %t12 12
+                Reg #13 i_2 1
+                L0:
+                    arg n
+                    f1 = 1
+                    f2 = 1
+                    i = n
+                    goto  L2
+                L2:
+                    f2_1 = phi(f2, f2_2)
+                    f1_1 = phi(f1, f1_2)
+                    i_1 = phi(i, i_2)
+                    %t5 = i_1>1
+                    if %t5 goto L3 else goto L4
+                L3:
+                    %t7 = f1_1+f2_1
+                    temp = %t7
+                    f1_2 = f2_1
+                    f2_2 = temp
+                    %t12 = i_1-1
+                    i_2 = %t12
+                    goto  L2
+                L4:
+                    ret f2_1
+                    goto  L1
+                L1:
+                func foo()->Int
+                Reg #0 %t0 0
+                Reg #1 %t1 1
+                L0:
+                    %t0 = 10
+                    %t1 = call fib params %t0
+                    ret %t1
+                    goto  L1
+                L1:
+                """, result);
         System.out.println(result);
     }
+
+    // http://users.csc.calpoly.edu/~akeen/courses/csc431/handouts/references/ssa_example.pdf
+    @Test
+    public void testSSAExample() {
+        String src = """
+                func foo(x: Int, y: Int)->Int {
+                   var sum: Int
+                
+                   if (x >= y)
+                      return 0
+                
+                   sum = 0;
+                   while (x < y) {
+                      if (x / 2 * 2 == x) {
+                         sum = sum + 1
+                      }
+                      x = x + 1
+                   }
+                   return sum
+                }
+                """;
+        String result = compileSrc(src);
+        Assert.assertEquals("""
+func foo(x: Int,y: Int)->Int
+Reg #0 x 0
+Reg #1 y 1
+Reg #2 sum 2
+Reg #3 %t3 3
+Reg #4 %t4 4
+Reg #5 x_1 0
+Reg #6 y_1 1
+Reg #7 %t7 7
+Reg #8 %t8 8
+Reg #9 %t9 9
+Reg #10 %t10 10
+Reg #11 sum_1 2
+Reg #12 sum_2 2
+Reg #13 %t13 13
+Reg #14 x_2 0
+Reg #15 x_3 0
+Reg #16 y_2 1
+Reg #17 sum_3 2
+L0:
+    arg x
+    arg y
+    %t3 = x>=y
+    if %t3 goto L2 else goto L3
+L2:
+    ret 0
+    goto  L1
+L1:
+L3:
+    sum = 0
+    goto  L4
+L4:
+    sum_1 = phi(sum, sum_3)
+    x_1 = phi(x, x_3)
+    %t4 = x_1<y
+    if %t4 goto L5 else goto L6
+L5:
+    %t7 = x_1/2
+    %t8 = %t7*2
+    %t9 = %t8==x_1
+    if %t9 goto L7 else goto L8
+L7:
+    %t10 = sum_1+1
+    sum_2 = %t10
+    goto  L8
+L8:
+    sum_3 = phi(sum_1, sum_2)
+    %t13 = x_1+1
+    x_3 = %t13
+    goto  L4
+L6:
+    ret sum_1
+    goto  L1
+""", result);
+    }
+
+
 }
\ No newline at end of file
diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
index 2a91a12..39aec29 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
@@ -824,7 +824,6 @@ func foo(x: Int) {
     // http://users.csc.calpoly.edu/~akeen/courses/csc431/handouts/references/ssa_example.pdf
     @Test
     public void testSSAExample() {
-        // TODO
         String src = """
 func foo(x: Int, y: Int)->Int {
    var sum: Int
@@ -843,8 +842,117 @@ func foo(x: Int, y: Int)->Int {
 }
                 """;
         String result = compileSrc(src);
-        System.out.println(result);
-
+        Assert.assertEquals("""
+func foo
+Before SSA
+==========
+L0:
+    arg x
+    arg y
+    %t3 = x>=y
+    if %t3 goto L2 else goto L3
+L2:
+    ret 0
+    goto  L1
+L1:
+L3:
+    sum = 0
+    goto  L4
+L4:
+    %t4 = x<y
+    if %t4 goto L5 else goto L6
+L5:
+    %t5 = x/2
+    %t6 = %t5*2
+    %t7 = %t6==x
+    if %t7 goto L7 else goto L8
+L7:
+    %t8 = sum+1
+    sum = %t8
+    goto  L8
+L8:
+    %t9 = x+1
+    x = %t9
+    goto  L4
+L6:
+    ret sum
+    goto  L1
+After SSA
+=========
+L0:
+    arg x_0
+    arg y_0
+    %t3_0 = x_0>=y_0
+    if %t3_0 goto L2 else goto L3
+L2:
+    ret 0
+    goto  L1
+L1:
+L3:
+    sum_0 = 0
+    goto  L4
+L4:
+    sum_1 = phi(sum_0, sum_3)
+    x_1 = phi(x_0, x_2)
+    %t4_0 = x_1<y_0
+    if %t4_0 goto L5 else goto L6
+L5:
+    %t5_0 = x_1/2
+    %t6_0 = %t5_0*2
+    %t7_0 = %t6_0==x_1
+    if %t7_0 goto L7 else goto L8
+L7:
+    %t8_0 = sum_1+1
+    sum_2 = %t8_0
+    goto  L8
+L8:
+    sum_3 = phi(sum_1, sum_2)
+    %t9_0 = x_1+1
+    x_2 = %t9_0
+    goto  L4
+L6:
+    ret sum_1
+    goto  L1
+After exiting SSA
+=================
+L0:
+    arg x_0
+    arg y_0
+    %t3_0 = x_0>=y_0
+    if %t3_0 goto L2 else goto L3
+L2:
+    ret 0
+    goto  L1
+L1:
+L3:
+    sum_0 = 0
+    sum_1 = sum_0
+    x_1 = x_0
+    goto  L4
+L4:
+    %t4_0 = x_1<y_0
+    if %t4_0 goto L5 else goto L6
+L5:
+    %t5_0 = x_1/2
+    %t6_0 = %t5_0*2
+    %t7_0 = %t6_0==x_1
+    sum_3 = sum_1
+    if %t7_0 goto L7 else goto L8
+L7:
+    %t8_0 = sum_1+1
+    sum_2 = %t8_0
+    sum_3 = sum_2
+    goto  L8
+L8:
+    %t9_0 = x_1+1
+    x_2 = %t9_0
+    sum_1 = sum_3
+    x_1 = x_2
+    goto  L4
+L6:
+    ret sum_1
+    goto  L1
+""", result);
     }
 
     @Test
@@ -1069,7 +1177,81 @@ func foo(n: Int)->Int {
                 }
                 """;
         String result = compileSrc(src);
-        System.out.println(result);
+        Assert.assertEquals("""
+func foo
+Before SSA
+==========
+L0:
+    arg n
+    a = 1
+    b = 2
+    goto  L2
+L2:
+    %t4 = n>0
+    if %t4 goto L3 else goto L4
+L3:
+    t = a
+    a = b
+    b = t
+    %t5 = n-1
+    n = %t5
+    goto  L2
+L4:
+    ret a
+    goto  L1
+L1:
+After SSA
+=========
+L0:
+    arg n_0
+    a_0 = 1
+    b_0 = 2
+    goto  L2
+L2:
+    b_1 = phi(b_0, b_2)
+    a_1 = phi(a_0, a_2)
+    n_1 = phi(n_0, n_2)
+    %t4_0 = n_1>0
+    if %t4_0 goto L3 else goto L4
+L3:
+    t_0 = a_1
+    a_2 = b_1
+    b_2 = t_0
+    %t5_0 = n_1-1
+    n_2 = %t5_0
+    goto  L2
+L4:
+    ret a_1
+    goto  L1
+L1:
+After exiting SSA
+=================
+L0:
+    arg n_0
+    a_0 = 1
+    b_0 = 2
+    b_1 = b_0
+    a_1 = a_0
+    n_1 = n_0
+    goto  L2
+L2:
+    %t4_0 = n_1>0
+    if %t4_0 goto L3 else goto L4
+L3:
+    t_0 = a_1
+    a_2 = b_1
+    b_2 = t_0
+    %t5_0 = n_1-1
+    n_2 = %t5_0
+    b_1 = b_2
+    a_1 = a_2
+    n_1 = n_2
+    goto  L2
+L4:
+    ret a_1
+    goto  L1
+L1:
+""", result);
     }
 
     @Test
diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java b/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java
index fcfe0f5..acb583c 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java
@@ -4,15 +4,24 @@
 import com.compilerprogramming.ezlang.compiler.Options;
 import org.junit.Assert;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
 
+import java.util.ArrayList;
+import java.util.Collection;
 import java.util.EnumSet;
+import java.util.List;
 
+@RunWith(Parameterized.class)
 public class TestInterpreter {
 
+    @Parameterized.Parameter
+    public EnumSet<Options> options;
+
     Value compileAndRun(String src, String mainFunction) {
-        return compileAndRun(src, mainFunction, Options.NONE);
+        return compileAndRun(src, mainFunction, options);
     }
-    Value compileAndRun(String src, String mainFunction, EnumSet<Options> options) {
+    Value compileAndRun(String src, String mainFunction, EnumSet<Options> optionsIgnored) {
         //options.add(Options.ISSA);
         var compiler = new Compiler();
         var typeDict = compiler.compileSrc(src, options);
@@ -22,6 +31,16 @@ Value compileAndRun(String src, String mainFunction, EnumSet<Options> options) {
         return interpreter.run(mainFunction);
     }
 
+    @Parameterized.Parameters
+    public static Collection<Object[]> data() {
+        List<Object[]> parameters = new ArrayList<>();
+        parameters.add(new Object[] { Options.NONE });
+        parameters.add(new Object[] { Options.OPT });
+        parameters.add(new Object[] { Options.OPT_ISSA });
+        return parameters;
+    }
+
+
     @Test
     public void testFunction1() {
         String src = """
diff --git a/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java b/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
index 9cd5112..b0849e6 100644
--- a/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
+++ b/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
@@ -176,14 +176,14 @@ private void compileBreak(AST.BreakStmt breakStmt) {
     }
 
     private void compileWhile(AST.WhileStmt whileStmt) {
-        BasicBlock loopBlock = createLoopHead();
+        BasicBlock loopHead = createLoopHead();
         BasicBlock bodyBlock = createBlock();
         BasicBlock exitBlock = createBlock();
         BasicBlock savedBreakTarget = currentBreakTarget;
         BasicBlock savedContinueTarget = currentContinueTarget;
         currentBreakTarget = exitBlock;
-        currentContinueTarget = loopBlock;
-        startBlock(loopBlock);
+        currentContinueTarget = loopHead;
+        startBlock(loopHead);
         boolean indexed = compileExpr(whileStmt.condition);
         if (indexed)
             codeIndexedLoad();
@@ -192,7 +192,7 @@ private void compileWhile(AST.WhileStmt whileStmt) {
         startBlock(bodyBlock);
         compileStatement(whileStmt.stmt);
         if (!isBlockTerminated(currentBlock))
-            jumpTo(loopBlock);
+            jumpTo(loopHead);
         startBlock(exitBlock);
         currentContinueTarget = savedContinueTarget;
         currentBreakTarget = savedBreakTarget;
@@ -217,16 +217,16 @@ private void startBlock(BasicBlock block) {
     }
 
     private void compileIf(AST.IfElseStmt ifElseStmt) {
-        BasicBlock ifBlock = createBlock();
+        BasicBlock thenBlock = createBlock();
         boolean needElse = ifElseStmt.elseStmt != null;
         BasicBlock elseBlock = needElse ? createBlock() : null;
         BasicBlock exitBlock = createBlock();
         boolean indexed = compileExpr(ifElseStmt.condition);
         if (indexed)
             codeIndexedLoad();
-        code(new Instruction.ConditionalBranch(currentBlock, pop(), ifBlock, needElse ? elseBlock : exitBlock));
+        code(new Instruction.ConditionalBranch(currentBlock, pop(), thenBlock, needElse ? elseBlock : exitBlock));
         assert vstackEmpty();
-        startBlock(ifBlock);
+        startBlock(thenBlock);
         compileStatement(ifElseStmt.ifStmt);
         if (!isBlockTerminated(currentBlock))
             jumpTo(exitBlock);
@@ -363,23 +363,6 @@ private boolean compileSetFieldExpr(AST.SetFieldExpr setFieldExpr) {
         return false;
     }
 
-    private void codeNew(Type type) {
-        var temp = createTemp(type);
-        if (type instanceof Type.TypeArray typeArray) {
-            code(new Instruction.NewArray(typeArray, temp));
-        }
-        else if (type instanceof Type.TypeStruct typeStruct) {
-            code(new Instruction.NewStruct(typeStruct, temp));
-        }
-        else
-            throw new CompilerException("Unexpected type: " + type);
-    }
-
-    private void codeStoreAppend() {
-        var operand = pop();
-        code(new Instruction.AStoreAppend((Operand.RegisterOperand) top(), operand));
-    }
-
     private boolean compileNewExpr(AST.NewExpr newExpr) {
         codeNew(newExpr.type);
         if (newExpr.initExprList != null && !newExpr.initExprList.isEmpty()) {
@@ -437,7 +420,6 @@ private boolean codeBoolean(AST.BinaryExpr binaryExpr) {
         return false;
     }
 
-
     private boolean compileBinaryExpr(AST.BinaryExpr binaryExpr) {
         String opCode = binaryExpr.op.str;
         if (opCode.equals("&&") ||
@@ -581,15 +563,17 @@ private Operand top() {
         return virtualStack.getLast();
     }
 
+    private boolean vstackEmpty() {
+        return virtualStack.isEmpty();
+    }
+
     private Operand.TempRegisterOperand codeIndexedLoad() {
         Operand indexed = pop();
         var temp = createTemp(indexed.type);
-        if (indexed instanceof Operand.LoadIndexedOperand loadIndexedOperand) {
+        if (indexed instanceof Operand.LoadIndexedOperand loadIndexedOperand)
             code(new Instruction.ArrayLoad(loadIndexedOperand, temp));
-        }
-        else if (indexed instanceof Operand.LoadFieldOperand loadFieldOperand) {
+        else if (indexed instanceof Operand.LoadFieldOperand loadFieldOperand)
             code(new Instruction.GetField(loadFieldOperand, temp));
-        }
         else
             code(new Instruction.Move(indexed, temp));
         return temp;
@@ -598,17 +582,28 @@ else if (indexed instanceof Operand.LoadFieldOperand loadFieldOperand) {
     private void codeIndexedStore() {
         Operand value = pop();
         Operand indexed = pop();
-        if (indexed instanceof Operand.LoadIndexedOperand loadIndexedOperand) {
+        if (indexed instanceof Operand.LoadIndexedOperand loadIndexedOperand)
             code(new Instruction.ArrayStore(value, loadIndexedOperand));
-        }
-        else if (indexed instanceof Operand.LoadFieldOperand loadFieldOperand) {
+        else if (indexed instanceof Operand.LoadFieldOperand loadFieldOperand)
             code(new Instruction.SetField(value, loadFieldOperand));
-        }
         else
             code(new Instruction.Move(value, indexed));
     }
 
-    private boolean vstackEmpty() {
-        return virtualStack.isEmpty();
+    private void codeNew(Type type) {
+        var temp = createTemp(type);
+        if (type instanceof Type.TypeArray typeArray)
+            code(new Instruction.NewArray(typeArray, temp));
+        else if (type instanceof Type.TypeStruct typeStruct)
+            code(new Instruction.NewStruct(typeStruct, temp));
+        else
+            throw new CompilerException("Unexpected type: " + type);
     }
+
+    private void codeStoreAppend() {
+        var operand = pop();
+        code(new Instruction.AStoreAppend((Operand.RegisterOperand) top(), operand));
+    }
+
+
 }

From 00ea3c0dd24d56a8a45e8c3576bdd7e6500698d4 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <dibyendu@redukti.com>
Date: Sun, 16 Mar 2025 09:27:05 +0000
Subject: [PATCH 042/119] At parse time set a flag on index and field
 expressions when we detect that it is a store opposed to a load

---
 .../com/compilerprogramming/ezlang/parser/AST.java | 10 ++++++++++
 .../compilerprogramming/ezlang/parser/Parser.java  | 14 +++++++++++++-
 2 files changed, 23 insertions(+), 1 deletion(-)

diff --git a/parser/src/main/java/com/compilerprogramming/ezlang/parser/AST.java b/parser/src/main/java/com/compilerprogramming/ezlang/parser/AST.java
index aa21c76..969625d 100644
--- a/parser/src/main/java/com/compilerprogramming/ezlang/parser/AST.java
+++ b/parser/src/main/java/com/compilerprogramming/ezlang/parser/AST.java
@@ -372,9 +372,14 @@ public void accept(ASTVisitor visitor) {
     public static class ArrayIndexExpr extends Expr {
         public final Expr array;
         public final Expr expr;
+        public final boolean loading;
         public ArrayIndexExpr(Expr array, Expr expr) {
+            this(array, expr, true);
+        }
+        public ArrayIndexExpr(Expr array, Expr expr, boolean loading) {
             this.array = array;
             this.expr = expr;
+            this.loading = loading;
         }
         @Override
         public StringBuilder toStr(StringBuilder sb) {
@@ -398,9 +403,14 @@ public void accept(ASTVisitor visitor) {
     public static class FieldExpr extends Expr {
         public final Expr object;
         public final String fieldName;
+        public final boolean loading;
         public FieldExpr(Expr object, String fieldName) {
+            this(object, fieldName, true);
+        }
+        public FieldExpr(Expr object, String fieldName, boolean loading) {
             this.object = object;
             this.fieldName = fieldName;
+            this.loading = loading;
         }
         @Override
         public StringBuilder toStr(StringBuilder sb) {
diff --git a/parser/src/main/java/com/compilerprogramming/ezlang/parser/Parser.java b/parser/src/main/java/com/compilerprogramming/ezlang/parser/Parser.java
index ca9deb9..97f36fc 100644
--- a/parser/src/main/java/com/compilerprogramming/ezlang/parser/Parser.java
+++ b/parser/src/main/java/com/compilerprogramming/ezlang/parser/Parser.java
@@ -246,7 +246,19 @@ private AST.Stmt parseAssign(Lexer lexer) {
         testPunctuation(lexer, ";");
         if (rhs == null)
             return new AST.ExprStmt(lhs);
-        return new AST.AssignStmt(lhs, rhs);
+        return new AST.AssignStmt(storing(lhs), rhs);
+    }
+
+    private AST.Expr storing(AST.Expr lhs) {
+        if (lhs instanceof AST.ArrayIndexExpr arrayIndexExpr &&
+            arrayIndexExpr.loading) {
+            return new AST.ArrayIndexExpr(arrayIndexExpr.array, arrayIndexExpr.expr, false);
+        }
+        else if (lhs instanceof AST.FieldExpr fieldExpr &&
+                fieldExpr.loading) {
+            return new AST.FieldExpr(fieldExpr.object, fieldExpr.fieldName, false);
+        }
+        return lhs;
     }
 
     private AST.Expr parseBool(Lexer lexer) {

From b1522b809bc002cd92fd5516da0fb8c2e941cdd4 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <dibyendu@redukti.com>
Date: Wed, 26 Mar 2025 10:34:26 +0000
Subject: [PATCH 043/119] Part 1

Part 2

Part 3

Part 4

Temp disable stackvm

AST revision to capture load/store array/field info
---
 .../ezlang/compiler/CompiledFunction.java     |  75 +++++----
 .../ezlang/compiler/Instruction.java          |  17 +-
 .../SparseConditionalConstantPropagation.java |   2 -
 .../ezlang/interpreter/Interpreter.java       |  18 +--
 .../ezlang/compiler/TestCompiler.java         |   8 +-
 .../ezlang/compiler/TestSSATransform.java     |  24 +--
 .../ezlang/parser/AST.java                    | 150 +++++++++++++-----
 .../ezlang/parser/ASTVisitor.java             |   8 +-
 .../ezlang/parser/Parser.java                 |  39 +++--
 .../ezlang/parser/TestParser.java             |   3 +
 .../ezlang/compiler/CompiledFunction.java     |  73 +++++----
 .../ezlang/compiler/Instruction.java          |  17 +-
 .../ezlang/interpreter/Interpreter.java       |  18 +--
 .../ezlang/compiler/TestCompiler.java         |   8 +-
 .../ezlang/semantic/SemaAssignTypes.java      |  89 +++++++++--
 .../ezlang/semantic/SemaDefineTypes.java      |  18 ++-
 .../ezlang/compiler/CompiledFunction.java     |  73 +++++----
 .../ezlang/compiler/Instruction.java          |  32 ++--
 .../ezlang/compiler/TestCompiler.java         |  15 +-
 19 files changed, 420 insertions(+), 267 deletions(-)

diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
index 4eb3bf3..5d237e0 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
@@ -309,11 +309,17 @@ private boolean compileExpr(AST.Expr expr) {
             case AST.NewExpr newExpr -> {
                 return compileNewExpr(newExpr);
             }
-            case AST.ArrayIndexExpr arrayIndexExpr -> {
-                return compileArrayIndexExpr(arrayIndexExpr);
+            case AST.InitExpr initExpr -> {
+                return compileInitExpr(initExpr);
             }
-            case AST.FieldExpr fieldExpr -> {
-                return compileFieldExpr(fieldExpr);
+            case AST.ArrayLoadExpr arrayLoadExpr -> {
+                return compileArrayIndexExpr(arrayLoadExpr);
+            }
+            case AST.ArrayStoreExpr arrayStoreExpr -> {
+                return compileArrayStoreExpr(arrayStoreExpr);
+            }
+            case AST.GetFieldExpr getFieldExpr -> {
+                return compileFieldExpr(getFieldExpr);
             }
             case AST.SetFieldExpr setFieldExpr -> {
                 return compileSetFieldExpr(setFieldExpr);
@@ -370,7 +376,7 @@ else if (t instanceof Type.TypeNullable ptr &&
             throw new CompilerException("Unexpected type: " + t);
     }
 
-    private boolean compileFieldExpr(AST.FieldExpr fieldExpr) {
+    private boolean compileFieldExpr(AST.GetFieldExpr fieldExpr) {
         Type.TypeStruct typeStruct = getStructType(fieldExpr.object.type);
         int fieldIndex = typeStruct.getFieldIndex(fieldExpr.fieldName);
         if (fieldIndex < 0)
@@ -382,7 +388,7 @@ private boolean compileFieldExpr(AST.FieldExpr fieldExpr) {
         return true;
     }
 
-    private boolean compileArrayIndexExpr(AST.ArrayIndexExpr arrayIndexExpr) {
+    private boolean compileArrayIndexExpr(AST.ArrayLoadExpr arrayIndexExpr) {
         compileExpr(arrayIndexExpr.array);
         boolean indexed = compileExpr(arrayIndexExpr.expr);
         if (indexed)
@@ -394,11 +400,15 @@ private boolean compileArrayIndexExpr(AST.ArrayIndexExpr arrayIndexExpr) {
     }
 
     private boolean compileSetFieldExpr(AST.SetFieldExpr setFieldExpr) {
-        Type.TypeStruct structType = (Type.TypeStruct) setFieldExpr.objectType;
+        Type.TypeStruct structType = (Type.TypeStruct) setFieldExpr.object.type;
         int fieldIndex = structType.getFieldIndex(setFieldExpr.fieldName);
         if (fieldIndex == -1)
             throw new CompilerException("Field " + setFieldExpr.fieldName + " not found in struct " + structType.name);
-        pushOperand(new Operand.LoadFieldOperand(top(), setFieldExpr.fieldName, fieldIndex));
+        if (setFieldExpr instanceof AST.InitFieldExpr)
+            pushOperand(top());
+        else
+            compileExpr(setFieldExpr.object);
+        pushOperand(new Operand.LoadFieldOperand(pop(), setFieldExpr.fieldName, fieldIndex));
         boolean indexed = compileExpr(setFieldExpr.value);
         if (indexed)
             codeIndexedLoad();
@@ -406,22 +416,34 @@ private boolean compileSetFieldExpr(AST.SetFieldExpr setFieldExpr) {
         return false;
     }
 
+    private boolean compileArrayStoreExpr(AST.ArrayStoreExpr arrayStoreExpr) {
+        if (arrayStoreExpr instanceof AST.ArrayInitExpr)
+            pushOperand(top()); // Array was created by new
+        else
+            compileExpr(arrayStoreExpr.array);
+        boolean indexed = compileExpr(arrayStoreExpr.expr);
+        if (indexed)
+            codeIndexedLoad();
+        Operand index = pop();
+        Operand array = pop();
+        pushOperand(new Operand.LoadIndexedOperand(array, index));
+        indexed = compileExpr(arrayStoreExpr.value);
+        if (indexed)
+            codeIndexedLoad();
+        codeIndexedStore();
+        return false;
+    }
+
     private boolean compileNewExpr(AST.NewExpr newExpr) {
         codeNew(newExpr.type);
-        if (newExpr.initExprList != null && !newExpr.initExprList.isEmpty()) {
-            if (newExpr.type instanceof Type.TypeArray) {
-                for (AST.Expr expr : newExpr.initExprList) {
-                    // Maybe have specific AST similar to how we have SetFieldExpr?
-                    boolean indexed = compileExpr(expr);
-                    if (indexed)
-                        codeIndexedLoad();
-                    codeStoreAppend();
-                }
-            }
-            else if (newExpr.type instanceof Type.TypeStruct) {
-                for (AST.Expr expr : newExpr.initExprList) {
-                    compileExpr(expr);
-                }
+        return false;
+    }
+
+    private boolean compileInitExpr(AST.InitExpr initExpr) {
+        compileExpr(initExpr.newExpr);
+        if (initExpr.initExprList != null && !initExpr.initExprList.isEmpty()) {
+            for (AST.Expr expr : initExpr.initExprList) {
+                compileExpr(expr);
             }
         }
         return false;
@@ -640,15 +662,6 @@ else if (type instanceof Type.TypeStruct typeStruct)
             throw new CompilerException("Unexpected type: " + type);
     }
 
-    private void codeStoreAppend() {
-        var operand = issa.read(pop());
-        Operand.RegisterOperand arrayOperand = (Operand.RegisterOperand) issa.read(top());
-        var insn = new Instruction.AStoreAppend(arrayOperand, operand);
-        issa.recordUse(arrayOperand, insn);
-        issa.recordUse(operand, insn);
-        code(insn);
-    }
-
     private void codeNewArray(Type.TypeArray typeArray) {
         var temp = createTemp(typeArray);
         var target = (Operand.RegisterOperand) issa.write(temp);
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java
index 7bb016b..0ab1203 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java
@@ -22,9 +22,8 @@ public abstract class Instruction {
     static final int I_NEW_STRUCT = 11;
     static final int I_ARRAY_STORE = 12;
     static final int I_ARRAY_LOAD = 13;
-    static final int I_ARRAY_APPEND = 14;
-    static final int I_FIELD_GET = 15;
-    static final int I_FIELD_SET = 16;
+    static final int I_FIELD_GET = 14;
+    static final int I_FIELD_SET = 15;
 
     public final int opcode;
     protected Operand.RegisterOperand def;
@@ -271,18 +270,6 @@ public StringBuilder toStr(StringBuilder sb) {
         }
     }
 
-    public static class AStoreAppend extends Instruction {
-        public AStoreAppend(Operand.RegisterOperand array, Operand value) {
-            super(I_ARRAY_APPEND, (Operand.RegisterOperand) null, array, value);
-        }
-        public Operand.RegisterOperand array() { return (Operand.RegisterOperand) uses[0]; }
-        public Operand value() { return uses[1]; }
-        @Override
-        public StringBuilder toStr(StringBuilder sb) {
-            return sb.append(uses[0]).append(".append(").append(uses[1]).append(")");
-        }
-    }
-
     public static class ConditionalBranch extends Instruction {
         public final BasicBlock trueBlock;
         public final BasicBlock falseBlock;
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SparseConditionalConstantPropagation.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SparseConditionalConstantPropagation.java
index c83ee31..38f0bc6 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SparseConditionalConstantPropagation.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SparseConditionalConstantPropagation.java
@@ -421,8 +421,6 @@ else if (binaryInst.right() instanceof Operand.RegisterOperand registerOperand)
                 var cell = valueLattice.get(newStructInst.destOperand().reg);
                 changed = cell.setKind(V_VARYING);
             }
-            case Instruction.AStoreAppend arrayAppendInst -> {
-            }
             case Instruction.ArrayStore arrayStoreInst -> {
             }
             case Instruction.ArrayLoad arrayLoadInst -> {
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/interpreter/Interpreter.java b/optvm/src/main/java/com/compilerprogramming/ezlang/interpreter/Interpreter.java
index d59e1f0..66de388 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/interpreter/Interpreter.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/interpreter/Interpreter.java
@@ -205,19 +205,6 @@ else if (binaryInst.right() instanceof Operand.RegisterOperand registerOperand)
                 case Instruction.NewStruct newStructInst -> {
                     execStack.stack[base + newStructInst.destOperand().frameSlot()] = new Value.StructValue(newStructInst.type);
                 }
-                case Instruction.AStoreAppend arrayAppendInst -> {
-                    Value.ArrayValue arrayValue = (Value.ArrayValue) execStack.stack[base + arrayAppendInst.array().frameSlot()];
-                    if (arrayAppendInst.value() instanceof Operand.ConstantOperand constant) {
-                        arrayValue.values.add(new Value.IntegerValue(constant.value));
-                    }
-                    else if (arrayAppendInst.value() instanceof Operand.NullConstantOperand) {
-                        arrayValue.values.add(new Value.NullValue());
-                    }
-                    else if (arrayAppendInst.value() instanceof Operand.RegisterOperand registerOperand) {
-                        arrayValue.values.add(execStack.stack[base + registerOperand.frameSlot()]);
-                    }
-                    else throw new IllegalStateException();
-                }
                 case Instruction.ArrayStore arrayStoreInst -> {
                     if (arrayStoreInst.arrayOperand() instanceof Operand.RegisterOperand arrayOperand) {
                         Value.ArrayValue arrayValue = (Value.ArrayValue) execStack.stack[base + arrayOperand.frameSlot()];
@@ -241,7 +228,10 @@ else if (arrayStoreInst.sourceOperand() instanceof Operand.RegisterOperand regis
                             value = execStack.stack[base + registerOperand.frameSlot()];
                         }
                         else throw new IllegalStateException();
-                        arrayValue.values.set(index, value);
+                        if (index == arrayValue.values.size())
+                            arrayValue.values.add(value);
+                        else
+                            arrayValue.values.set(index, value);
                     } else throw new IllegalStateException();
                 }
                 case Instruction.ArrayLoad arrayLoadInst -> {
diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestCompiler.java b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestCompiler.java
index fa148b6..b6873ec 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestCompiler.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestCompiler.java
@@ -215,9 +215,9 @@ func foo()->[Int] {
         Assert.assertEquals("""
 L0:
     %t0 = New([Int])
-    %t0.append(1)
-    %t0.append(2)
-    %t0.append(3)
+    %t0[0] = 1
+    %t0[1] = 2
+    %t0[2] = 3
     ret %t0
     goto  L1
 L1:
@@ -236,7 +236,7 @@ func foo(n: Int) -> [Int] {
 L0:
     arg n
     %t1 = New([Int])
-    %t1.append(n)
+    %t1[0] = n
     ret %t1
     goto  L1
 L1:
diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
index 39aec29..a605c93 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
@@ -1602,8 +1602,8 @@ func foo()->Int {
 ==========
 L0:
     %t2 = New([Int])
-    %t2.append(1)
-    %t2.append(2)
+    %t2[0] = 1
+    %t2[1] = 2
     arr = %t2
     arr[0] = 10
     %t3 = arr[0]
@@ -1615,8 +1615,8 @@ func foo()->Int {
 =========
 L0:
     %t2_0 = New([Int])
-    %t2_0.append(1)
-    %t2_0.append(2)
+    %t2_0[0] = 1
+    %t2_0[1] = 2
     arr_0 = %t2_0
     arr_0[0] = 10
     %t3_0 = arr_0[0]
@@ -1628,8 +1628,8 @@ func foo()->Int {
 =================
 L0:
     %t2_0 = New([Int])
-    %t2_0.append(1)
-    %t2_0.append(2)
+    %t2_0[0] = 1
+    %t2_0[1] = 2
     arr_0 = %t2_0
     arr_0[0] = 10
     %t3_0 = arr_0[0]
@@ -2738,8 +2738,8 @@ func foo()->Int
     %t1 = New([Foo?])
     %t2 = New(Foo)
     %t2.i = 1
-    %t1.append(%t2)
-    %t1.append(null)
+    %t1[0] = %t2
+    %t1[1] = null
     f = %t1
     %t3 = f[1]
     %t4 = null==%t3
@@ -2762,8 +2762,8 @@ func foo()->Int
     %t1_0 = New([Foo?])
     %t2_0 = New(Foo)
     %t2_0.i = 1
-    %t1_0.append(%t2_0)
-    %t1_0.append(null)
+    %t1_0[0] = %t2_0
+    %t1_0[1] = null
     f_0 = %t1_0
     %t3_0 = f_0[1]
     %t4_0 = null==%t3_0
@@ -2787,8 +2787,8 @@ func foo()->Int
     %t1_0 = New([Foo?])
     %t2_0 = New(Foo)
     %t2_0.i = 1
-    %t1_0.append(%t2_0)
-    %t1_0.append(null)
+    %t1_0[0] = %t2_0
+    %t1_0[1] = null
     f_0 = %t1_0
     %t3_0 = f_0[1]
     %t4_0 = null==%t3_0
diff --git a/parser/src/main/java/com/compilerprogramming/ezlang/parser/AST.java b/parser/src/main/java/com/compilerprogramming/ezlang/parser/AST.java
index 969625d..1fbb4f7 100644
--- a/parser/src/main/java/com/compilerprogramming/ezlang/parser/AST.java
+++ b/parser/src/main/java/com/compilerprogramming/ezlang/parser/AST.java
@@ -137,7 +137,12 @@ public StringBuilder toStr(StringBuilder sb) {
                 }
                 args[i].toStr(sb);
             }
-            sb.append(")\n");
+            sb.append(")");
+            if (returnType != null) {
+                sb.append("->");
+                returnType.toStr(sb);
+            }
+            sb.append("\n");
             return block.toStr(sb);
         }
 
@@ -368,18 +373,13 @@ public void accept(ASTVisitor visitor) {
         }
     }
 
-    // Array Index
-    public static class ArrayIndexExpr extends Expr {
+    // array[index]
+    public static class ArrayLoadExpr extends Expr {
         public final Expr array;
         public final Expr expr;
-        public final boolean loading;
-        public ArrayIndexExpr(Expr array, Expr expr) {
-            this(array, expr, true);
-        }
-        public ArrayIndexExpr(Expr array, Expr expr, boolean loading) {
+        public ArrayLoadExpr(Expr array, Expr expr) {
             this.array = array;
             this.expr = expr;
-            this.loading = loading;
         }
         @Override
         public StringBuilder toStr(StringBuilder sb) {
@@ -400,17 +400,55 @@ public void accept(ASTVisitor visitor) {
         }
     }
 
-    public static class FieldExpr extends Expr {
+    // array[index] = value
+    public static class ArrayStoreExpr extends Expr {
+        public final Expr array;
+        public final Expr expr;
+        public final Expr value;
+        public ArrayStoreExpr(Expr array, Expr expr, Expr value) {
+            this.array = array;
+            this.expr = expr;
+            this.value = value;
+        }
+        @Override
+        public StringBuilder toStr(StringBuilder sb) {
+            array.toStr(sb);
+            sb.append("[");
+            expr.toStr(sb);
+            return sb.append("]").append("=").append(value);
+        }
+        @Override
+        public void accept(ASTVisitor visitor) {
+            visitor = visitor.visit(this, true);
+            if (visitor == null)
+                return;
+            array.accept(visitor);
+            expr.accept(visitor);
+            value.accept(visitor);
+            visitor.visit(this, false);
+        }
+    }
+
+    /**
+     * Used inside InitExpr
+     * Specializes how we display
+     */
+    public static class ArrayInitExpr extends ArrayStoreExpr {
+        public ArrayInitExpr(Expr array, Expr expr, Expr value) {
+            super(array, expr, value);
+        }
+        @Override
+        public StringBuilder toStr(StringBuilder sb) {
+            return sb.append(value);
+        }
+    }
+
+    public static class GetFieldExpr extends Expr {
         public final Expr object;
         public final String fieldName;
-        public final boolean loading;
-        public FieldExpr(Expr object, String fieldName) {
-            this(object, fieldName, true);
-        }
-        public FieldExpr(Expr object, String fieldName, boolean loading) {
+        public GetFieldExpr(Expr object, String fieldName) {
             this.object = object;
             this.fieldName = fieldName;
-            this.loading = loading;
         }
         @Override
         public StringBuilder toStr(StringBuilder sb) {
@@ -428,6 +466,45 @@ public void accept(ASTVisitor visitor) {
         }
     }
 
+    public static class SetFieldExpr extends Expr {
+        public final Expr object;
+        public final String fieldName;
+        public final Expr value;
+        public SetFieldExpr(Expr object, String fieldName, Expr value) {
+            this.object = object;
+            this.fieldName = fieldName;
+            this.value = value;
+        }
+        @Override
+        public StringBuilder toStr(StringBuilder sb) {
+            object.toStr(sb);
+            return sb.append(".").append(fieldName).append("=").append(value);
+        }
+        @Override
+        public void accept(ASTVisitor visitor) {
+            visitor = visitor.visit(this, true);
+            if (visitor == null)
+                return;
+            object.accept(visitor);
+            value.accept(visitor);
+            visitor.visit(this, false);
+        }
+    }
+
+    /**
+     * Used inside InitExpr
+     * Specializes how we display
+     */
+    public static class InitFieldExpr extends SetFieldExpr {
+        public InitFieldExpr(Expr object, String fieldName, Expr value) {
+            super(object, fieldName, value);
+        }
+        @Override
+        public StringBuilder toStr(StringBuilder sb) {
+            return sb.append(fieldName).append("=").append(value);
+        }
+    }
+
     public static class CallExpr extends Expr {
         public final Expr callee;
         public final List<Expr> args;
@@ -462,44 +539,45 @@ public void accept(ASTVisitor visitor) {
         }
     }
 
-    public static class SetFieldExpr extends Expr {
-        public final String fieldName;
-        public final AST.Expr value;
-        public Type objectType;
-        public SetFieldExpr(String fieldName, Expr value) {
-            this.fieldName = fieldName;
-            this.value = value;
+    /**
+     * new T
+     * result type is T
+     */
+    public static class NewExpr extends Expr {
+        public final TypeExpr typeExpr;
+        public NewExpr(TypeExpr typeExpr) {
+            this.typeExpr = typeExpr;
         }
         @Override
         public StringBuilder toStr(StringBuilder sb) {
-            if (fieldName != null) {
-                sb.append(fieldName).append("=");
-            }
-            value.toStr(sb);
+            sb.append("new ");
+            typeExpr.toStr(sb);
             return sb;
         }
-
         @Override
         public void accept(ASTVisitor visitor) {
             visitor = visitor.visit(this, true);
             if (visitor == null)
                 return;
-            value.accept(visitor);
+            typeExpr.accept(visitor);
             visitor.visit(this, false);
         }
     }
 
-    public static class NewExpr extends Expr {
-        public final TypeExpr typeExpr;
+    /**
+     * new T { initializers }
+     * result type is T
+     */
+    public static class InitExpr extends Expr {
+        public final NewExpr newExpr;
         public final List<Expr> initExprList;
-        public NewExpr(TypeExpr typeExpr, List<Expr> initExprList) {
-            this.typeExpr = typeExpr;
+        public InitExpr(NewExpr newExpr, List<Expr> initExprList) {
+            this.newExpr = newExpr;
             this.initExprList = initExprList;
         }
         @Override
         public StringBuilder toStr(StringBuilder sb) {
-            sb.append("new ");
-            typeExpr.toStr(sb);
+            newExpr.toStr(sb);
             sb.append("{");
             boolean first = true;
             for (Expr expr: initExprList) {
@@ -517,7 +595,7 @@ public void accept(ASTVisitor visitor) {
             visitor = visitor.visit(this, true);
             if (visitor == null)
                 return;
-            typeExpr.accept(visitor);
+            newExpr.accept(visitor);
             for (Expr expr: initExprList) {
                 expr.accept(visitor);
             }
diff --git a/parser/src/main/java/com/compilerprogramming/ezlang/parser/ASTVisitor.java b/parser/src/main/java/com/compilerprogramming/ezlang/parser/ASTVisitor.java
index adf901a..d12f0dc 100644
--- a/parser/src/main/java/com/compilerprogramming/ezlang/parser/ASTVisitor.java
+++ b/parser/src/main/java/com/compilerprogramming/ezlang/parser/ASTVisitor.java
@@ -7,17 +7,19 @@ public interface ASTVisitor {
     ASTVisitor visit(AST.VarDecl varDecl, boolean enter);
     ASTVisitor visit(AST.BinaryExpr binaryExpr, boolean enter);
     ASTVisitor visit(AST.UnaryExpr unaryExpr, boolean enter);
-    ASTVisitor visit(AST.FieldExpr fieldExpr, boolean enter);
+    ASTVisitor visit(AST.GetFieldExpr fieldExpr, boolean enter);
+    ASTVisitor visit(AST.SetFieldExpr fieldExpr, boolean enter);
     ASTVisitor visit(AST.CallExpr callExpr, boolean enter);
-    ASTVisitor visit(AST.SetFieldExpr setFieldExpr, boolean enter);
     ASTVisitor visit(AST.SimpleTypeExpr simpleTypeExpr, boolean enter);
     ASTVisitor visit(AST.NullableSimpleTypeExpr simpleTypeExpr, boolean enter);
     ASTVisitor visit(AST.ArrayTypeExpr arrayTypeExpr, boolean enter);
     ASTVisitor visit(AST.NullableArrayTypeExpr arrayTypeExpr, boolean enter);
     ASTVisitor visit(AST.ReturnTypeExpr returnTypeExpr, boolean enter);
     ASTVisitor visit(AST.LiteralExpr literalExpr, boolean enter);
-    ASTVisitor visit(AST.ArrayIndexExpr arrayIndexExpr, boolean enter);
+    ASTVisitor visit(AST.ArrayLoadExpr arrayLoadExpr, boolean enter);
+    ASTVisitor visit(AST.ArrayStoreExpr arrayStoreExpr, boolean enter);
     ASTVisitor visit(AST.NewExpr newExpr, boolean enter);
+    ASTVisitor visit(AST.InitExpr initExpr, boolean enter);
     ASTVisitor visit(AST.NameExpr nameExpr, boolean enter);
     ASTVisitor visit(AST.BreakStmt breakStmt, boolean enter);
     ASTVisitor visit(AST.ContinueStmt continueStmt, boolean enter);
diff --git a/parser/src/main/java/com/compilerprogramming/ezlang/parser/Parser.java b/parser/src/main/java/com/compilerprogramming/ezlang/parser/Parser.java
index 97f36fc..f7b4951 100644
--- a/parser/src/main/java/com/compilerprogramming/ezlang/parser/Parser.java
+++ b/parser/src/main/java/com/compilerprogramming/ezlang/parser/Parser.java
@@ -246,19 +246,15 @@ private AST.Stmt parseAssign(Lexer lexer) {
         testPunctuation(lexer, ";");
         if (rhs == null)
             return new AST.ExprStmt(lhs);
-        return new AST.AssignStmt(storing(lhs), rhs);
-    }
-
-    private AST.Expr storing(AST.Expr lhs) {
-        if (lhs instanceof AST.ArrayIndexExpr arrayIndexExpr &&
-            arrayIndexExpr.loading) {
-            return new AST.ArrayIndexExpr(arrayIndexExpr.array, arrayIndexExpr.expr, false);
-        }
-        else if (lhs instanceof AST.FieldExpr fieldExpr &&
-                fieldExpr.loading) {
-            return new AST.FieldExpr(fieldExpr.object, fieldExpr.fieldName, false);
+        else {
+            if (lhs instanceof AST.ArrayLoadExpr arrayLoadExpr) {
+                return new AST.ExprStmt(new AST.ArrayStoreExpr(arrayLoadExpr.array, arrayLoadExpr.expr, rhs));
+            }
+            else if (lhs instanceof AST.GetFieldExpr getFieldExpr) {
+                return new AST.ExprStmt(new AST.SetFieldExpr(getFieldExpr.object, getFieldExpr.fieldName, rhs));
+            }
         }
-        return lhs;
+        return new AST.AssignStmt(lhs, rhs);
     }
 
     private AST.Expr parseBool(Lexer lexer) {
@@ -333,18 +329,27 @@ private AST.Expr parseUnary(Lexer lexer) {
     private AST.Expr parseNew(Lexer lexer) {
         matchIdentifier(lexer, "new");
         AST.TypeExpr resultType = parseTypeExpr(lexer);
+        var newExpr = new AST.NewExpr(resultType);
         List<AST.Expr> initExpr = new ArrayList<>();
+        int initType = 0;
+        int index = 0;
         if (testPunctuation(lexer, "{")) {
             while (!isToken(currentToken, "}")) {
                 if (currentToken.kind == Token.Kind.IDENT && lexer.peekChar() == '=') {
+                    if (initType == 0) initType = 1;
+                    else if (initType != 1) throw new CompilerException("Cannot mix initializer expressions");
                     String fieldname = currentToken.str;
                     nextToken(lexer);
                     matchPunctuation(lexer, "=");
                     AST.Expr value = parseBool(lexer);
-                    initExpr.add(new AST.SetFieldExpr(fieldname, value));
+                    initExpr.add(new AST.InitFieldExpr(newExpr, fieldname, value));
                 }
                 else {
-                    initExpr.add(parseBool(lexer));
+                    if (initType == 0) initType = 2;
+                    else if (initType != 2) throw new CompilerException("Cannot mix initializer expressions");
+                    var indexLit = Integer.valueOf(index++);
+                    var indexExpr = new AST.LiteralExpr(Token.newNum(indexLit,indexLit.toString(),0));
+                    initExpr.add(new AST.ArrayInitExpr(newExpr, indexExpr, parseBool(lexer)));
                 }
                 if (isToken(currentToken, ","))
                     nextToken(lexer);
@@ -352,7 +357,7 @@ private AST.Expr parseNew(Lexer lexer) {
             }
         }
         matchPunctuation(lexer, "}");
-        return new AST.NewExpr(resultType, initExpr);
+        return new AST.InitExpr(newExpr, initExpr);
     }
 
     private AST.Expr parsePrimary(Lexer lexer) {
@@ -401,12 +406,12 @@ private AST.Expr parsePostfix(Lexer lexer, AST.Expr primaryExpr) {
             switch (tok.str) {
                 case "[" -> {
                     AST.Expr expr = parseBool(lexer);
-                    prevExpr = new AST.ArrayIndexExpr(prevExpr, expr);
+                    prevExpr = new AST.ArrayLoadExpr(prevExpr, expr);
                     matchPunctuation(lexer, "]");
                 }
                 case "." -> {
                     if (currentToken.kind == Token.Kind.IDENT) {
-                        prevExpr = new AST.FieldExpr(prevExpr, currentToken.str);
+                        prevExpr = new AST.GetFieldExpr(prevExpr, currentToken.str);
                         nextToken(lexer);
                     }
                     else
diff --git a/parser/src/test/java/com/compilerprogramming/ezlang/parser/TestParser.java b/parser/src/test/java/com/compilerprogramming/ezlang/parser/TestParser.java
index c98f54c..101c75d 100644
--- a/parser/src/test/java/com/compilerprogramming/ezlang/parser/TestParser.java
+++ b/parser/src/test/java/com/compilerprogramming/ezlang/parser/TestParser.java
@@ -36,6 +36,9 @@ func bar() -> Test {
 func main() {
     var m = 42
     var t: Tree
+    var array = new [Int] {1,2,3}
+    array[1] = 42
+    t.left = null
     if (m < 1)
        print(1)
     else if (m == 5)
diff --git a/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java b/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
index b0849e6..0e25924 100644
--- a/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
+++ b/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
@@ -265,11 +265,17 @@ private boolean compileExpr(AST.Expr expr) {
             case AST.NewExpr newExpr -> {
                 return compileNewExpr(newExpr);
             }
-            case AST.ArrayIndexExpr arrayIndexExpr -> {
-                return compileArrayIndexExpr(arrayIndexExpr);
+            case AST.InitExpr initExpr -> {
+                return compileInitExpr(initExpr);
             }
-            case AST.FieldExpr fieldExpr -> {
-                return compileFieldExpr(fieldExpr);
+            case AST.ArrayLoadExpr arrayLoadExpr -> {
+                return compileArrayIndexExpr(arrayLoadExpr);
+            }
+            case AST.ArrayStoreExpr arrayStoreExpr -> {
+                return compileArrayStoreExpr(arrayStoreExpr);
+            }
+            case AST.GetFieldExpr getFieldExpr -> {
+                return compileFieldExpr(getFieldExpr);
             }
             case AST.SetFieldExpr setFieldExpr -> {
                 return compileSetFieldExpr(setFieldExpr);
@@ -327,7 +333,7 @@ else if (t instanceof Type.TypeNullable ptr &&
             throw new CompilerException("Unexpected type: " + t);
     }
 
-    private boolean compileFieldExpr(AST.FieldExpr fieldExpr) {
+    private boolean compileFieldExpr(AST.GetFieldExpr fieldExpr) {
         Type.TypeStruct typeStruct = getStructType(fieldExpr.object.type);
         int fieldIndex = typeStruct.getFieldIndex(fieldExpr.fieldName);
         if (fieldIndex < 0)
@@ -339,7 +345,7 @@ private boolean compileFieldExpr(AST.FieldExpr fieldExpr) {
         return true;
     }
 
-    private boolean compileArrayIndexExpr(AST.ArrayIndexExpr arrayIndexExpr) {
+    private boolean compileArrayIndexExpr(AST.ArrayLoadExpr arrayIndexExpr) {
         compileExpr(arrayIndexExpr.array);
         boolean indexed = compileExpr(arrayIndexExpr.expr);
         if (indexed)
@@ -351,11 +357,15 @@ private boolean compileArrayIndexExpr(AST.ArrayIndexExpr arrayIndexExpr) {
     }
 
     private boolean compileSetFieldExpr(AST.SetFieldExpr setFieldExpr) {
-        Type.TypeStruct structType = (Type.TypeStruct) setFieldExpr.objectType;
+        Type.TypeStruct structType = (Type.TypeStruct) setFieldExpr.object.type;
         int fieldIndex = structType.getFieldIndex(setFieldExpr.fieldName);
         if (fieldIndex == -1)
             throw new CompilerException("Field " + setFieldExpr.fieldName + " not found in struct " + structType.name);
-        pushOperand(new Operand.LoadFieldOperand(top(), setFieldExpr.fieldName, fieldIndex));
+        if (setFieldExpr instanceof AST.InitFieldExpr)
+            pushOperand(top());
+        else
+            compileExpr(setFieldExpr.object);
+        pushOperand(new Operand.LoadFieldOperand(pop(), setFieldExpr.fieldName, fieldIndex));
         boolean indexed = compileExpr(setFieldExpr.value);
         if (indexed)
             codeIndexedLoad();
@@ -363,22 +373,34 @@ private boolean compileSetFieldExpr(AST.SetFieldExpr setFieldExpr) {
         return false;
     }
 
+    private boolean compileArrayStoreExpr(AST.ArrayStoreExpr arrayStoreExpr) {
+        if (arrayStoreExpr instanceof AST.ArrayInitExpr)
+            pushOperand(top()); // Array was created by new
+        else
+            compileExpr(arrayStoreExpr.array);
+        boolean indexed = compileExpr(arrayStoreExpr.expr);
+        if (indexed)
+            codeIndexedLoad();
+        Operand index = pop();
+        Operand array = pop();
+        pushOperand(new Operand.LoadIndexedOperand(array, index));
+        indexed = compileExpr(arrayStoreExpr.value);
+        if (indexed)
+            codeIndexedLoad();
+        codeIndexedStore();
+        return false;
+    }
+
     private boolean compileNewExpr(AST.NewExpr newExpr) {
         codeNew(newExpr.type);
-        if (newExpr.initExprList != null && !newExpr.initExprList.isEmpty()) {
-            if (newExpr.type instanceof Type.TypeArray) {
-                for (AST.Expr expr : newExpr.initExprList) {
-                    // Maybe have specific AST similar to how we have SetFieldExpr?
-                    boolean indexed = compileExpr(expr);
-                    if (indexed)
-                        codeIndexedLoad();
-                    codeStoreAppend();
-                }
-            }
-            else if (newExpr.type instanceof Type.TypeStruct) {
-                for (AST.Expr expr : newExpr.initExprList) {
-                    compileExpr(expr);
-                }
+        return false;
+    }
+
+    private boolean compileInitExpr(AST.InitExpr initExpr) {
+        compileExpr(initExpr.newExpr);
+        if (initExpr.initExprList != null && !initExpr.initExprList.isEmpty()) {
+            for (AST.Expr expr : initExpr.initExprList) {
+                compileExpr(expr);
             }
         }
         return false;
@@ -599,11 +621,4 @@ else if (type instanceof Type.TypeStruct typeStruct)
         else
             throw new CompilerException("Unexpected type: " + type);
     }
-
-    private void codeStoreAppend() {
-        var operand = pop();
-        code(new Instruction.AStoreAppend((Operand.RegisterOperand) top(), operand));
-    }
-
-
 }
diff --git a/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java b/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java
index 35c1fda..a80169a 100644
--- a/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java
+++ b/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java
@@ -18,9 +18,8 @@ public abstract class Instruction {
     static final int I_NEW_STRUCT = 11;
     static final int I_ARRAY_STORE = 12;
     static final int I_ARRAY_LOAD = 13;
-    static final int I_ARRAY_APPEND = 14;
-    static final int I_FIELD_GET = 15;
-    static final int I_FIELD_SET = 16;
+    static final int I_FIELD_GET = 14;
+    static final int I_FIELD_SET = 15;
 
     public final int opcode;
     protected Operand.RegisterOperand def;
@@ -209,18 +208,6 @@ public StringBuilder toStr(StringBuilder sb) {
         }
     }
 
-    public static class AStoreAppend extends Instruction {
-        public AStoreAppend(Operand.RegisterOperand array, Operand value) {
-            super(I_ARRAY_APPEND, (Operand.RegisterOperand) null, array, value);
-        }
-        public Operand.RegisterOperand array() { return (Operand.RegisterOperand) uses[0]; }
-        public Operand value() { return uses[1]; }
-        @Override
-        public StringBuilder toStr(StringBuilder sb) {
-            return sb.append(uses[0]).append(".append(").append(uses[1]).append(")");
-        }
-    }
-
     public static class ConditionalBranch extends Instruction {
         public final BasicBlock trueBlock;
         public final BasicBlock falseBlock;
diff --git a/registervm/src/main/java/com/compilerprogramming/ezlang/interpreter/Interpreter.java b/registervm/src/main/java/com/compilerprogramming/ezlang/interpreter/Interpreter.java
index 5ceaeaa..7771700 100644
--- a/registervm/src/main/java/com/compilerprogramming/ezlang/interpreter/Interpreter.java
+++ b/registervm/src/main/java/com/compilerprogramming/ezlang/interpreter/Interpreter.java
@@ -205,19 +205,6 @@ else if (binaryInst.right() instanceof Operand.RegisterOperand registerOperand)
                 case Instruction.NewStruct newStructInst -> {
                     execStack.stack[base + newStructInst.destOperand().frameSlot()] = new Value.StructValue(newStructInst.type);
                 }
-                case Instruction.AStoreAppend arrayAppendInst -> {
-                    Value.ArrayValue arrayValue = (Value.ArrayValue) execStack.stack[base + arrayAppendInst.array().frameSlot()];
-                    if (arrayAppendInst.value() instanceof Operand.ConstantOperand constant) {
-                        arrayValue.values.add(new Value.IntegerValue(constant.value));
-                    }
-                    else if (arrayAppendInst.value() instanceof Operand.NullConstantOperand) {
-                        arrayValue.values.add(new Value.NullValue());
-                    }
-                    else if (arrayAppendInst.value() instanceof Operand.RegisterOperand registerOperand) {
-                        arrayValue.values.add(execStack.stack[base + registerOperand.frameSlot()]);
-                    }
-                    else throw new IllegalStateException();
-                }
                 case Instruction.ArrayStore arrayStoreInst -> {
                     if (arrayStoreInst.arrayOperand() instanceof Operand.RegisterOperand arrayOperand) {
                         Value.ArrayValue arrayValue = (Value.ArrayValue) execStack.stack[base + arrayOperand.frameSlot()];
@@ -241,7 +228,10 @@ else if (arrayStoreInst.sourceOperand() instanceof Operand.RegisterOperand regis
                             value = execStack.stack[base + registerOperand.frameSlot()];
                         }
                         else throw new IllegalStateException();
-                        arrayValue.values.set(index, value);
+                        if (index == arrayValue.values.size())
+                            arrayValue.values.add(value);
+                        else
+                            arrayValue.values.set(index, value);
                     } else throw new IllegalStateException();
                 }
                 case Instruction.ArrayLoad arrayLoadInst -> {
diff --git a/registervm/src/test/java/com/compilerprogramming/ezlang/compiler/TestCompiler.java b/registervm/src/test/java/com/compilerprogramming/ezlang/compiler/TestCompiler.java
index 2deb384..142b8b6 100644
--- a/registervm/src/test/java/com/compilerprogramming/ezlang/compiler/TestCompiler.java
+++ b/registervm/src/test/java/com/compilerprogramming/ezlang/compiler/TestCompiler.java
@@ -204,9 +204,9 @@ func foo()->[Int] {
         Assert.assertEquals("""
                 L0:
                     %t0 = New([Int])
-                    %t0.append(1)
-                    %t0.append(2)
-                    %t0.append(3)
+                    %t0[0] = 1
+                    %t0[1] = 2
+                    %t0[2] = 3
                     ret %t0
                     goto  L1
                 L1:
@@ -224,7 +224,7 @@ func foo(n: Int) -> [Int] {
         Assert.assertEquals("""
                 L0:
                     %t1 = New([Int])
-                    %t1.append(n)
+                    %t1[0] = n
                     ret %t1
                     goto  L1
                 L1:
diff --git a/semantic/src/main/java/com/compilerprogramming/ezlang/semantic/SemaAssignTypes.java b/semantic/src/main/java/com/compilerprogramming/ezlang/semantic/SemaAssignTypes.java
index f22c855..25abf39 100644
--- a/semantic/src/main/java/com/compilerprogramming/ezlang/semantic/SemaAssignTypes.java
+++ b/semantic/src/main/java/com/compilerprogramming/ezlang/semantic/SemaAssignTypes.java
@@ -106,7 +106,7 @@ public ASTVisitor visit(AST.UnaryExpr unaryExpr, boolean enter) {
     }
 
     @Override
-    public ASTVisitor visit(AST.FieldExpr fieldExpr, boolean enter) {
+    public ASTVisitor visit(AST.GetFieldExpr fieldExpr, boolean enter) {
         if (enter)
             return this;
         validType(fieldExpr.object.type, false);
@@ -127,6 +127,31 @@ else if (fieldExpr.object.type instanceof Type.TypeNullable ptr &&
         return this;
     }
 
+    @Override
+    public ASTVisitor visit(AST.SetFieldExpr fieldExpr, boolean enter) {
+        if (enter)
+            return this;
+        validType(fieldExpr.object.type, true);
+        Type.TypeStruct structType = null;
+        if (fieldExpr.object.type instanceof Type.TypeStruct ts) {
+            structType = ts;
+        }
+        else if (fieldExpr.object.type instanceof Type.TypeNullable ptr &&
+                ptr.baseType instanceof Type.TypeStruct ts) {
+            structType = ts;
+        }
+        else
+            throw new CompilerException("Unexpected struct type " + fieldExpr.object.type);
+        var fieldType = structType.getField(fieldExpr.fieldName);
+        if (fieldType == null)
+            throw new CompilerException("Struct " + structType + " does not have field named " + fieldExpr.fieldName);
+        validType(fieldExpr.value.type, true);
+        checkAssignmentCompatible(fieldType, fieldExpr.value.type);
+        fieldExpr.type = fieldType;
+        return this;
+    }
+
+
     @Override
     public ASTVisitor visit(AST.CallExpr callExpr, boolean enter) {
         if (!enter) {
@@ -140,14 +165,6 @@ public ASTVisitor visit(AST.CallExpr callExpr, boolean enter) {
         return this;
     }
 
-    @Override
-    public ASTVisitor visit(AST.SetFieldExpr setFieldExpr, boolean enter) {
-        if (!enter) {
-            validType(setFieldExpr.value.type, true);
-        }
-        return this;
-    }
-
     @Override
     public ASTVisitor visit(AST.SimpleTypeExpr simpleTypeExpr, boolean enter) {
         return this;
@@ -190,7 +207,29 @@ else if (literalExpr.value.kind == Token.Kind.IDENT
     }
 
     @Override
-    public ASTVisitor visit(AST.ArrayIndexExpr arrayIndexExpr, boolean enter) {
+    public ASTVisitor visit(AST.ArrayLoadExpr arrayIndexExpr, boolean enter) {
+        if (!enter) {
+            validType(arrayIndexExpr.array.type, false);
+            Type.TypeArray arrayType = null;
+            if (arrayIndexExpr.array.type instanceof Type.TypeArray ta) {
+                arrayType = ta;
+            }
+            else if (arrayIndexExpr.array.type instanceof Type.TypeNullable ptr &&
+                    ptr.baseType instanceof Type.TypeArray ta) {
+                arrayType = ta;
+            }
+            else
+                throw new CompilerException("Unexpected array type " + arrayIndexExpr.array.type);
+            if (!(arrayIndexExpr.expr.type instanceof Type.TypeInteger))
+                throw new CompilerException("Array index must be integer type");
+            arrayIndexExpr.type = arrayType.getElementType();
+            validType(arrayIndexExpr.type, false);
+        }
+        return this;
+    }
+
+    @Override
+    public ASTVisitor visit(AST.ArrayStoreExpr arrayIndexExpr, boolean enter) {
         if (!enter) {
             validType(arrayIndexExpr.array.type, false);
             Type.TypeArray arrayType = null;
@@ -207,6 +246,8 @@ else if (arrayIndexExpr.array.type instanceof Type.TypeNullable ptr &&
                 throw new CompilerException("Array index must be integer type");
             arrayIndexExpr.type = arrayType.getElementType();
             validType(arrayIndexExpr.type, false);
+            validType(arrayIndexExpr.value.type, true);
+            checkAssignmentCompatible(arrayIndexExpr.type, arrayIndexExpr.value.type);
         }
         return this;
     }
@@ -222,22 +263,40 @@ public ASTVisitor visit(AST.NewExpr newExpr, boolean enter) {
             throw new CompilerException("new cannot be used to create a Nullable type");
         if (newExpr.typeExpr.type instanceof Type.TypeStruct typeStruct) {
             newExpr.type = newExpr.typeExpr.type;
-            for (AST.Expr expr: newExpr.initExprList) {
+        }
+        else if (newExpr.typeExpr.type instanceof Type.TypeArray arrayType) {
+            newExpr.type = newExpr.typeExpr.type;
+        }
+        else
+            throw new CompilerException("Unsupported type in new expression");
+        return this;
+    }
+
+    @Override
+    public ASTVisitor visit(AST.InitExpr initExpr, boolean enter) {
+        if (enter)
+            return this;
+        if (initExpr.newExpr.type == null)
+            throw new CompilerException("Unresolved type in new expression");
+        validType(initExpr.newExpr.type, false);
+        if (initExpr.newExpr.type instanceof Type.TypeNullable)
+            throw new CompilerException("new cannot be used to create a Nullable type");
+        if (initExpr.newExpr.type instanceof Type.TypeStruct typeStruct) {
+            for (AST.Expr expr: initExpr.initExprList) {
                 if (expr instanceof AST.SetFieldExpr setFieldExpr) {
-                    setFieldExpr.objectType = newExpr.typeExpr.type;
                     var fieldType = typeStruct.getField(setFieldExpr.fieldName);
                     checkAssignmentCompatible(fieldType, setFieldExpr.value.type);
                 }
             }
         }
-        else if (newExpr.typeExpr.type instanceof Type.TypeArray arrayType) {
-            newExpr.type = newExpr.typeExpr.type;
-            for (AST.Expr expr: newExpr.initExprList) {
+        else if (initExpr.newExpr.type instanceof Type.TypeArray arrayType) {
+            for (AST.Expr expr: initExpr.initExprList) {
                 checkAssignmentCompatible(arrayType.getElementType(), expr.type);
             }
         }
         else
             throw new CompilerException("Unsupported type in new expression");
+        initExpr.type = initExpr.newExpr.type;
         return this;
     }
 
diff --git a/semantic/src/main/java/com/compilerprogramming/ezlang/semantic/SemaDefineTypes.java b/semantic/src/main/java/com/compilerprogramming/ezlang/semantic/SemaDefineTypes.java
index f0bf0c6..e5d3498 100644
--- a/semantic/src/main/java/com/compilerprogramming/ezlang/semantic/SemaDefineTypes.java
+++ b/semantic/src/main/java/com/compilerprogramming/ezlang/semantic/SemaDefineTypes.java
@@ -139,17 +139,17 @@ public ASTVisitor visit(AST.UnaryExpr unaryExpr, boolean enter) {
     }
 
     @Override
-    public ASTVisitor visit(AST.FieldExpr fieldExpr, boolean enter) {
+    public ASTVisitor visit(AST.GetFieldExpr fieldExpr, boolean enter) {
         return this;
     }
 
     @Override
-    public ASTVisitor visit(AST.CallExpr callExpr, boolean enter) {
+    public ASTVisitor visit(AST.SetFieldExpr fieldExpr, boolean enter) {
         return this;
     }
 
     @Override
-    public ASTVisitor visit(AST.SetFieldExpr setFieldExpr, boolean enter) {
+    public ASTVisitor visit(AST.CallExpr callExpr, boolean enter) {
         return this;
     }
 
@@ -249,7 +249,12 @@ public ASTVisitor visit(AST.LiteralExpr literalExpr, boolean enter) {
     }
 
     @Override
-    public ASTVisitor visit(AST.ArrayIndexExpr arrayIndexExpr, boolean enter) {
+    public ASTVisitor visit(AST.ArrayLoadExpr arrayLoadExpr, boolean enter) {
+        return this;
+    }
+
+    @Override
+    public ASTVisitor visit(AST.ArrayStoreExpr arrayStoreExpr, boolean enter) {
         return this;
     }
 
@@ -258,6 +263,11 @@ public ASTVisitor visit(AST.NewExpr newExpr, boolean enter) {
         return this;
     }
 
+    @Override
+    public ASTVisitor visit(AST.InitExpr initExpr, boolean enter) {
+        return this;
+    }
+
     @Override
     public ASTVisitor visit(AST.NameExpr nameExpr, boolean enter) {
         return this;
diff --git a/stackvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java b/stackvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
index 209ba60..7be5198 100644
--- a/stackvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
+++ b/stackvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
@@ -84,6 +84,7 @@ private void compileStatement(AST.Stmt statement) {
             case AST.VarStmt letStmt -> {
                 compileLet(letStmt);
             }
+            case AST.VarDeclStmt varDeclStmt -> {}
             case AST.IfElseStmt ifElseStmt -> {
                 compileIf(ifElseStmt);
             }
@@ -146,14 +147,14 @@ private void compileBreak(AST.BreakStmt breakStmt) {
     }
 
     private void compileWhile(AST.WhileStmt whileStmt) {
-        BasicBlock loopBlock = createLoopHead();
+        BasicBlock loopHead = createLoopHead();
         BasicBlock bodyBlock = createBlock();
         BasicBlock exitBlock = createBlock();
         BasicBlock savedBreakTarget = currentBreakTarget;
         BasicBlock savedContinueTarget = currentContinueTarget;
         currentBreakTarget = exitBlock;
-        currentContinueTarget = loopBlock;
-        startBlock(loopBlock);
+        currentContinueTarget = loopHead;
+        startBlock(loopHead);
         boolean indexed = compileExpr(whileStmt.condition);
         if (indexed)
             codeIndexedLoad();
@@ -161,7 +162,7 @@ private void compileWhile(AST.WhileStmt whileStmt) {
         startBlock(bodyBlock);
         compileStatement(whileStmt.stmt);
         if (!isBlockTerminated(currentBlock))
-            jumpTo(loopBlock);
+            jumpTo(loopHead);
         startBlock(exitBlock);
         currentContinueTarget = savedContinueTarget;
         currentBreakTarget = savedBreakTarget;
@@ -173,6 +174,7 @@ private boolean isBlockTerminated(BasicBlock block) {
     }
 
     private void jumpTo(BasicBlock block) {
+        assert !isBlockTerminated(currentBlock);
         currentBlock.add(new Instruction.Jump(block));
         currentBlock.addSuccessor(block);
     }
@@ -185,15 +187,15 @@ private void startBlock(BasicBlock block) {
     }
 
     private void compileIf(AST.IfElseStmt ifElseStmt) {
-        BasicBlock ifBlock = createBlock();
+        BasicBlock thenBlock = createBlock();
         boolean needElse = ifElseStmt.elseStmt != null;
         BasicBlock elseBlock = needElse ? createBlock() : null;
         BasicBlock exitBlock = createBlock();
         boolean indexed = compileExpr(ifElseStmt.condition);
         if (indexed)
             codeIndexedLoad();
-        code(new Instruction.ConditionalBranch(currentBlock, ifBlock, needElse ? elseBlock : exitBlock));
-        startBlock(ifBlock);
+        code(new Instruction.ConditionalBranch(currentBlock, thenBlock, needElse ? elseBlock : exitBlock));
+        startBlock(thenBlock);
         compileStatement(ifElseStmt.ifStmt);
         if (!isBlockTerminated(currentBlock))
             jumpTo(exitBlock);
@@ -232,11 +234,17 @@ private boolean compileExpr(AST.Expr expr) {
             case AST.NewExpr newExpr -> {
                 return compileNewExpr(newExpr);
             }
-            case AST.ArrayIndexExpr arrayIndexExpr -> {
-                return compileArrayIndexExpr(arrayIndexExpr);
+            case AST.InitExpr initExpr -> {
+                return compileInitExpr(initExpr);
             }
-            case AST.FieldExpr fieldExpr -> {
-                return compileFieldExpr(fieldExpr);
+            case AST.ArrayLoadExpr arrayLoadExpr -> {
+                return compileArrayIndexExpr(arrayLoadExpr);
+            }
+            case AST.ArrayStoreExpr arrayStoreExpr -> {
+                return compileArrayStoreExpr(arrayStoreExpr);
+            }
+            case AST.GetFieldExpr getFieldExpr -> {
+                return compileFieldExpr(getFieldExpr);
             }
             case AST.SetFieldExpr setFieldExpr -> {
                 return compileSetFieldExpr(setFieldExpr);
@@ -271,7 +279,7 @@ else if (t instanceof Type.TypeNullable ptr &&
             throw new CompilerException("Unexpected type: " + t);
     }
 
-    private boolean compileFieldExpr(AST.FieldExpr fieldExpr) {
+    private boolean compileFieldExpr(AST.GetFieldExpr fieldExpr) {
         Type.TypeStruct typeStruct = getStructType(fieldExpr.object.type);
         int fieldIndex = typeStruct.getFieldIndex(fieldExpr.fieldName);
         if (fieldIndex < 0)
@@ -283,7 +291,7 @@ private boolean compileFieldExpr(AST.FieldExpr fieldExpr) {
         return true;
     }
 
-    private boolean compileArrayIndexExpr(AST.ArrayIndexExpr arrayIndexExpr) {
+    private boolean compileArrayIndexExpr(AST.ArrayLoadExpr arrayIndexExpr) {
         compileExpr(arrayIndexExpr.array);
         boolean indexed = compileExpr(arrayIndexExpr.expr);
         if (indexed)
@@ -292,10 +300,12 @@ private boolean compileArrayIndexExpr(AST.ArrayIndexExpr arrayIndexExpr) {
     }
 
     private boolean compileSetFieldExpr(AST.SetFieldExpr setFieldExpr) {
-        Type.TypeStruct structType = (Type.TypeStruct) setFieldExpr.objectType;
+        Type.TypeStruct structType = (Type.TypeStruct) setFieldExpr.object.type;
         int fieldIndex = structType.getFieldIndex(setFieldExpr.fieldName);
         if (fieldIndex == -1)
             throw new CompilerException("Field " + setFieldExpr.fieldName + " not found in struct " + structType.name);
+        if (!(setFieldExpr instanceof AST.InitFieldExpr))
+            compileExpr(setFieldExpr.object);
         code(new Instruction.PushConst(fieldIndex));
         boolean indexed = compileExpr(setFieldExpr.value);
         if (indexed)
@@ -304,22 +314,29 @@ private boolean compileSetFieldExpr(AST.SetFieldExpr setFieldExpr) {
         return false;
     }
 
+    private boolean compileArrayStoreExpr(AST.ArrayStoreExpr arrayStoreExpr) {
+        if (!(arrayStoreExpr instanceof AST.ArrayInitExpr))
+            compileExpr(arrayStoreExpr.array);
+        boolean indexed = compileExpr(arrayStoreExpr.expr);
+        if (indexed)
+            codeIndexedLoad();
+        indexed = compileExpr(arrayStoreExpr.value);
+        if (indexed)
+            codeIndexedLoad();
+        codeIndexedStore();
+        return false;
+    }
+
     private boolean compileNewExpr(AST.NewExpr newExpr) {
         code(new Instruction.New(newExpr.type));
-        if (newExpr.initExprList != null && !newExpr.initExprList.isEmpty()) {
-            if (newExpr.type instanceof Type.TypeArray) {
-                for (AST.Expr expr : newExpr.initExprList) {
-                    // Maybe have specific AST similar to how we have SetFieldExpr?
-                    boolean indexed = compileExpr(expr);
-                    if (indexed)
-                        codeIndexedLoad();
-                    code(new Instruction.StoreAppend());
-                }
-            }
-            else if (newExpr.type instanceof Type.TypeStruct) {
-                for (AST.Expr expr : newExpr.initExprList) {
-                    compileExpr(expr);
-                }
+        return false;
+    }
+
+    private boolean compileInitExpr(AST.InitExpr initExpr) {
+        compileExpr(initExpr.newExpr);
+        if (initExpr.initExprList != null && !initExpr.initExprList.isEmpty()) {
+            for (AST.Expr expr : initExpr.initExprList) {
+                compileExpr(expr);
             }
         }
         return false;
diff --git a/stackvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java b/stackvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java
index bc195ef..7fd42cd 100644
--- a/stackvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java
+++ b/stackvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java
@@ -18,19 +18,18 @@ public class Instruction {
     public static final int LOAD_VAR = 10;
     public static final int NEW = 11;
     public static final int LOAD_INDEXED = 12;
-    public static final int STORE_APPEND = 13; // array append
-    public static final int STORE_INDEXED = 14;
-    public static final int CALL = 15;
-    public static final int STORE = 16;
-    public static final int CBR = 17;
-    public static final int JUMP = 18;
-    public static final int POP = 19;
-    public static final int EQ = 20;
-    public static final int NE = 21;
-    public static final int LT = 22;
-    public static final int GT = 23;
-    public static final int LE = 24;
-    public static final int GE = 25;
+    public static final int STORE_INDEXED = 13;
+    public static final int CALL = 14;
+    public static final int STORE = 15;
+    public static final int CBR = 16;
+    public static final int JUMP = 17;
+    public static final int POP = 18;
+    public static final int EQ = 19;
+    public static final int NE = 20;
+    public static final int LT = 21;
+    public static final int GT = 22;
+    public static final int LE = 23;
+    public static final int GE = 24;
 
     static final String[] opNames = {
         "ret",
@@ -46,7 +45,6 @@ public class Instruction {
         "load",
         "new",
         "loadindexed",
-        "storeappend", // array append
         "storeindexed",
         "call",
         "store",
@@ -141,12 +139,6 @@ public LoadIndexed() {
         }
     }
 
-    public static class StoreAppend extends Instruction {
-        public StoreAppend() {
-            super(STORE_APPEND);
-        }
-    }
-
     public static class StoreIndexed extends Instruction {
         public StoreIndexed() {
             super(STORE_INDEXED);
diff --git a/stackvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestCompiler.java b/stackvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestCompiler.java
index 2a9a21e..6393090 100644
--- a/stackvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestCompiler.java
+++ b/stackvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestCompiler.java
@@ -228,12 +228,15 @@ func foo()->[Int] {
         Assert.assertEquals("""
 L0:
 	new [Int]
+	pushi 0
+	pushi 1
+	storeindexed
 	pushi 1
-	storeappend
 	pushi 2
-	storeappend
+	storeindexed
+	pushi 2
 	pushi 3
-	storeappend
+	storeindexed
 	jump L1
 L1:
 """, result);
@@ -250,8 +253,9 @@ func foo(n: Int) -> [Int] {
         Assert.assertEquals("""
 L0:
 	new [Int]
+	pushi 0
 	load 0
-	storeappend
+	storeindexed
 	jump L1
 L1:
 """, result);
@@ -294,6 +298,7 @@ func foo(p: Person) -> Person {
 	pushi 0
 	pushi 10
 	storeindexed
+	pop
 	jump L1
 L1:
 """, result);
@@ -341,10 +346,12 @@ func foo(array: [Int]) {
 	pushi 0
 	pushi 1
 	storeindexed
+	pop
 	load 0
 	pushi 1
 	pushi 2
 	storeindexed
+	pop
 	jump L1
 L1:
 """, result);

From 0f46dc92a5c3363fe41b53cd47a456615c330577 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <dibyendu@redukti.com>
Date: Wed, 26 Mar 2025 15:09:33 +0000
Subject: [PATCH 044/119] Add Sea of Nodes IR implementation based on chapter
 21 in Simple project

---
 README.md                                     |   2 +-
 pom.xml                                       |   1 +
 seaofnodes/README.md                          |   6 +
 seaofnodes/pom.xml                            |  31 +
 .../ezlang/compiler/Ary.java                  | 228 ++++
 .../ezlang/compiler/Compiler.java             | 983 ++++++++++++++++++
 .../ezlang/compiler/IterPeeps.java            | 197 ++++
 .../ezlang/compiler/SB.java                   |  99 ++
 .../ezlang/compiler/Utils.java                |  53 +
 .../ezlang/compiler/Var.java                  |  52 +
 .../ezlang/compiler/codegen/BuildLRG.java     |  91 ++
 .../ezlang/compiler/codegen/Coalesce.java     | 119 +++
 .../ezlang/compiler/codegen/CodeGen.java      | 389 +++++++
 .../ezlang/compiler/codegen/ElfExpr.java      |  87 ++
 .../ezlang/compiler/codegen/ElfFile.java      | 387 +++++++
 .../ezlang/compiler/codegen/Encoding.java     | 418 ++++++++
 .../compiler/codegen/GlobalCodeMotion.java    | 281 +++++
 .../ezlang/compiler/codegen/IFG.java          | 526 ++++++++++
 .../ezlang/compiler/codegen/LRG.java          | 235 +++++
 .../compiler/codegen/ListScheduler.java       | 258 +++++
 .../ezlang/compiler/codegen/Machine.java      |  48 +
 .../ezlang/compiler/codegen/RIPRelSize.java   |  14 +
 .../ezlang/compiler/codegen/RegAlloc.java     | 526 ++++++++++
 .../ezlang/compiler/codegen/RegMask.java      | 142 +++
 .../ezlang/compiler/nodes/AddFNode.java       |  44 +
 .../ezlang/compiler/nodes/AddNode.java        | 200 ++++
 .../ezlang/compiler/nodes/AndNode.java        |  58 ++
 .../ezlang/compiler/nodes/BoolNode.java       | 135 +++
 .../ezlang/compiler/nodes/CFGNode.java        | 181 ++++
 .../ezlang/compiler/nodes/CProjNode.java      |  66 ++
 .../ezlang/compiler/nodes/CallEndNode.java    | 100 ++
 .../ezlang/compiler/nodes/CallNode.java       | 165 +++
 .../ezlang/compiler/nodes/CalleeSaveNode.java |  26 +
 .../ezlang/compiler/nodes/CastNode.java       |  64 ++
 .../ezlang/compiler/nodes/ConstantNode.java   |  69 ++
 .../ezlang/compiler/nodes/CtrlNode.java       |  14 +
 .../ezlang/compiler/nodes/DivFNode.java       |  41 +
 .../ezlang/compiler/nodes/DivNode.java        |  46 +
 .../ezlang/compiler/nodes/FRefNode.java       |  37 +
 .../ezlang/compiler/nodes/FunNode.java        | 199 ++++
 .../ezlang/compiler/nodes/IfNode.java         |  82 ++
 .../ezlang/compiler/nodes/LoadNode.java       | 209 ++++
 .../ezlang/compiler/nodes/LogicalNode.java    |  28 +
 .../ezlang/compiler/nodes/LoopNode.java       |  80 ++
 .../compiler/nodes/MachConcreteNode.java      |  27 +
 .../ezlang/compiler/nodes/MachNode.java       |  62 ++
 .../ezlang/compiler/nodes/MemMergeNode.java   | 155 +++
 .../ezlang/compiler/nodes/MemOpNode.java      |  78 ++
 .../ezlang/compiler/nodes/MinusFNode.java     |  36 +
 .../ezlang/compiler/nodes/MinusNode.java      |  39 +
 .../ezlang/compiler/nodes/MulFNode.java       |  51 +
 .../ezlang/compiler/nodes/MulNode.java        |  89 ++
 .../ezlang/compiler/nodes/MultiNode.java      |  29 +
 .../ezlang/compiler/nodes/NeverNode.java      |  20 +
 .../ezlang/compiler/nodes/NewNode.java        |  83 ++
 .../ezlang/compiler/nodes/Node.java           | 721 +++++++++++++
 .../ezlang/compiler/nodes/NotNode.java        |  31 +
 .../ezlang/compiler/nodes/OrNode.java         |  53 +
 .../ezlang/compiler/nodes/ParmNode.java       |  47 +
 .../ezlang/compiler/nodes/PhiNode.java        | 205 ++++
 .../ezlang/compiler/nodes/ProjNode.java       |  54 +
 .../ezlang/compiler/nodes/ReadOnlyNode.java   |  30 +
 .../ezlang/compiler/nodes/RegionNode.java     | 123 +++
 .../ezlang/compiler/nodes/ReturnNode.java     | 131 +++
 .../ezlang/compiler/nodes/RoundF32Node.java   |  38 +
 .../ezlang/compiler/nodes/SarNode.java        |  48 +
 .../ezlang/compiler/nodes/ScopeNode.java      | 429 ++++++++
 .../ezlang/compiler/nodes/ShlNode.java        |  52 +
 .../ezlang/compiler/nodes/ShrNode.java        |  45 +
 .../ezlang/compiler/nodes/SplitNode.java      |  22 +
 .../ezlang/compiler/nodes/StartNode.java      |  57 +
 .../ezlang/compiler/nodes/StopNode.java       |  63 ++
 .../ezlang/compiler/nodes/StoreNode.java      | 115 ++
 .../ezlang/compiler/nodes/StructNode.java     |  46 +
 .../ezlang/compiler/nodes/SubFNode.java       |  44 +
 .../ezlang/compiler/nodes/SubNode.java        |  58 ++
 .../ezlang/compiler/nodes/ToFloatNode.java    |  34 +
 .../ezlang/compiler/nodes/XCtrlNode.java      |  14 +
 .../ezlang/compiler/nodes/XorNode.java        |  52 +
 .../compiler/nodes/cpus/arm/AddARM.java       |  20 +
 .../compiler/nodes/cpus/arm/AddFARM.java      |  22 +
 .../compiler/nodes/cpus/arm/AddIARM.java      |  27 +
 .../compiler/nodes/cpus/arm/AndARM.java       |  21 +
 .../compiler/nodes/cpus/arm/AndIARM.java      |  24 +
 .../compiler/nodes/cpus/arm/AsrARM.java       |  18 +
 .../compiler/nodes/cpus/arm/AsrIARM.java      |  31 +
 .../compiler/nodes/cpus/arm/BranchARM.java    |  65 ++
 .../compiler/nodes/cpus/arm/CallARM.java      |  47 +
 .../compiler/nodes/cpus/arm/CallEndARM.java   |  24 +
 .../compiler/nodes/cpus/arm/CallRRARM.java    |  32 +
 .../compiler/nodes/cpus/arm/CmpARM.java       |  22 +
 .../compiler/nodes/cpus/arm/CmpFARM.java      |  22 +
 .../compiler/nodes/cpus/arm/CmpIARM.java      |  40 +
 .../compiler/nodes/cpus/arm/DivARM.java       |  19 +
 .../compiler/nodes/cpus/arm/DivFARM.java      |  19 +
 .../compiler/nodes/cpus/arm/FloatARM.java     |  45 +
 .../compiler/nodes/cpus/arm/FunARM.java       |  36 +
 .../compiler/nodes/cpus/arm/I2F8ARM.java      |  25 +
 .../compiler/nodes/cpus/arm/IntARM.java       |  66 ++
 .../compiler/nodes/cpus/arm/LoadARM.java      |  33 +
 .../compiler/nodes/cpus/arm/LslARM.java       |  19 +
 .../compiler/nodes/cpus/arm/LslIARM.java      |  33 +
 .../compiler/nodes/cpus/arm/LsrARM.java       |  19 +
 .../compiler/nodes/cpus/arm/LsrIARM.java      |  29 +
 .../compiler/nodes/cpus/arm/MemOpARM.java     |  67 ++
 .../compiler/nodes/cpus/arm/MulARM.java       |  19 +
 .../compiler/nodes/cpus/arm/MulFARM.java      |  18 +
 .../compiler/nodes/cpus/arm/NewARM.java       |  24 +
 .../compiler/nodes/cpus/arm/NotARM.java       |  28 +
 .../ezlang/compiler/nodes/cpus/arm/OrARM.java |  20 +
 .../compiler/nodes/cpus/arm/OrIARM.java       |  24 +
 .../compiler/nodes/cpus/arm/ParmARM.java      |  17 +
 .../compiler/nodes/cpus/arm/ProjARM.java      |  15 +
 .../compiler/nodes/cpus/arm/RetARM.java       |  43 +
 .../compiler/nodes/cpus/arm/SetARM.java       |  33 +
 .../compiler/nodes/cpus/arm/SplitARM.java     |  54 +
 .../compiler/nodes/cpus/arm/StoreARM.java     |  37 +
 .../compiler/nodes/cpus/arm/SubARM.java       |  19 +
 .../compiler/nodes/cpus/arm/SubFARM.java      |  18 +
 .../compiler/nodes/cpus/arm/SubIARM.java      |  27 +
 .../compiler/nodes/cpus/arm/TFPARM.java       |  52 +
 .../compiler/nodes/cpus/arm/UJmpARM.java      |  48 +
 .../compiler/nodes/cpus/arm/XorARM.java       |  19 +
 .../compiler/nodes/cpus/arm/XorIARM.java      |  27 +
 .../ezlang/compiler/nodes/cpus/arm/arm.java   | 662 ++++++++++++
 .../compiler/nodes/cpus/riscv/AUIPC.java      |  41 +
 .../compiler/nodes/cpus/riscv/AddFRISC.java   |  17 +
 .../compiler/nodes/cpus/riscv/AddIRISC.java   |  21 +
 .../compiler/nodes/cpus/riscv/AddRISC.java    |  21 +
 .../compiler/nodes/cpus/riscv/AndIRISC.java   |  12 +
 .../compiler/nodes/cpus/riscv/AndRISC.java    |  16 +
 .../compiler/nodes/cpus/riscv/BranchRISC.java |  76 ++
 .../nodes/cpus/riscv/CallEndRISC.java         |  23 +
 .../compiler/nodes/cpus/riscv/CallRISC.java   |  62 ++
 .../compiler/nodes/cpus/riscv/CallRRISC.java  |  31 +
 .../compiler/nodes/cpus/riscv/CastRISC.java   |  18 +
 .../compiler/nodes/cpus/riscv/DivFRISC.java   |  19 +
 .../compiler/nodes/cpus/riscv/DivRISC.java    |  17 +
 .../compiler/nodes/cpus/riscv/FltRISC.java    |  44 +
 .../compiler/nodes/cpus/riscv/FunRISC.java    |  36 +
 .../compiler/nodes/cpus/riscv/I2F8RISC.java   |  23 +
 .../compiler/nodes/cpus/riscv/ImmRISC.java    |  54 +
 .../compiler/nodes/cpus/riscv/IntRISC.java    |  29 +
 .../ezlang/compiler/nodes/cpus/riscv/LUI.java |  32 +
 .../compiler/nodes/cpus/riscv/LoadRISC.java   |  28 +
 .../compiler/nodes/cpus/riscv/MemOpRISC.java  |  76 ++
 .../compiler/nodes/cpus/riscv/MulFRISC.java   |  17 +
 .../compiler/nodes/cpus/riscv/MulRISC.java    |  17 +
 .../compiler/nodes/cpus/riscv/NewRISC.java    |  34 +
 .../compiler/nodes/cpus/riscv/NotRISC.java    |  16 +
 .../compiler/nodes/cpus/riscv/OrIRISC.java    |  12 +
 .../compiler/nodes/cpus/riscv/OrRISC.java     |  16 +
 .../compiler/nodes/cpus/riscv/ParmRISC.java   |  17 +
 .../compiler/nodes/cpus/riscv/ProjRISC.java   |  15 +
 .../compiler/nodes/cpus/riscv/RetRISC.java    |  50 +
 .../compiler/nodes/cpus/riscv/SetFRISC.java   |  33 +
 .../compiler/nodes/cpus/riscv/SetIRISC.java   |  17 +
 .../compiler/nodes/cpus/riscv/SetRISC.java    |  17 +
 .../compiler/nodes/cpus/riscv/SllIRISC.java   |  12 +
 .../compiler/nodes/cpus/riscv/SllRISC.java    |  17 +
 .../compiler/nodes/cpus/riscv/SplitRISC.java  |  55 +
 .../compiler/nodes/cpus/riscv/SraIRISC.java   |  12 +
 .../compiler/nodes/cpus/riscv/SraRISC.java    |  17 +
 .../compiler/nodes/cpus/riscv/SrlIRISC.java   |  12 +
 .../compiler/nodes/cpus/riscv/SrlRISC.java    |  17 +
 .../compiler/nodes/cpus/riscv/StoreRISC.java  |  46 +
 .../compiler/nodes/cpus/riscv/SubFRISC.java   |  17 +
 .../compiler/nodes/cpus/riscv/SubRISC.java    |  16 +
 .../compiler/nodes/cpus/riscv/TFPRISC.java    |  54 +
 .../compiler/nodes/cpus/riscv/UJmpRISC.java   |  49 +
 .../compiler/nodes/cpus/riscv/XorIRISC.java   |  12 +
 .../compiler/nodes/cpus/riscv/XorRISC.java    |  16 +
 .../compiler/nodes/cpus/riscv/riscv.java      | 557 ++++++++++
 .../nodes/cpus/x86_64_v2/AddFMemX86.java      |  44 +
 .../nodes/cpus/x86_64_v2/AddFX86.java         |  33 +
 .../nodes/cpus/x86_64_v2/AddIX86.java         |  13 +
 .../nodes/cpus/x86_64_v2/AddMemX86.java       |  35 +
 .../compiler/nodes/cpus/x86_64_v2/AddX86.java |  11 +
 .../nodes/cpus/x86_64_v2/AndIX86.java         |  11 +
 .../compiler/nodes/cpus/x86_64_v2/AndX86.java |  11 +
 .../nodes/cpus/x86_64_v2/CallEndX86.java      |  23 +
 .../nodes/cpus/x86_64_v2/CallRX86.java        |  33 +
 .../nodes/cpus/x86_64_v2/CallX86.java         |  46 +
 .../nodes/cpus/x86_64_v2/CastX86.java         |  16 +
 .../nodes/cpus/x86_64_v2/CmpFX86.java         |  32 +
 .../nodes/cpus/x86_64_v2/CmpIX86.java         |  49 +
 .../nodes/cpus/x86_64_v2/CmpMemX86.java       |  43 +
 .../compiler/nodes/cpus/x86_64_v2/CmpX86.java |  18 +
 .../nodes/cpus/x86_64_v2/DivFX86.java         |  34 +
 .../compiler/nodes/cpus/x86_64_v2/DivX86.java |  33 +
 .../compiler/nodes/cpus/x86_64_v2/FltX86.java |  47 +
 .../compiler/nodes/cpus/x86_64_v2/FunX86.java |  44 +
 .../nodes/cpus/x86_64_v2/I2f8X86.java         |  31 +
 .../compiler/nodes/cpus/x86_64_v2/ImmX86.java |  37 +
 .../compiler/nodes/cpus/x86_64_v2/IntX86.java |  76 ++
 .../compiler/nodes/cpus/x86_64_v2/JmpX86.java |  69 ++
 .../compiler/nodes/cpus/x86_64_v2/LeaX86.java |  55 +
 .../nodes/cpus/x86_64_v2/LoadX86.java         |  80 ++
 .../nodes/cpus/x86_64_v2/MemAddX86.java       |  39 +
 .../nodes/cpus/x86_64_v2/MemOpX86.java        |  85 ++
 .../nodes/cpus/x86_64_v2/MulFX86.java         |  32 +
 .../nodes/cpus/x86_64_v2/MulIX86.java         |  11 +
 .../compiler/nodes/cpus/x86_64_v2/MulX86.java |  29 +
 .../compiler/nodes/cpus/x86_64_v2/NewX86.java |  30 +
 .../compiler/nodes/cpus/x86_64_v2/NotX86.java |  39 +
 .../compiler/nodes/cpus/x86_64_v2/OrIX86.java |  11 +
 .../compiler/nodes/cpus/x86_64_v2/OrX86.java  |  11 +
 .../nodes/cpus/x86_64_v2/ParmX86.java         |  17 +
 .../nodes/cpus/x86_64_v2/ProjX86.java         |  15 +
 .../compiler/nodes/cpus/x86_64_v2/RegX86.java |  25 +
 .../compiler/nodes/cpus/x86_64_v2/RetX86.java |  41 +
 .../nodes/cpus/x86_64_v2/SarIX86.java         |  12 +
 .../compiler/nodes/cpus/x86_64_v2/SarX86.java |  24 +
 .../compiler/nodes/cpus/x86_64_v2/SetX86.java |  50 +
 .../nodes/cpus/x86_64_v2/ShlIX86.java         |  11 +
 .../compiler/nodes/cpus/x86_64_v2/ShlX86.java |  24 +
 .../nodes/cpus/x86_64_v2/ShrIX86.java         |  11 +
 .../compiler/nodes/cpus/x86_64_v2/ShrX86.java |  24 +
 .../nodes/cpus/x86_64_v2/SplitX86.java        |  99 ++
 .../nodes/cpus/x86_64_v2/StoreX86.java        |  63 ++
 .../nodes/cpus/x86_64_v2/SubFX86.java         |  31 +
 .../compiler/nodes/cpus/x86_64_v2/SubX86.java |  10 +
 .../compiler/nodes/cpus/x86_64_v2/TFPX86.java |  66 ++
 .../nodes/cpus/x86_64_v2/UJmpX86.java         |  53 +
 .../nodes/cpus/x86_64_v2/XorIX86.java         |  11 +
 .../compiler/nodes/cpus/x86_64_v2/XorX86.java |  11 +
 .../nodes/cpus/x86_64_v2/x86_64_v2.java       | 678 ++++++++++++
 .../ezlang/compiler/print/ASMPrinter.java     | 254 +++++
 .../compiler/print/GraphVisualizer.java       | 287 +++++
 .../ezlang/compiler/print/IRPrinter.java      | 327 ++++++
 .../ezlang/compiler/sontypes/Field.java       |  74 ++
 .../ezlang/compiler/sontypes/SONType.java     | 235 +++++
 .../compiler/sontypes/SONTypeFloat.java       |  94 ++
 .../compiler/sontypes/SONTypeFunPtr.java      | 106 ++
 .../compiler/sontypes/SONTypeInteger.java     | 127 +++
 .../ezlang/compiler/sontypes/SONTypeMem.java  |  72 ++
 .../compiler/sontypes/SONTypeMemPtr.java      | 107 ++
 .../ezlang/compiler/sontypes/SONTypeNil.java  |  60 ++
 .../ezlang/compiler/sontypes/SONTypePtr.java  |  56 +
 .../ezlang/compiler/sontypes/SONTypeRPC.java  |  97 ++
 .../compiler/sontypes/SONTypeStruct.java      | 259 +++++
 .../compiler/sontypes/SONTypeTuple.java       |  99 ++
 .../ezlang/compiler/Main.java                 |  19 +
 .../ezlang/compiler/Simple.java               | 304 ++++++
 .../ezlang/compiler/TestSONTypes.java         | 221 ++++
 .../ezlang/types/Type.java                    |   1 +
 246 files changed, 19378 insertions(+), 1 deletion(-)
 create mode 100644 seaofnodes/README.md
 create mode 100644 seaofnodes/pom.xml
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/Ary.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/IterPeeps.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/SB.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/Utils.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/Var.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/BuildLRG.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/Coalesce.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/CodeGen.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/ElfExpr.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/ElfFile.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/Encoding.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/GlobalCodeMotion.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/IFG.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/LRG.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/ListScheduler.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/Machine.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/RIPRelSize.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/RegAlloc.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/RegMask.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/AddFNode.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/AddNode.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/AndNode.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/BoolNode.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CFGNode.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CProjNode.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CallEndNode.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CallNode.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CalleeSaveNode.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CastNode.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ConstantNode.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CtrlNode.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/DivFNode.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/DivNode.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/FRefNode.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/FunNode.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/IfNode.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/LoadNode.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/LogicalNode.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/LoopNode.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MachConcreteNode.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MachNode.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MemMergeNode.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MemOpNode.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MinusFNode.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MinusNode.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MulFNode.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MulNode.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MultiNode.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/NeverNode.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/NewNode.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/Node.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/NotNode.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/OrNode.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ParmNode.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/PhiNode.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ProjNode.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ReadOnlyNode.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/RegionNode.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ReturnNode.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/RoundF32Node.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/SarNode.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ScopeNode.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ShlNode.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ShrNode.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/SplitNode.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StartNode.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StopNode.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StoreNode.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StructNode.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/SubFNode.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/SubNode.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ToFloatNode.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/XCtrlNode.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/XorNode.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AddARM.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AddFARM.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AddIARM.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AndARM.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AndIARM.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AsrARM.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AsrIARM.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/BranchARM.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CallARM.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CallEndARM.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CallRRARM.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CmpARM.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CmpFARM.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CmpIARM.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/DivARM.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/DivFARM.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/FloatARM.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/FunARM.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/I2F8ARM.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/IntARM.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LoadARM.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LslARM.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LslIARM.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LsrARM.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LsrIARM.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/MemOpARM.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/MulARM.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/MulFARM.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/NewARM.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/NotARM.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/OrARM.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/OrIARM.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/ParmARM.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/ProjARM.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/RetARM.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SetARM.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SplitARM.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/StoreARM.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SubARM.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SubFARM.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SubIARM.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/TFPARM.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/UJmpARM.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/XorARM.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/XorIARM.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/arm.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/AUIPC.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/AddFRISC.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/AddIRISC.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/AddRISC.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/AndIRISC.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/AndRISC.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/BranchRISC.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CallEndRISC.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CallRISC.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CallRRISC.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CastRISC.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/DivFRISC.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/DivRISC.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/FltRISC.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/FunRISC.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/I2F8RISC.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/ImmRISC.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/IntRISC.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/LUI.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/LoadRISC.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/MemOpRISC.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/MulFRISC.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/MulRISC.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/NewRISC.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/NotRISC.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/OrIRISC.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/OrRISC.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/ParmRISC.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/ProjRISC.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/RetRISC.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SetFRISC.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SetIRISC.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SetRISC.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SllIRISC.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SllRISC.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SplitRISC.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SraIRISC.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SraRISC.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SrlIRISC.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SrlRISC.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/StoreRISC.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SubFRISC.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SubRISC.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/TFPRISC.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/UJmpRISC.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/XorIRISC.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/XorRISC.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/riscv.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/AddFMemX86.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/AddFX86.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/AddIX86.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/AddMemX86.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/AddX86.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/AndIX86.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/AndX86.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CallEndX86.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CallRX86.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CallX86.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CastX86.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CmpFX86.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CmpIX86.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CmpMemX86.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CmpX86.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/DivFX86.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/DivX86.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/FltX86.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/FunX86.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/I2f8X86.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/ImmX86.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/IntX86.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/JmpX86.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/LeaX86.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/LoadX86.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/MemAddX86.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/MemOpX86.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/MulFX86.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/MulIX86.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/MulX86.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/NewX86.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/NotX86.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/OrIX86.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/OrX86.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/ParmX86.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/ProjX86.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/RegX86.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/RetX86.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/SarIX86.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/SarX86.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/SetX86.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/ShlIX86.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/ShlX86.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/ShrIX86.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/ShrX86.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/SplitX86.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/StoreX86.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/SubFX86.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/SubX86.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/TFPX86.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/UJmpX86.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/XorIX86.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/XorX86.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/x86_64_v2.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/print/ASMPrinter.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/print/GraphVisualizer.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/print/IRPrinter.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/Field.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONType.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeFloat.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeFunPtr.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeInteger.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeMem.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeMemPtr.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeNil.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypePtr.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeRPC.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeStruct.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeTuple.java
 create mode 100644 seaofnodes/src/test/java/com/compilerprogramming/ezlang/compiler/Main.java
 create mode 100644 seaofnodes/src/test/java/com/compilerprogramming/ezlang/compiler/Simple.java
 create mode 100644 seaofnodes/src/test/java/com/compilerprogramming/ezlang/compiler/TestSONTypes.java

diff --git a/README.md b/README.md
index 913da06..d9783b2 100644
--- a/README.md
+++ b/README.md
@@ -27,7 +27,7 @@ The project is under development and subject to change. At this point in time, w
 * [stackvm](./stackvm/README.md) - a compiler that generates IR for a stack based virtual machine
 * [registervm](./registervm/README.md) - a compiler that generates a so called three-address IR and an interpreter that can execute the IR
 * [optvm](./optvm/README.md) - an optimizing compiler (WIP) that supports SSA.
-* seaofnodes - a compiler that will generate Sea of Nodes IR (planned)
+* [seaofnodes](./seaofnodes/README.md) - a compiler that will generate Sea of Nodes IR, using SoN backend from [Simple](https://github.com/SeaOfNodes/Simple).
 
 ## How can you contribute?
 
diff --git a/pom.xml b/pom.xml
index 4ea1d08..f2eb179 100644
--- a/pom.xml
+++ b/pom.xml
@@ -28,6 +28,7 @@
         <module>stackvm</module>
         <module>registervm</module>
         <module>optvm</module>
+        <module>seaofnodes</module>
     </modules>
 
     <properties>
diff --git a/seaofnodes/README.md b/seaofnodes/README.md
new file mode 100644
index 0000000..668c18e
--- /dev/null
+++ b/seaofnodes/README.md
@@ -0,0 +1,6 @@
+## Sea of Nodes IR 
+
+This module uses the [Simple](https://github.com/SeaOfNodes/Simple) project as a Sea of Nodes
+backend. We translate from the EeZee AST to Sea of Nodes IR.
+
+For copyright info on Simple project please see above website.
\ No newline at end of file
diff --git a/seaofnodes/pom.xml b/seaofnodes/pom.xml
new file mode 100644
index 0000000..e2fef93
--- /dev/null
+++ b/seaofnodes/pom.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>com.compilerprogramming.ezlang</groupId>
+        <artifactId>compilercraft</artifactId>
+        <version>1.0</version>
+    </parent>
+    <artifactId>seaofnodes</artifactId>
+    <packaging>jar</packaging>
+    <name>SeaOfNodes</name>
+    <dependencies>
+        <dependency>
+            <groupId>com.compilerprogramming.ezlang</groupId>
+            <artifactId>parser</artifactId>
+            <version>1.0</version>
+        </dependency>
+        <dependency>
+            <groupId>com.compilerprogramming.ezlang</groupId>
+            <artifactId>types</artifactId>
+            <version>1.0</version>
+        </dependency>
+        <dependency>
+            <groupId>com.compilerprogramming.ezlang</groupId>
+            <artifactId>semantic</artifactId>
+            <version>1.0</version>
+        </dependency>
+    </dependencies>
+</project>
\ No newline at end of file
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/Ary.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/Ary.java
new file mode 100644
index 0000000..9a217e0
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/Ary.java
@@ -0,0 +1,228 @@
+package com.compilerprogramming.ezlang.compiler;
+
+import java.lang.StringBuilder;
+import java.lang.reflect.Array;
+import java.util.*;
+
+// ArrayList with saner syntax
+public class Ary<E> extends AbstractList<E> implements List<E> {
+    public E[] _es;
+    public int _len;
+
+    public Ary(E[] es) { this(es,es.length); }
+    public Ary(E[] es, int len) { if( es.length==0 ) es=Arrays.copyOf(es,1); _es=es; _len=len; }
+    @SuppressWarnings("unchecked")
+    public Ary(Class<E> clazz) { this((E[]) Array.newInstance(clazz, 1),0); }
+
+    /** @return list is empty */
+    public boolean isEmpty() { return _len==0; }
+    /** @return active list length */
+    public int len() { return _len; }
+    public int size() { return len(); }
+
+    /** @param i element index
+     *  @return element being returned; throws if OOB
+     *  @exception AIOOBE if !(0 <= i < _len)
+     */
+    public E get( int i ) { return at(i); }
+    public E at( int i ) {
+        range_check(i);
+        return _es[i];
+    }
+    public E atX( int i ) {
+        if( i >= _len ) return null;
+        return _es[i];
+    }
+
+    /** @return last element */
+    public E last( ) { return at(_len-1); }
+
+    /** Add element in amortized constant time
+     *  @param e Element to add at end of list
+     **/
+    public E push( E e ) {
+        if( _len >= _es.length ) _es = Arrays.copyOf(_es,Math.max(1,_es.length<<1));
+        return (_es[_len++] = e);
+    }
+    /** @return remove and return last element */
+    public E pop( ) {
+        return _len==0 ? null : _es[--_len];
+    }
+    @Override public E removeLast() { return pop(); }
+
+
+    /** Add element in amortized constant time
+     *  @param e Element to add at end of list
+     *  @return true */
+    public boolean add( E e ) {
+        if( _len >= _es.length ) _es = Arrays.copyOf(_es,Math.max(1,_es.length<<1));
+        _es[_len++] = e;
+        return true;
+    }
+
+    /** Fast, constant-time, element removal.  Does not preserve order
+     *  @param i element to be removed
+     *  @return element removed */
+    public E del( int i ) {
+        range_check(i);
+        E tmp = _es[i];
+        _es[i]=_es[--_len];
+        return tmp;
+    }
+
+    /** Slow, linear-time, element removal.  Preserves order.
+     *  @param i element to be removed
+     *  @return element removed */
+    @Override public E remove( int i ) {
+        range_check(i);
+        E e = _es[i];
+        System.arraycopy(_es,i+1,_es,i,(--_len)-i);
+        return e;
+    }
+
+    /** Slow, linear-time, element insertion.  Preserves order.
+     *  @param i where to insert
+     *  @return element inserted */
+    public E insert( E e, int i ) {
+        setLen(_len+1);
+        System.arraycopy(_es,i,_es,i+1,_len-i);
+        _es[i] = e;
+        return e;
+    }
+
+
+    /** Remove all elements */
+    public void clear( ) { _len=0; }
+
+    /** Set existing element
+     *  @param i element to set
+     *  @param e value to set
+     *  @return old value
+     *  @exception AIOOBE if !(0 <= i < _len)
+     */
+    public E set( int i, E e ) {
+        range_check(i);
+        E old = _es[i];
+        _es[i] = 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);
+        _len = len;
+        while( _es.length > (len<<1) ) // Shrink if hugely too large
+            _es = Arrays.copyOf(_es,_es.length>>1);
+        Arrays.fill(_es,len,_es.length,null);
+        return this;
+    }
+
+    public E swap( int i, int j ) {
+        range_check(i);
+        range_check(j);
+        E tmp = _es[i];
+        if( i==j ) return tmp;
+        _es[i] = _es[j];
+        _es[j] = tmp;
+        return tmp;
+    }
+
+
+    /** @param c Collection to be added */
+    public boolean addAll( Collection<? extends E> c ) {
+        if( c==null ) return false;
+        for( E e : c ) add(e);
+        return true;
+    }
+    public Ary<E> addAll( Iterable<? extends E> it ) { for( E e : it ) add(e); return this; }
+
+    /** @param es Array to be added */
+    public <F extends E> Ary<E> addAll( F[] es ) {
+        if( es==null || es.length==0 ) return this;
+        while( _len+es.length > _es.length ) _es = Arrays.copyOf(_es,_es.length<<1);
+        System.arraycopy(es,0,_es,_len,es.length);
+        _len += es.length;
+        return this;
+    }
+
+    /** @param c Collection to be added */
+    public Ary<E> addAll( Ary<? extends E> c ) {
+        if( c==null || c._len==0 ) return this;
+        while( _len+c._len > _es.length ) _es = Arrays.copyOf(_es,_es.length<<1);
+        System.arraycopy(c._es,0,_es,_len,c._len);
+        _len += c._len;
+        return this;
+    }
+
+    /** @return compact array version */
+    public E[] asAry() { return Arrays.copyOf(_es,_len); }
+
+    /** 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.
+     *  @param e Element to find
+     *  @return index of first matching element, or -1 if none */
+    public int find( E e ) {
+        for( int i=0; i<_len; i++ )  if( _es[i]==e )  return i;
+        return -1;
+    }
+
+
+    /** @return an iterator */
+    @Override public Iterator<E> iterator() { return new Iter(); }
+    private class Iter implements Iterator<E> {
+        int _i=0;
+        @Override public boolean hasNext() { return _i<_len; }
+        @Override public E next() { return _es[_i++]; }
+    }
+
+    @Override public String toString() {
+        StringBuilder sb = new StringBuilder().append('{');
+        for( int i=0; i<_len; i++ ) {
+            if( i>0 ) sb.append(',');
+            if( _es[i] != null ) sb.append(_es[i].toString());
+        }
+        return sb.append('}').toString();
+    }
+
+    private void range_check( int i ) {
+        if( i < 0 || i>=_len )
+            throw new ArrayIndexOutOfBoundsException(""+i+" >= "+_len);
+    }
+
+    // Note that the hashCode() and equals() are not invariant to changes in the
+    // underlying array.  If the hashCode() is used (e.g., inserting into a
+    // HashMap) and the then the array changes, the hashCode() will change also.
+    @Override public boolean equals( Object o ) {
+        if( this==o ) return true;
+        if( !(o instanceof Ary ary) ) return false;
+        if( _len != ary._len ) return false;
+        if( _es == ary._es ) return true;
+        for( int i=0; i<_len; i++ )
+            if( !(_es[i]==null ? (ary._es[i] == null) : _es[i].equals(ary._es[i])) )
+                return false;
+        return true;
+    }
+    @Override public int hashCode( ) {
+        int sum=_len;
+        for( int i=0; i<_len; i++ )
+            sum += _es[i]==null ? 0 : _es[i].hashCode();
+        return sum;
+    }
+
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java
new file mode 100644
index 0000000..4798830
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java
@@ -0,0 +1,983 @@
+package com.compilerprogramming.ezlang.compiler;
+
+import com.compilerprogramming.ezlang.compiler.codegen.CodeGen;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+import com.compilerprogramming.ezlang.compiler.print.GraphVisualizer;
+import com.compilerprogramming.ezlang.compiler.sontypes.*;
+import com.compilerprogramming.ezlang.exceptions.CompilerException;
+import com.compilerprogramming.ezlang.lexer.Lexer;
+import com.compilerprogramming.ezlang.parser.AST;
+import com.compilerprogramming.ezlang.parser.Parser;
+import com.compilerprogramming.ezlang.semantic.SemaAssignTypes;
+import com.compilerprogramming.ezlang.semantic.SemaDefineTypes;
+import com.compilerprogramming.ezlang.types.Scope;
+import com.compilerprogramming.ezlang.types.Symbol;
+import com.compilerprogramming.ezlang.types.Type;
+import com.compilerprogramming.ezlang.types.TypeDictionary;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Stack;
+
+public class Compiler {
+
+    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
+
+    TypeDictionary typeDictionary;
+
+    // Compile driver
+    public CodeGen _code;
+
+    /**
+     * Current ScopeNode - ScopeNodes change as we parse code, but at any point of time
+     * there is one current ScopeNode. The reason the current ScopeNode can change is to do with how
+     * we handle branching.
+     * <p>
+     * Each ScopeNode contains a stack of lexical scopes, each scope is a symbol table that binds
+     * variable names to Nodes.  The top of this stack represents current scope.
+     * <p>
+     * We keep a list of all ScopeNodes so that we can show them in graphs
+     */
+    public ScopeNode _scope;
+
+    /**
+     * We clone ScopeNodes when control flows branch; it is useful to have
+     * a list of all active ScopeNodes for purposes of visualization of the SoN graph
+     */
+    public final Stack<ScopeNode> _xScopes = new Stack<>();
+
+    ScopeNode _continueScope;
+    ScopeNode _breakScope;      // Merge all the while-breaks here
+    FunNode _fun;               // Current function being parsed
+
+
+    // Mapping from a type name to a Type.  The string name matches
+    // `type.str()` call.  No TypeMemPtrs are in here, because Simple does not
+    // have C-style '*ptr' references.
+    public static HashMap<String, SONType> TYPES = new HashMap<>();
+
+    private ArrayList<Node> ctorStack = new ArrayList<>();
+
+    public Compiler(CodeGen codeGen, SONTypeInteger arg) {
+        this._code = codeGen;
+    }
+
+    public void parse() {
+        this.typeDictionary = createAST(_code._src);
+        Map<String, SONType> types = new HashMap<>();
+        populateDefaultTypes(types);
+        populateTypes(types);
+        TYPES.putAll(types);
+        ZERO  = con(SONTypeInteger.ZERO).keep();
+        NIL  = con(SONType.NIL).keep();
+        XCTRL= new XCtrlNode().peephole().keep();
+        processFunctions();
+    }
+
+    public TypeDictionary createAST(String src) {
+        Parser parser = new Parser();
+        var program = parser.parse(new Lexer(src));
+        var typeDict = new TypeDictionary();
+        var sema = new SemaDefineTypes(typeDict);
+        sema.analyze(program);
+        var sema2 = new SemaAssignTypes(typeDict);
+        sema2.analyze(program);
+        return typeDict;
+    }
+
+
+    private void populateDefaultTypes(Map<String,SONType> types) {
+        types.put(typeDictionary.INT.name(), SONTypeInteger.BOT);
+        for (SONType t: SONType.gather()) {
+            types.put(t.str(), t);
+        }
+    }
+
+    private void populateTypes(Map<String, SONType> structTypes) {
+        // First process struct types
+        for (var symbol: typeDictionary.getLocalSymbols()) {
+            if (symbol instanceof Symbol.TypeSymbol typeSymbol) {
+                if (typeSymbol.type instanceof Type.TypeStruct typeStruct) {
+                    createSONStructType(structTypes,typeSymbol.name, typeStruct);
+                }
+                else if (typeSymbol.type instanceof Type.TypeArray typeArray) {
+                    getSONType(structTypes, typeArray);
+                }
+            }
+        }
+        // Next process function types
+        for (var symbol: typeDictionary.getLocalSymbols()) {
+            if (symbol instanceof Symbol.FunctionTypeSymbol functionTypeSymbol) {
+                createFunctionType(structTypes, functionTypeSymbol);
+            }
+        }
+    }
+
+    private void processFunctions() {
+        for (var symbol: typeDictionary.getLocalSymbols()) {
+            if (symbol instanceof Symbol.FunctionTypeSymbol functionTypeSymbol) {
+                generateFunction(functionTypeSymbol);
+            }
+        }
+    }
+
+    private void createFunctionType(Map<String, SONType> structTypes, Symbol.FunctionTypeSymbol functionSymbol) {
+        Type.TypeFunction functionType = (Type.TypeFunction) functionSymbol.type;
+        Ary<SONType> params = new Ary<>(SONType.class);
+        for (var symbol: functionType.args) {
+            if (symbol instanceof Symbol.ParameterSymbol parameterSymbol) {
+                SONType paramType = getSONType(structTypes, parameterSymbol.type);
+                params.push(paramType);
+            }
+        }
+        var retType = getSONType(structTypes, functionType.returnType);
+        SONTypeFunPtr tfp = _code.makeFun2(new SONTypeTuple(params.asAry()),retType);
+        structTypes.put(functionSymbol.name, tfp);
+    }
+
+    private void createSONStructType(Map<String, SONType> structTypes, String typeName, Type.TypeStruct typeStruct) {
+        Ary<Field> fs = new Ary<>(Field.class);
+        for (int i = 0; i < typeStruct.numFields(); i++) {
+            String name = typeStruct.getFieldName(i);
+            Type type = typeStruct.getField(name); // FIXME
+            fs.push(new Field(name,getSONType(structTypes,type),_code.getALIAS(),false));
+        }
+        SONType fref = structTypes.get(typeName);
+        if (fref != null) {
+            if (fref instanceof SONTypeStruct ts) {
+                ts._fields = fs.asAry();
+            }
+            else throw new CompilerException("");
+        }
+        else {
+            structTypes.put(typeName, new SONTypeStruct(typeName, fs.asAry()));
+        }
+//        SONTypeStruct ts = SONTypeStruct.make(typeName, fs.asAry());
+//        TYPES.put(typeName, SONTypeMemPtr.make(ts));
+    }
+
+    private String getSONTypeName(Type type) {
+        if (type instanceof Type.TypeFunction typeFunction) {
+            return typeFunction.name;
+        }
+        else if (type instanceof Type.TypeArray typeArray) {
+            return typeArray.name();
+        }
+        else if (type instanceof Type.TypeStruct typeStruct) {
+            return "*" + typeStruct.name;
+        }
+        else if (type instanceof Type.TypeInteger ||
+                 type instanceof Type.TypeVoid) {
+            return SONTypeInteger.BOT.str();
+        }
+        else if (type instanceof Type.TypeNull) {
+            return SONTypeNil.NIL.str();
+        }
+        else if (type instanceof Type.TypeNullable typeNullable) {
+            return getSONTypeName(typeNullable.baseType)+"?";
+        }
+        else throw new CompilerException("Not yet implemented " + type.name());
+    }
+
+    private SONType getSONType(Map<String, SONType> structTypes, Type type) {
+        String tname = getSONTypeName(type);
+        SONType t = structTypes.get(tname);
+        if (t != null) return t;
+        if (type instanceof Type.TypeStruct) {
+            SONType existing = structTypes.get(type.name);
+            SONTypeStruct ts;
+            if (existing instanceof SONTypeStruct tsExisting)
+                ts = tsExisting;
+            else {
+                ts = new SONTypeStruct(type.name, new Field[0]);
+                structTypes.put(ts.str(), ts);
+            }
+            SONTypeMemPtr ptr = new SONTypeMemPtr((byte)2,ts);
+            if (type.name().equals(ts.str())) {
+                structTypes.put(ptr.str(), ptr);
+                return ptr;
+            }
+            else throw new CompilerException("Unexpected error");
+        }
+        else if (type instanceof Type.TypeArray typeArray) {
+            SONType elementType = getSONType(structTypes,typeArray.getElementType());
+            SONTypeStruct ts = null;
+            SONType existing = structTypes.get(typeArray.name());
+            if (existing instanceof SONTypeStruct tsExisting)
+                ts = tsExisting;
+            else {
+                ts = SONTypeStruct.makeArray(SONTypeInteger.U32, _code.getALIAS(), elementType, _code.getALIAS());
+                structTypes.put(ts.str(), ts);
+            }
+            SONTypeMemPtr ptr = new SONTypeMemPtr((byte)2,ts);
+            structTypes.put(typeArray.name(), ptr); // Array type name is not same as ptr str()
+            return ptr;
+        }
+        else if (type instanceof Type.TypeNullable typeNullable) {
+            SONType baseType = getSONType(structTypes,typeNullable.baseType);
+            SONTypeMemPtr ptr = null;
+            if (baseType instanceof SONTypeMemPtr ptr1) {
+                if (ptr1.nullable())
+                    ptr = ptr1;
+                else
+                    ptr = new SONTypeMemPtr((byte)3,ptr1._obj);
+            }
+            else
+                ptr = new SONTypeMemPtr((byte)2,(SONTypeStruct) baseType);
+            structTypes.put(ptr.str(), ptr);
+            return ptr;
+        }
+        else if (type instanceof Type.TypeVoid) {
+            return structTypes.get("Int"); // Only allowed in return types
+        }
+        throw new CompilerException("Count not find type " + type.name());
+    }
+
+    private Node ctrl() { return _scope.ctrl(); }
+    private <N extends Node> N ctrl(N n) { return _scope.ctrl(n); }
+
+    // TODO because first two slots are MEM and RPC
+    private int REGNUM = 2;
+    private void setVarIds(Scope scope, ScopeNode scopeNode, FunNode fun) {
+        for (Symbol symbol: scope.getLocalSymbols()) {
+            if (symbol instanceof Symbol.VarSymbol varSymbol) {
+                varSymbol.regNumber = REGNUM++;
+                String sonTypeName = getSONTypeName(varSymbol.type);
+                SONType sonType = TYPES.get(sonTypeName);
+                if (sonType == null) throw new CompilerException("Unknown SON Type "+sonTypeName);
+                Node init = null;
+
+                if (varSymbol instanceof Symbol.ParameterSymbol) {
+                    init = new ParmNode(makeVarName(varSymbol),varSymbol.regNumber,sonType,fun,con(sonType)).peephole();
+                }
+                scopeNode.define(makeVarName(varSymbol), sonType, false, init);
+            }
+        }
+        for (Scope childScope: scope.children) {
+            setVarIds(childScope, scopeNode, fun);
+        }
+    }
+
+    private void generateFunction(Symbol.FunctionTypeSymbol functionTypeSymbol) {
+        _scope = new ScopeNode();
+        _scope.define(ScopeNode.CTRL, SONType.CONTROL   , false, null);
+        _scope.define(ScopeNode.MEM0, SONTypeMem.BOT    , false, null);
+
+        ctrl(XCTRL);
+        _scope.mem(new MemMergeNode(false));
+
+        var funType = (SONTypeFunPtr) TYPES.get(functionTypeSymbol.name);
+        if (funType == null) throw new CompilerException("Function " + functionTypeSymbol.name + " not found");
+
+        // Parse whole program, as-if function header "{ int arg -> body }"
+        generateFunctionBody(functionTypeSymbol,funType);
+        _code.link(funType)._name = functionTypeSymbol.name;
+
+        // Clean up and reset
+        //_xScopes.pop();
+        _scope.kill();
+//        for( StructNode init : INITS.values() )
+//            init.unkeep().kill();
+//        INITS.clear();
+        _code._stop.peephole();
+//        if( show ) showGraph();
+        showGraph();
+    }
+
+    /**
+     * Dumps out the node graph
+     * @return {@code null}
+     */
+    Node showGraph() {
+        System.out.println(new GraphVisualizer().generateDotOutput(_code._stop,_scope,_xScopes));
+        return null;
+    }
+
+    /**
+     *  Parses a function body, assuming the header is parsed.
+     */
+    private ReturnNode generateFunctionBody(Symbol.FunctionTypeSymbol functionTypeSymbol,SONTypeFunPtr sig) {
+        // Stack parser state on the local Java stack, and unstack it later
+        Node oldctrl = ctrl().keep();
+        Node oldmem  = _scope.mem().keep();
+        FunNode oldfun  = _fun;
+        ScopeNode breakScope = _breakScope; _breakScope = null;
+        ScopeNode continueScope = _continueScope; _continueScope = null;
+
+        FunNode fun = _fun = (FunNode)peep(new FunNode(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).
+        _code.link(fun);
+
+        Node rpc = new ParmNode("$rpc",0,SONTypeRPC.BOT,fun,con(SONTypeRPC.BOT)).peephole();
+
+        // Build a multi-exit return point for all function returns
+        RegionNode r = new RegionNode(null,null).init();
+        assert r.inProgress();
+        PhiNode rmem = new PhiNode(ScopeNode.MEM0,SONTypeMem.BOT,r,null).init();
+        PhiNode rrez = new PhiNode(ScopeNode.ARG0,SONType.BOTTOM,r,null).init();
+        ReturnNode ret = new ReturnNode(r, rmem, rrez, rpc, fun).init();
+        fun.setRet(ret);
+        assert ret.inProgress();
+        _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
+        // external calls.
+        _scope.push(ScopeNode.Kind.Function);
+        ctrl(fun);              // Scope control from function
+        // Private mem alias tracking per function
+        MemMergeNode mem = new MemMergeNode(true);
+        mem.addDef(null);       // Alias#0
+        mem.addDef(new ParmNode(ScopeNode.MEM0,1,SONTypeMem.BOT,fun,con(SONTypeMem.BOT)).peephole()); // All aliases
+        _scope.mem(mem);
+        // All args, "as-if" called externally
+        AST.FuncDecl funcDecl = (AST.FuncDecl) functionTypeSymbol.functionDecl;
+        setVarIds(funcDecl.scope,_scope,fun);
+
+        // Parse the body
+        Node last = compileStatement(funcDecl.block);
+
+        // Last expression is the return
+        if( ctrl()._type==SONType.CONTROL )
+            fun.addReturn(ctrl(), _scope.mem().merge(), last);
+
+        // Pop off the inProgress node on the multi-exit Region merge
+        assert r.inProgress();
+        r   ._inputs.pop();
+        rmem._inputs.pop();
+        rrez._inputs.pop();
+        assert !r.inProgress();
+
+        // Force peeps, which have been avoided due to inProgress
+        ret.setDef(1,rmem.peephole());
+        ret.setDef(2,rrez.peephole());
+        ret.setDef(0,r.peephole());
+        ret = (ReturnNode)ret.peephole();
+
+        // Function scope ends
+        _scope.pop();
+        _fun = oldfun;
+        _breakScope = breakScope;
+        _continueScope = continueScope;
+        // Reset control and memory to pre-function parsing days
+        ctrl(oldctrl.unkeep());
+        _scope.mem(oldmem.unkeep());
+
+        return ret;
+    }
+
+    private Node compileExpr(AST.Expr expr) {
+        switch (expr) {
+            case AST.LiteralExpr constantExpr -> {
+                return compileConstantExpr(constantExpr);
+            }
+            case AST.BinaryExpr binaryExpr -> {
+                return compileBinaryExpr(binaryExpr);
+            }
+            case AST.UnaryExpr unaryExpr -> {
+                return compileUnaryExpr(unaryExpr);
+            }
+            case AST.NameExpr symbolExpr -> {
+                return compileSymbolExpr(symbolExpr);
+            }
+            case AST.NewExpr newExpr -> {
+                return compileNewExpr(newExpr);
+            }
+            case AST.InitExpr initExpr -> {
+                return compileInitExpr(initExpr);
+            }
+            case AST.ArrayLoadExpr arrayLoadExpr -> {
+                return compileArrayIndexExpr(arrayLoadExpr);
+            }
+            case AST.ArrayStoreExpr arrayStoreExpr -> {
+                return compileArrayStoreExpr(arrayStoreExpr);
+            }
+            case AST.GetFieldExpr getFieldExpr -> {
+                return compileFieldExpr(getFieldExpr);
+            }
+            case AST.SetFieldExpr setFieldExpr -> {
+                return compileSetFieldExpr(setFieldExpr);
+            }
+            case AST.CallExpr callExpr -> {
+                return compileCallExpr(callExpr);
+            }
+            default -> throw new IllegalStateException("Unexpected value: " + expr);
+        }
+    }
+
+    private Node compileFieldExpr(AST.GetFieldExpr getFieldExpr) {
+        Node objPtr = compileExpr(getFieldExpr.object).keep();
+        // Sanity check expr for being a reference
+        if( !(objPtr._type instanceof SONTypeMemPtr ptr) ) {
+            throw new CompilerException("Unexpected type " + objPtr._type.str());
+        }
+        String name = getFieldExpr.fieldName;
+        SONTypeStruct base = ptr._obj;
+        int fidx = base.find(name);
+        if( fidx == -1 ) throw error("Accessing unknown field '" + name + "' from '" + ptr.str() + "'");
+        Field f = base._fields[fidx];  // Field from field index
+        SONType tf = f._type;
+        // Field offset; fixed for structs
+        Node off = con(base.offset(fidx)).keep();
+        Node load = new LoadNode(name, f._alias, tf, memAlias(f._alias), objPtr, off);
+        // 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.
+        load = peep(load);
+        objPtr.unkeep();
+        off.unkeep();
+        return load;
+    }
+
+    private Node compileSetFieldExpr(AST.SetFieldExpr setFieldExpr) {
+        Node objPtr;
+        if (setFieldExpr instanceof AST.InitFieldExpr)
+            objPtr = ctorStack.getLast();
+        else
+            objPtr = compileExpr(setFieldExpr.object);
+        // Sanity check expr for being a reference
+        if( !(objPtr._type instanceof SONTypeMemPtr ptr) ) {
+            throw new CompilerException("Unexpected type " + objPtr._type.str());
+        }
+        Node val = compileExpr(setFieldExpr.value).keep();
+        String name = setFieldExpr.fieldName;
+        SONTypeStruct base = ptr._obj;
+        int fidx = base.find(name);
+        if( fidx == -1 ) throw error("Accessing unknown field '" + name + "' from '" + ptr.str() + "'");
+        Field f = base._fields[fidx];  // Field from field index
+        SONType tf = f._type;
+        Node mem = memAlias(f._alias);
+        Node st = new StoreNode(f._fname,f._alias,tf,mem,objPtr,con(base.offset(fidx)),val.unkeep(),true).peephole();
+        memAlias(f._alias,st);
+        return objPtr;
+    }
+
+    private Node compileArrayIndexExpr(AST.ArrayLoadExpr arrayLoadExpr) {
+        Node objPtr = compileExpr(arrayLoadExpr.array).keep();
+        // Sanity check expr for being a reference
+        if( !(objPtr._type instanceof SONTypeMemPtr ptr) ) {
+            throw new CompilerException("Unexpected type " + objPtr._type.str());
+        }
+        String name = "[]";
+        SONTypeStruct base = ptr._obj;
+        Node index = compileExpr(arrayLoadExpr.expr).keep();
+        Node off = peep(new AddNode(con(base.aryBase()),peep(new ShlNode(index.unkeep(),con(base.aryScale()))))).keep();
+        int fidx = base.find(name);
+        if( fidx == -1 ) throw error("Accessing unknown field '" + name + "' from '" + ptr.str() + "'");
+        Field f = base._fields[fidx];  // Field from field index
+        SONType tf = f._type;
+        Node load = new LoadNode(name, f._alias, tf, memAlias(f._alias), objPtr, off);
+        // 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.
+        load.setDef(0,ctrl());
+        load = peep(load);
+        objPtr.unkeep();
+        off.unkeep();
+        return load;
+    }
+
+    private Node compileArrayStoreExpr(AST.ArrayStoreExpr arrayStoreExpr) {
+        Node objPtr;
+        if (arrayStoreExpr instanceof AST.ArrayInitExpr)
+            objPtr = ctorStack.getLast();
+        else
+            objPtr = compileExpr(arrayStoreExpr.array);
+        // Sanity check expr for being a reference
+        if( !(objPtr._type instanceof SONTypeMemPtr ptr) ) {
+            throw new CompilerException("Unexpected type " + objPtr._type.str());
+        }
+        String name = "[]";
+        SONTypeStruct base = ptr._obj;
+        Node index = compileExpr(arrayStoreExpr.expr).keep();
+        Node off = peep(new AddNode(con(base.aryBase()),peep(new ShlNode(index.unkeep(),con(base.aryScale()))))).keep();
+        Node val = compileExpr(arrayStoreExpr.value).keep();
+        int fidx = base.find(name);
+        if( fidx == -1 ) throw error("Accessing unknown field '" + name + "' from '" + ptr.str() + "'");
+        Field f = base._fields[fidx];  // Field from field index
+        SONType tf = f._type;
+        Node mem = memAlias(f._alias);
+        Node st = new StoreNode(f._fname,f._alias,tf,mem,objPtr,off.unkeep(),val.unkeep(),true).peephole();
+        memAlias(f._alias,st);
+        return objPtr;
+    }
+
+    private Node compileInitExpr(AST.InitExpr initExpr) {
+        Node objPtr = compileExpr(initExpr.newExpr).keep();
+        ctorStack.add(objPtr);
+        // Sanity check expr for being a reference
+        if( !(objPtr._type instanceof SONTypeMemPtr ptr) ) {
+            throw new CompilerException("Unexpected type " + objPtr._type.str());
+        }
+        if (initExpr.initExprList != null && !initExpr.initExprList.isEmpty()) {
+            for (AST.Expr expr : initExpr.initExprList) {
+                compileExpr(expr);
+            }
+        }
+        ctorStack.removeLast();
+        return objPtr.unkeep();
+    }
+
+    private Node newArray(SONTypeStruct ary, Node len) {
+        int base = ary.aryBase ();
+        int scale= ary.aryScale();
+        Node size = peep(new AddNode(con(base),peep(new ShlNode(len.keep(),con(scale)))));
+        return newStruct(ary,size);
+    }
+    /**
+     * Return a NewNode initialized memory.
+     * @param obj is the declared type, with GLB fields
+     */
+    private Node newStruct( SONTypeStruct obj, Node size) {
+        Field[] fs = obj._fields;
+        if( fs==null )
+            throw error("Unknown struct type '" + obj._name + "'");
+        int len = fs.length;
+        Node[] ns = new Node[2+len];
+        ns[0] = ctrl();         // Control in slot 0
+        // Total allocated length in bytes
+        ns[1] = size;
+        // Memory aliases for every field
+        for( int i = 0; i < len; i++ )
+            ns[2+i] = memAlias(fs[i]._alias);
+        // FIXME make
+        Node nnn = new NewNode(SONTypeMemPtr.make(obj), ns).peephole().keep();
+        for( int i = 0; i < len; i++ )
+            memAlias(fs[i]._alias, new ProjNode(nnn,i+2,memName(fs[i]._alias)).peephole());
+        return new ProjNode(nnn.unkeep(),1,obj._name).peephole();
+    }
+
+    private Node compileNewExpr(AST.NewExpr newExpr) {
+        Type type = newExpr.type;
+        if (type instanceof Type.TypeArray typeArray) {
+            SONTypeMemPtr tarray = (SONTypeMemPtr) TYPES.get(getSONTypeName(typeArray));
+            return newArray(tarray._obj,ZERO);
+        }
+        else if (type instanceof Type.TypeStruct typeStruct) {
+            SONTypeStruct tstruct = (SONTypeStruct) TYPES.get(typeStruct.name());
+            return newStruct(tstruct,con(tstruct.offset(tstruct._fields.length)));
+        }
+        else
+            throw new CompilerException("Unexpected type: " + type);
+    }
+
+
+    private Node compileUnaryExpr(AST.UnaryExpr unaryExpr) {
+        String opCode = unaryExpr.op.str;
+        switch (opCode) {
+            case "-": return peep(new MinusNode(compileExpr(unaryExpr.expr)).widen());
+            // Maybe below we should explicitly set Int
+            case "!": return peep(new NotNode(compileExpr(unaryExpr.expr)));
+            default: throw new CompilerException("Invalid unary op");
+        }
+    }
+
+    private Node compileBinaryExpr(AST.BinaryExpr binaryExpr) {
+        String opCode = binaryExpr.op.str;
+        int idx=0;
+        boolean negate = false;
+        var lhs = compileExpr(binaryExpr.expr1);
+        switch (opCode) {
+            case "&&":
+            case "||":
+                throw new CompilerException("Not yet implemented");
+            case "==":
+                idx=2;  lhs = new BoolNode.EQ(lhs, null);
+                break;
+            case "!=":
+                idx=2;  lhs = new BoolNode.EQ(lhs, null); negate=true;
+                break;
+            case "<=":
+                idx=2;  lhs = new BoolNode.LE(lhs, null);
+                break;
+            case "<":
+                idx=2;  lhs = new BoolNode.LT(lhs, null);
+                break;
+            case ">=":
+                idx=1;  lhs = new BoolNode.LE(null, lhs);
+                break;
+            case ">":
+                idx=1;  lhs = new BoolNode.LT(null, lhs);
+                break;
+            case "+":
+                idx=2; lhs = new AddNode(lhs,null);
+                break;
+            case "-":
+                idx=2; lhs = new SubNode(lhs,null);
+                break;
+            case "*":
+                idx=2; lhs = new MulNode(lhs,null);
+                break;
+            case "/":
+                idx=2; lhs = new DivNode(lhs,null);
+                break;
+            default:
+                throw new CompilerException("Not yet implemented");
+        }
+        lhs.setDef(idx,compileExpr(binaryExpr.expr2));
+        lhs = peep(lhs.widen());
+        if( negate )        // Extra negate for !=
+            lhs = peep(new NotNode(lhs));
+        return lhs;
+    }
+
+    private Node compileConstantExpr(AST.LiteralExpr constantExpr) {
+        if (constantExpr.type instanceof Type.TypeInteger)
+            return con(constantExpr.value.num.intValue());
+        else if (constantExpr.type instanceof Type.TypeNull)
+            return NIL;
+        else throw new CompilerException("Invalid constant type");
+    }
+
+    private Node compileSymbolExpr(AST.NameExpr symbolExpr) {
+        if (symbolExpr.type instanceof Type.TypeFunction functionType)
+            return con(TYPES.get(functionType.name));
+        else {
+            Symbol.VarSymbol varSymbol = (Symbol.VarSymbol) symbolExpr.symbol;
+            Var v = _scope.lookup(makeVarName(varSymbol));
+            if (v == null) throw new CompilerException("Unknown variable name: " + varSymbol.name);
+            Node n = _scope.in(v._idx);
+            if (n == null) throw new CompilerException("Variable " + varSymbol.name + " not defined");
+            return n;
+        }
+    }
+
+    /**
+     *  Parse function call arguments; caller will parse the surrounding "()"
+     * <pre>
+     *   ( arg* )
+     * </pre>
+     */
+    private Node compileCallExpr(AST.CallExpr callExpr) {
+        Node expr = compileExpr(callExpr.callee);
+        if( expr._type == SONType.NIL )
+            throw error("Calling a null function pointer");
+        if( !(expr instanceof FRefNode) && !expr._type.isa(SONTypeFunPtr.BOT) )
+            throw error("Expected a function but got "+expr._type.glb().str());
+        expr.keep();            // Keep while parsing args
+
+        Ary<Node> args = new Ary<Node>(Node.class);
+        args.push(null);        // Space for ctrl,mem
+        args.push(null);
+        for (AST.Expr e: callExpr.args) {
+            Node arg = compileExpr(e);
+            args.push(arg.keep());
+        }
+        // Control & memory after parsing args
+        args.set(0,ctrl().keep());
+        args.set(1,_scope.mem().merge().keep());
+        args.push(expr);        // Function pointer
+        // Unkeep them all
+        for( Node arg : args )
+            arg.unkeep();
+        // Dead into the call?  Skip all the node gen
+        if( ctrl()._type == SONType.XCONTROL ) {
+            for( Node arg : args )
+                if( arg.isUnused() )
+                    arg.kill();
+            return con(SONType.TOP);
+        }
+
+        // Into the call
+        CallNode call = (CallNode)new CallNode(args.asAry()).peephole();
+
+        // Post-call setup
+        CallEndNode cend = (CallEndNode)new CallEndNode(call).peephole();
+        // Control from CallEnd
+        ctrl(new CProjNode(cend,0,ScopeNode.CTRL).peephole());
+        // Memory from CallEnd
+        MemMergeNode mem = new MemMergeNode(true);
+        mem.addDef(null);       // Alias#0
+        mem.addDef(new ProjNode(cend,1,ScopeNode.MEM0).peephole());
+        _scope.mem(mem);
+        // Call result
+        return new ProjNode(cend,2,"#2").peephole();
+    }
+
+    private String makeVarName(Symbol.VarSymbol varSymbol) {
+        return varSymbol.name + "$" + varSymbol.regNumber;
+    }
+
+    private Node compileStatement(AST.Stmt statement) {
+        switch (statement) {
+            case AST.BlockStmt blockStmt -> {
+                return compileBlock(blockStmt);
+            }
+            case AST.VarStmt letStmt -> {
+                return compileLet(letStmt);
+            }
+            case AST.VarDeclStmt varDeclStmt -> {}
+            case AST.IfElseStmt ifElseStmt -> {
+                return compileIf(ifElseStmt);
+            }
+            case AST.WhileStmt whileStmt -> {
+                return compileWhile(whileStmt);
+            }
+            case AST.ContinueStmt continueStmt -> {
+                return compileContinue(continueStmt);
+            }
+            case AST.BreakStmt breakStmt -> {
+                return compileBreak(breakStmt);
+            }
+            case AST.ReturnStmt returnStmt -> {
+                return compileReturn(returnStmt);
+            }
+            case AST.AssignStmt assignStmt -> {
+                return compileAssign(assignStmt);
+            }
+            case AST.ExprStmt exprStmt -> {
+                return compileExprStmt(exprStmt);
+            }
+            default -> throw new IllegalStateException("Unexpected value: " + statement);
+        }
+        throw new CompilerException("Not yet implemented");
+    }
+
+    private ScopeNode jumpTo(ScopeNode toScope) {
+        ScopeNode cur = _scope.dup();
+        ctrl(XCTRL); // Kill current scope
+        // Prune nested lexical scopes that have depth > than the loop head
+        // We use _breakScope as a proxy for the loop head scope to obtain the depth
+        while( cur._lexSize.size() > _breakScope._lexSize.size() )
+            cur.pop();
+        // If this is a continue then first time the target is null
+        // So we just use the pruned current scope as the base for the
+        // "continue"
+        if( toScope == null )
+            return cur;
+        // toScope is either the break scope, or a scope that was created here
+        assert toScope._lexSize.size() <= _breakScope._lexSize.size();
+        toScope.ctrl(toScope.mergeScopes(cur).peephole());
+        return toScope;
+    }
+    private void checkLoopActive() { if (_breakScope == null) throw error("No active loop for a break or continue"); }
+
+    private Node compileBreak(AST.BreakStmt breakStmt) {
+        checkLoopActive();
+        // At the time of the break, and loop-exit conditions are only valid if
+        // they are ALSO valid at the break.  It is the intersection of
+        // conditions here, not the union.
+        _breakScope.removeGuards(_breakScope.ctrl());
+        _breakScope = jumpTo(_breakScope );
+        _breakScope.addGuards(_breakScope.ctrl(), null, false);
+        return ZERO;
+    }
+
+    private Node compileContinue(AST.ContinueStmt continueStmt) {
+        checkLoopActive(); _continueScope = jumpTo( _continueScope ); return ZERO;
+    }
+
+    private Node compileWhile(AST.WhileStmt whileStmt) {
+        var savedContinueScope = _continueScope;
+        var savedBreakScope    = _breakScope;
+
+        // Loop region has two control inputs, the first is the entry
+        // point, and second is back edge that is set after loop is parsed
+        // (see end_loop() call below).  Note that the absence of back edge is
+        // used as an indicator to switch off peepholes of the region and
+        // associated phis; see {@code inProgress()}.
+
+        ctrl(new LoopNode(ctrl()).peephole()); // Note we set back edge to null here
+
+        // At loop head, we clone the current Scope (this includes all
+        // names in every nesting level within the Scope).
+        // We create phis eagerly for all the names we find, see dup().
+
+        // Save the current scope as the loop head
+        ScopeNode head = _scope.keep();
+        // Clone the head Scope to create a new Scope for the body.
+        // Create phis eagerly as part of cloning
+        _xScopes.push(_scope = _scope.dup(true)); // The true argument triggers creating phis
+
+        // Parse predicate
+        var pred = compileExpr(whileStmt.condition);
+
+        // IfNode takes current control and predicate
+        Node ifNode = new IfNode(ctrl(), pred.keep()).peephole();
+        // Setup projection nodes
+        Node ifT = new CProjNode(ifNode.  keep(), 0, "True" ).peephole().keep();
+        Node ifF = new CProjNode(ifNode.unkeep(), 1, "False").peephole();
+
+        // Clone the body Scope to create the break/exit Scope which accounts for any
+        // side effects in the predicate.  The break/exit Scope will be the final
+        // scope after the loop, and its control input is the False branch of
+        // the loop predicate.  Note that body Scope is still our current scope.
+        ctrl(ifF);
+        _xScopes.push(_breakScope = _scope.dup());
+        _breakScope.addGuards(ifF,pred,true); // Up-cast predicate
+
+        // No continues yet
+        _continueScope = null;
+
+        // Parse the true side, which corresponds to loop body
+        // 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
+        compileStatement(whileStmt.stmt);       // Parse loop body
+        _scope.removeGuards(ifT);
+
+        // Merge the loop bottom into other continue statements
+        if (_continueScope != null) {
+            _continueScope = jumpTo(_continueScope);
+            _scope.kill();
+            _scope = _continueScope;
+        }
+
+
+        // The true branch loops back, so whatever is current _scope.ctrl gets
+        // added to head loop as input.  endLoop() updates the head scope, and
+        // goes through all the phis that were created earlier.  For each phi,
+        // it sets the second input to the corresponding input from the back
+        // edge.  If the phi is redundant, it is replaced by its sole input.
+        var exit = _breakScope;
+        head.endLoop(_scope, exit);
+        head.unkeep().kill();
+
+        _xScopes.pop();       // Cleanup
+        _xScopes.pop();       // Cleanup
+
+        _continueScope = savedContinueScope;
+        _breakScope = savedBreakScope;
+
+        // At exit the false control is the current control, and
+        // the scope is the exit scope after the exit test.
+        _xScopes.pop();
+        _xScopes.push(exit);
+        _scope = exit;
+        return ZERO;
+    }
+
+    private Node compileIf(AST.IfElseStmt ifElseStmt) {
+        var pred = compileExpr(ifElseStmt.condition).keep();
+        pred.keep();
+
+        // IfNode takes current control and predicate
+        Node ifNode = new IfNode(ctrl(), pred).peephole();
+        // Setup projection nodes
+        Node ifT = new CProjNode(ifNode.  keep(), 0, "True" ).peephole().keep();
+        Node ifF = new CProjNode(ifNode.unkeep(), 1, "False").peephole().keep();
+        // In if true branch, the ifT proj node becomes the ctrl
+        // But first clone the scope and set it as current
+        ScopeNode fScope = _scope.dup(); // Duplicate current scope
+        _xScopes.push(fScope); // For graph visualization we need all scopes
+
+        // Parse the true side
+        ctrl(ifT.unkeep());     // set ctrl token to ifTrue projection
+        _scope.addGuards(ifT,pred,false); // Up-cast predicate
+        Node lhs = compileStatement(ifElseStmt.ifStmt).keep(); // Parse true-side
+        _scope.removeGuards(ifT);
+
+        // See if a one-sided def was made: "if(pred) int x = 1;" and throw.
+        // See if any forward-refs were made, and copy them to the other side:
+        // "pred ? n*fact(n-1) : 1"
+        fScope.balanceIf(_scope);
+
+        ScopeNode tScope = _scope;
+
+        // Parse the false side
+        _scope = fScope;        // Restore scope, then parse else block if any
+        ctrl(ifF.unkeep());     // Ctrl token is now set to ifFalse projection
+        // Up-cast predicate, even if not else clause, because predicate can
+        // remain true if the true clause exits: `if( !ptr ) return 0; return ptr.fld;`
+        _scope.addGuards(ifF,pred,true);
+        boolean doRHS = ifElseStmt.elseStmt != null;
+        Node rhs = (doRHS
+            ? compileStatement(ifElseStmt.elseStmt)
+            : con(lhs._type.makeZero())).keep();
+        _scope.removeGuards(ifF);
+        if( doRHS )
+            fScope = _scope;
+        pred.unkeep();
+
+        _scope = tScope;
+        _xScopes.pop();       // Discard pushed from graph display
+        // See if a one-sided def was made: "if(pred) int x = 1;" and throw.
+        // See if any forward-refs were made, and copy them to the other side:
+        // "pred ? n*fact(n-1) : 1"
+        tScope.balanceIf(fScope);
+
+        // Merge results
+        RegionNode r = ctrl(tScope.mergeScopes(fScope));
+        Node ret = peep(new PhiNode("",lhs._type.meet(rhs._type),r,lhs.unkeep(),rhs.unkeep()));
+        r.peephole();
+        return ret;
+    }
+
+    private Node compileExprStmt(AST.ExprStmt exprStmt) {
+        return compileExpr(exprStmt.expr);
+    }
+
+    private Node compileAssign(AST.AssignStmt assignStmt) {
+        if (!(assignStmt.lhs instanceof AST.NameExpr nameExpr))
+            throw new CompilerException("Assignment requires a nameExpr");
+        String name = nameExpr.name;
+        if (!(nameExpr.symbol instanceof Symbol.VarSymbol varSymbol))
+            throw new CompilerException("Assignment requires a variable name");
+        String scopedName = makeVarName(varSymbol);
+        // Parse rhs
+        Node expr = compileExpr(assignStmt.rhs);
+        Var def = _scope.lookup(scopedName);
+        if( def==null )
+            throw error("Undefined name '" + name + "'");
+        // Update
+        _scope.update(scopedName,expr);
+        return expr;
+    }
+
+    private Node compileLet(AST.VarStmt letStmt) {
+        String name = letStmt.varName;
+        String scopedName = makeVarName(letStmt.symbol);
+        // Parse rhs
+        Node expr = compileExpr(letStmt.expr);
+        Var def = _scope.lookup(scopedName);
+        if( def==null )
+            throw error("Undefined name '" + name + "'");
+        // Update
+        _scope.update(scopedName,expr);
+        return expr;
+    }
+
+    private Node compileReturn(AST.ReturnStmt returnStmt) {
+        var expr = compileExpr(returnStmt.expr);
+        // Need default memory, since it can be lazy, need to force
+        // a non-lazy Phi
+        _fun.addReturn(ctrl(), _scope.mem().merge(), expr);
+        ctrl(XCTRL);            // Kill control
+        return expr;
+    }
+
+    private Node compileBlock(AST.BlockStmt block) {
+        Node last = ZERO;
+        for (AST.Stmt stmt: block.stmtList) {
+            last = compileStatement(stmt);
+        }
+        return last;
+    }
+
+    public static Node con(long con ) { return con==0 ? ZERO : con(SONTypeInteger.constant(con));  }
+    public static ConstantNode con( SONType t ) { return (ConstantNode)new ConstantNode(t).peephole();  }
+    public Node peep( Node n ) {
+        // Peephole, then improve with lexically scoped guards
+        return _scope.upcastGuard(n.peephole());
+    }
+
+    // We set up memory aliases by inserting special vars in the scope these
+    // variables are prefixed by $ so they cannot be referenced in Simple code.
+    // Using vars has the benefit that all the existing machinery of scoping
+    // and phis work as expected
+    private Node memAlias(int alias         ) { return _scope.mem(alias    ); }
+    private void memAlias(int alias, Node st) {        _scope.mem(alias, st); }
+    public static String memName(int alias) { return ("$"+alias).intern(); }
+
+    CompilerException errorSyntax(String syntax) { return _errorSyntax("expected "+syntax);  }
+    private CompilerException _errorSyntax(String msg) {
+        return error("Syntax error, "+msg);
+    }
+    public static CompilerException error(String msg) { return new CompilerException(msg); }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/IterPeeps.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/IterPeeps.java
new file mode 100644
index 0000000..f58ae9f
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/IterPeeps.java
@@ -0,0 +1,197 @@
+package com.compilerprogramming.ezlang.compiler;
+
+import com.compilerprogramming.ezlang.compiler.codegen.CodeGen;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+//import com.seaofnodes.simple.print.JSViewer;
+import java.util.Arrays;
+import java.util.BitSet;
+import java.util.Random;
+
+/**
+ * The IterPeeps runs after parsing. It iterates the peepholes to a fixed point
+ * so that no more peepholes apply.  This should be linear because peepholes rarely
+ * (never?)  increase code size.  The graph should monotonically reduce in some
+ * dimension, which is usually size.  It might also reduce in e.g. number of
+ * MulNodes or Load/Store nodes, swapping out more "expensive" Nodes for cheaper
+ * ones.
+ * <br>
+ * The theoretical overall worklist is mindless just grabbing the next thing and
+ * doing it.  If the graph changes, put the neighbors on the worklist.
+ * <br>
+ * Lather, Rinse, Repeat until the worklist runs dry.
+ * <p>
+ * The main issues we have to deal with:
+ *
+ * <ul>
+ * <li>Nodes have uses; replacing some set of Nodes with another requires more graph
+ *   reworking.  Not rocket science, but it can be fiddly.  Its helpful to have a
+ *   small set of graph munging utilities, and the strong invariant that the graph
+ *   is stable and correct between peepholes.  In our case `Node.subsume` does
+ *   most of the munging, building on our prior stable Node utilities.</li>
+ *
+ * <li>Changing a Node also changes the graph "neighborhood".  The neighbors need to
+ *   be checked to see if THEY can also peephole, and so on.  After any peephole
+ *   or graph update we put a Nodes uses and defs on the worklist.</li>
+ *
+ * <li>Our strong invariant is that for all Nodes, either they are on the worklist
+ *   OR no peephole applies.  This invariant is easy to check, although expensive.
+ *   Basically the normal "iterate peepholes to a fixed point" is linear, and this
+ *   check is linear at each peephole step... so quadratic overall.  Its a useful
+ *   assert, but one we can disable once the overall algorithm is stable - and
+ *   then turn it back on again when some new set of peepholes is misbehaving.
+ *   The code for this is turned on in `IterPeeps.iterate` as `assert
+ *   progressOnList(stop);`</li>
+ * </ul>
+ */
+public class IterPeeps {
+
+    private final WorkList<Node> _work;
+
+    public IterPeeps( long seed ) { _work = new WorkList<>(seed); }
+
+    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 void iterate( CodeGen code ) {
+        assert progressOnList(code);
+        int cnt=0;
+
+        Node n;
+        while( (n=_work.pop()) != null ) {
+            if( n.isDead() )  continue;
+            cnt++;              // Useful for debugging, searching which peephole broke things
+            Node x = n.peepholeOpt();
+            if( x != null ) {
+                if( x.isDead() ) continue;
+                // peepholeOpt can return brand-new nodes, needing an initial type set
+                if( x._type==null ) x.setType(x.compute());
+                // 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);
+                    // Everybody gets a free "go again" in case they didn't get
+                    // made in their final form.
+                    _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);
+                        n.subsume(x);
+                    }
+                }
+                // If there are distant neighbors, move to worklist
+                n.moveDepsToWorklist();
+                //JSViewer.show(); // Show again
+                assert progressOnList(code); // Very expensive assert
+            }
+            if( n.isUnused() && !(n instanceof StopNode) )
+                n.kill();       // Just plain dead
+        }
+
+    }
+
+    // Visit ALL nodes and confirm the invariant:
+    //   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
+    // with debugging, the {@code assert} is broken out in a place where it is easy to
+    // stop if a change is found.
+
+    // Also, the normal usage of `iter()` may attempt peepholes with distance
+    // 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 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;
+                    m = n.peepholeOpt();
+                    if( m==null ) return null;
+                }
+                System.err.println("BREAK HERE FOR BUG");
+                return m;
+            });
+        code._midAssert = false;
+        return changed==null;
+    }
+
+    /**
+     * Classic WorkList, with a fast add/remove, dup removal, random pull.
+     * The Node's nid is used to check membership in the worklist.
+     */
+    public static class WorkList<E extends Node> {
+
+        private Node[] _es;
+        private int _len;
+        private final BitSet _on;   // Bit set if Node._nid is on WorkList
+        private final Random _R;    // For randomizing pull from the WorkList
+        private final long _seed;
+
+        /* Useful stat - how many nodes are processed in the post parse iterative opt */
+        private long _totalWork = 0;
+
+        public WorkList() { this(123); }
+        WorkList(long seed) {
+            _es = new Node[1];
+            _len=0;
+            _on = new BitSet();
+            _seed = seed;
+            _R = new Random();
+            _R.setSeed(_seed);
+        }
+
+        /**
+         * Pushes a Node on the WorkList, ensuring no duplicates
+         * If Node is null it will not be added.
+         */
+        public E push( E x ) {
+            if( x==null ) return null;
+            int idx = x._nid;
+            if( !_on.get(idx) ) {
+                _on.set(idx);
+                if( _len==_es.length )
+                    _es = Arrays.copyOf(_es,_len<<1);
+                _es[_len++] = x;
+                _totalWork++;
+            }
+            return x;
+        }
+
+        public void addAll( Ary<E> ary ) {
+            for( E n : ary )
+                push(n);
+        }
+
+        /**
+         * True if Node is on the WorkList
+         */
+        boolean on( E x ) { return _on.get(x._nid); }
+        boolean isEmpty() { return _len==0; }
+
+        /**
+         * Removes a random Node from the WorkList; null if WorkList is empty
+         */
+        public E pop() {
+            if( _len == 0 ) return null;
+            int idx = _R.nextInt(_len);
+            E x = (E)_es[idx];
+            _es[idx] = _es[--_len]; // Compress array
+            _on.clear(x._nid);
+            return x;
+        }
+
+        public void clear() {
+            _len = 0;
+            _on.clear();
+            _R.setSeed(_seed);
+            _totalWork = 0;
+        }
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/SB.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/SB.java
new file mode 100644
index 0000000..0ed652d
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/SB.java
@@ -0,0 +1,99 @@
+package com.compilerprogramming.ezlang.compiler;
+
+/** Tight/tiny StringBuilder wrapper.
+ *  Short short names on purpose; so they don't obscure the printing.
+ *  Can't believe this wasn't done long, long ago. */
+public final class SB {
+  private final StringBuilder _sb;
+  private int _indent = 0;
+  public SB(        ) { _sb = new StringBuilder( ); }
+  public SB(String s) { _sb = new StringBuilder(s); }
+  public SB p( String s ) { _sb.append(s); return this; }
+  public SB p( float  s ) {
+    if( Float.isNaN(s) )
+      _sb.append( "Float.NaN");
+    else if( Float.isInfinite(s) ) {
+      _sb.append(s > 0 ? "Float.POSITIVE_INFINITY" : "Float.NEGATIVE_INFINITY");
+    } else _sb.append(s);
+    return this;
+  }
+  public SB p( double s ) {
+    if( Double.isNaN(s) )
+      _sb.append("Double.NaN");
+    else if( Double.isInfinite(s) ) {
+      _sb.append(s > 0 ? "Double.POSITIVE_INFINITY" : "Double.NEGATIVE_INFINITY");
+    } else _sb.append(s);
+    return this;
+  }
+  public SB p( char   s ) { _sb.append(s); return this; }
+  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; }
+  // 1 byte, 2 hex digits, 8 bits
+  public SB hex1(int s) {
+    int digit = (s>>4) & 0xf;
+    _sb.append((char)((digit <= 9 ? '0' : ('A'-10))+digit));
+    digit = s & 0xf;
+    _sb.append((char)((digit <= 9 ? '0' : ('A'-10))+digit));
+    return this;
+  }
+  // 2 bytes, 4 hex digits, 16 bits, Big Endian
+  public SB hex2(int s) { return hex1(s>> 8).hex1(s); }
+  // 4 bytes, 8 hex digits, 32 bits, Big Endian
+  public SB hex4(int s) { return hex2(s>>16).hex2(s); }
+  // 8 bytes, 16 hex digits, 64 bits, Big Endian
+    public SB hex8(long s) { return hex4((int)(s>>32)).hex4((int)s); }
+
+  // Fixed width field
+  public SB fix( int sz, String s ) {
+    for( int i=0; i<sz; i++ )
+      _sb.append( i < s.length() ? s.charAt(i) : ' ');
+    return this;
+  }
+  public char at(int idx ) { return _sb.charAt(idx); }
+
+  // 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; }
+  public SB i( int d ) { for( int i=0; i<d+_indent; i++ ) p("  "); return this; }
+  public SB i( ) { return i(0); }
+  public SB ip(String s) { return i().p(s); }
+  public SB s() { _sb.append(' '); return this; }
+
+  // Increase indentation
+  public SB ii( int i) { _indent += i; return this; }
+  public SB ii() { return ii(1); }
+  // Decrease indentation
+  public SB di( int i) { _indent -= i; return this; }
+  public SB di() { return di(1); }
+
+  public SB nl( ) { return p(System.lineSeparator()); }
+  // Last printed a nl
+  public boolean was_nl() {
+    int len = _sb.length();
+    String nl = System.lineSeparator();
+    int nlen = nl.length();
+    if( len < nlen ) return false;
+    for( int i=0; i<nlen; i++ )
+      if( _sb.charAt(len-nlen+i)!=nl.charAt(i) )
+        return false;
+    return true;
+  }
+
+  // Delete last char.  Useful when doing string-joins and JSON printing and an
+  // extra separater char needs to be removed:
+  //
+  //   sb.p('[');
+  //   for( Foo foo : foos )
+  //     sb.p(foo).p(',');
+  //   sb.unchar().p(']');  // remove extra trailing comma
+  //
+  public SB unchar() { return unchar(1); }
+  public SB unchar(int x) { _sb.setLength(_sb.length()-x); return this; }
+  public SB setLen(int len) { _sb.setLength(len); return this; }
+  public String subString(int start, int end ) { return _sb.substring(start,end); }
+
+  public SB clear() { _sb.setLength(0); return this; }
+  public int len() { return _sb.length(); }
+  @Override public String toString() { return _sb.toString(); }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/Utils.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/Utils.java
new file mode 100644
index 0000000..086f1c7
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/Utils.java
@@ -0,0 +1,53 @@
+package com.compilerprogramming.ezlang.compiler;
+
+import java.util.ArrayList;
+
+public class Utils {
+    public static RuntimeException TODO() { return TODO("Not yet implemented"); }
+    public static RuntimeException TODO(String msg) { return new RuntimeException(msg); }
+
+    /**
+     *  Fast, constant-time, element removal.  Does not preserve order
+     *
+     *  @param array ArrayList to modify
+     *  @param i element to be removed
+     *  @return element removed
+     */
+    public static <E> void del(ArrayList<E> array, int i) {
+        E last = array.removeLast();
+        if (i < array.size()) array.set(i, last);
+    }
+
+    /**
+     * Search a list for an element by reference
+     *
+     * @param ary List to search in
+     * @param x Object to be searched
+     * @return >= 0 on success, -1 on failure
+     */
+    public static <E> int find( ArrayList<E> ary, E x ) {
+        for( int i=0; i<ary.size(); i++ )
+            if( ary.get(i)==x )
+                return i;
+        return -1;
+    }
+    /**
+     * Search a list for an element by reference
+     *
+     * @param ary List to search in
+     * @param x Object to be searched
+     * @return >= 0 on success, -1 on failure
+     */
+    public static <E> int find( E[] ary, E x ) {
+        for( int i=0; i<ary.length; i++ )
+            if( ary[i]==x )
+                return i;
+        return -1;
+    }
+
+    // Rotate a long, nice for hashes
+    public static long rot( long x, int n ) { return (x<<n) | (x>>>n); }
+
+    // Fold a 64bit hash into 32 bits
+    public static int fold( long x ) { return (int)((x>>32) ^ x); }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/Var.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/Var.java
new file mode 100644
index 0000000..6016939
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/Var.java
@@ -0,0 +1,52 @@
+package com.compilerprogramming.ezlang.compiler;
+
+import com.compilerprogramming.ezlang.compiler.sontypes.*;
+
+
+/**
+ *  The tracked fields are now complex enough to deserve a array-of-structs layout
+ */
+public class Var {
+
+    public final String _name;   // Declared name
+    public int _idx;             // index in containing scope
+    private SONType _type;          // Declared type
+    public boolean _final;       // Final field
+    public boolean _fref;        // Forward ref
+
+    public Var(int idx, String name, SONType type, boolean xfinal) {
+        this(idx,name,type,xfinal,false);
+    }
+    public Var(int idx, String name, SONType type, boolean xfinal, boolean fref) {
+        _idx = idx;
+        _name = name;
+        _type = type;
+        _final = xfinal;
+        _fref = fref;
+    }
+    public SONType type() {
+        if( !_type.isFRef() ) return _type;
+        // Update self to no longer use the forward ref type
+        SONType def = Compiler.TYPES.get(((SONTypeMemPtr)_type)._obj._name);
+        return (_type=_type.meet(def));
+    }
+    public SONType lazyGLB() {
+        SONType t = type();
+        return t instanceof SONTypeMemPtr ? t : t.glb();
+    }
+
+    // Forward reference variables (not types) must be BOTTOM and
+    // distinct from inferred variables
+    public boolean isFRef() { return _fref; }
+
+    public void defFRef(SONType type, boolean xfinal) {
+        assert isFRef() && xfinal;
+        _type = type;
+        _final = true;
+        _fref = false;
+    }
+
+    @Override public String toString() {
+        return _type.toString()+(_final ? " ": " !")+_name;
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/BuildLRG.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/BuildLRG.java
new file mode 100644
index 0000000..99c3fbd
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/BuildLRG.java
@@ -0,0 +1,91 @@
+package com.compilerprogramming.ezlang.compiler.codegen;
+
+import com.compilerprogramming.ezlang.compiler.Utils;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeMem;
+
+abstract public class BuildLRG {
+    // Compute live ranges in a single forwards pass.  Every def is a new live
+    // range except live ranges are joined at Phi.  Also, gather the
+    // intersection of register masks in each live range.  Example: "free
+    // always zero" register forces register zero, and calling conventions
+    // typically force registers.
+
+    // Sets FAILED to the set of hard-conflicts (means no need for an IFG,
+    // since it will not color, just split the conflicted ranges now).  Returns
+    // true if no hard-conflicts, although we still might not color.
+    public static boolean run(int round, RegAlloc alloc) {
+        for( Node bb : alloc._code._cfg )
+            for( Node n : bb.outs() ) {
+                if( n instanceof PhiNode phi && !(phi._type instanceof SONTypeMem) ) {
+                    // All Phi inputs end up with the same LRG.
+                    // Pass 1: find any pre-existing LRG, to avoid make-then-Union a LRG
+                    LRG lrg = alloc.lrg(phi);
+                    if( lrg == null )
+                        for( int i=1; i<phi.nIns(); i++ )
+                            if( (lrg = alloc.lrg(phi.in(i))) != null )
+                                break;
+                    // If none, make one.
+                    if( lrg==null ) lrg = alloc.newLRG(n);
+                    if( phi instanceof MachNode ) defLRG(alloc,n);
+                    // Pass 2: everybody uses the same LRG
+                    lrg=alloc.union(lrg,phi);
+                    for( int i=phi instanceof ParmNode ? 2 : 1; i<n.nIns(); i++ )
+                        lrg=alloc.union(lrg,n.in(i));
+                    if( lrg._mask!=null && lrg._mask.isEmpty() )
+                        alloc.fail(lrg);
+
+                } else if( n instanceof MachNode mach ) {
+                    // Attempt to commute ops to keep live ranges compatible.
+                    if( mach.commutes() && n.nOuts()==1 ) {
+                        int uidx = n.out(0)._inputs.find(n);
+                        RegMask mask1 = n.in (1) instanceof MachNode machx ? machx.outregmap() : alloc.lrg(n.in(1))._mask;
+                        RegMask mask2 = n.in (2) instanceof MachNode machx ? machx.outregmap() : alloc.lrg(n.in(2))._mask;
+                        RegMask masko = n.out(0) instanceof MachNode machx ? machx.regmap(uidx): alloc.lrg(n      )._mask;
+                        if( !mask1.overlap(masko) && mask2.overlap(masko) )
+                            n.swap12();
+                    }
+
+                    // Define live range
+                    defLRG(alloc,n);
+
+                    // Now, look in the opposite direction. How are incoming
+                    // LRGs affected by this node: For all uses, make live lrgs
+                    for( int i=1; i<n.nIns(); i++ )
+                        if( n.in(i)!=null ) {
+                            LRG lrg2 = alloc.lrg(n.in(i));
+                            if( lrg2 != null ) { // Anti-dep or other, no LRG
+                                RegMask use_mask = mach.regmap(i);
+                                if( !lrg2.machUse(mach,(short)i,use_mask.size1()).and(use_mask) )
+                                    alloc.fail(lrg2); // Empty register mask, must split
+                            }
+                        }
+
+                }
+
+                // MultiNodes have projections which set registers
+                if( n instanceof MultiNode )
+                    for( Node proj : n.outs() )
+                        if( proj instanceof MachNode )
+                            defLRG(alloc,proj);
+            }
+
+        // Collect live ranges
+        alloc.unify();
+
+        return alloc.success();
+    }
+
+    private static void defLRG( RegAlloc alloc, Node n ) {
+        MachNode mach = (MachNode)n;
+        RegMask def_mask = mach.outregmap();
+        if( def_mask == null ) return;
+        LRG lrg = mach.twoAddress() == 0
+            ? alloc.newLRG(n)                  // Define a new LRG for N
+            : alloc.lrg2(n,mach.twoAddress()); // Use the matching 2-adr input
+        // Record mask and mach
+        if( !lrg.machDef(mach,def_mask.size1()).and(def_mask) )
+            alloc.fail(lrg); // Empty register mask, must split
+    }
+
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/Coalesce.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/Coalesce.java
new file mode 100644
index 0000000..a5b9183
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/Coalesce.java
@@ -0,0 +1,119 @@
+package com.compilerprogramming.ezlang.compiler.codegen;
+
+import com.compilerprogramming.ezlang.compiler.Ary;
+import com.compilerprogramming.ezlang.compiler.Utils;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+import java.util.BitSet;
+import java.util.IdentityHashMap;
+
+// Coalesce copies
+abstract public class Coalesce {
+
+    public static boolean coalesce( int round, RegAlloc alloc ) {
+        // Convert the 2-D array of bits (a 1-D array of BitSets) into an
+        // adjacency matrix.
+        int maxlrg = alloc._LRGS.length;
+        for( int i=1; i<maxlrg; i++ ) {
+            BitSet ifg = IFG.IFG.atX(i);
+            if( ifg != null ) {
+                LRG lrg0 = alloc._LRGS[i];
+                for( int lrg = ifg.nextSetBit(0); lrg>=0; lrg=ifg.nextSetBit(lrg+1) ) {
+                    LRG lrg1 = alloc._LRGS[lrg];
+                    lrg0.addNeighbor(lrg1);
+                    lrg1.addNeighbor(lrg0);
+                }
+            }
+        }
+
+        // Walk all the splits, looking for coalesce chances
+        boolean progress = false;
+        for( CFGNode bb : alloc._code._cfg ) {
+            outer:
+            for( int j=0; j<bb.nOuts(); j++ ) {
+                Node n = bb.out(j);
+                if( !(n instanceof SplitNode) ) continue;
+                LRG v1 = alloc.lrg(n);
+                LRG v2 = alloc.lrg(n.in(1));
+                if( v1 != v2 ) {
+                    LRG ov1 = v1, ov2 = v2;
+                    // Get the smaller neighbor count in v1
+                    if( (v1._adj==null ? 0 : v1._adj._len) > (v2._adj==null ? 0 : v2._adj._len) )
+                        { v1 = v2; v2 = alloc.lrg(n); }
+                    int v2len = v2._adj==null ? 0 : v2._adj._len;
+                    // See that they do not conflict (coalescing would make a self-conflict)
+                    if( v1._adj!=null && v2._adj!=null && v1._mask.overlap(v2._mask) ) {
+                        // Walk the smaller neighbors, and add to the larger.
+                        // Check for direct conflicts along the way.
+                        for( LRG v3 : v1._adj ) {
+                            if( v3==v2 ) {
+                                // Direct conflict, skip this LRG pair
+                                v2._adj.setLen(v2len);
+                                continue outer;
+                            }
+                            // Add missing LRGs from V1 to V2
+                            if( v2._adj.find(v3)== -1 )
+                                v2._adj.push(v3);
+                        }
+                    }
+                    // Most constrained mask
+                    RegMask mask = v1._mask==v2._mask ? v1._mask : v1._mask.copy().and(v2._mask);
+                    // Check for capacity
+                    if( (v2._adj==null ? 0 : v2._adj._len) >= mask.size() ) {
+                        // Fails capacity, will not be trivial colorable
+                        v2._adj.setLen(v2len);
+                        continue;
+                    }
+
+                    // Union lrgs.
+                    progress = true;
+
+                    // Since `n` is dying, remove from the LRGS BEFORE doing
+                    // the union; this will let the union code keep the
+                    // matching bits from the other side, preserving some
+                    // info for biased coloring
+                    if( ov1. _machDef==n ) ov1. _machDef = null;
+                    if( ov1._splitDef==n ) ov1._splitDef = null;
+                    if( ov2. _machUse==n ) ov2. _machUse = null;
+                    if( ov2._splitUse==n ) ov2._splitUse = null;
+                    // Keep larger adjacency list.
+                    Ary<LRG> larger = v2._adj;
+                    v2._adj = null;
+
+                    // Union lrgs.  Swaps again, based on small lrg
+                    LRG vWin = v1.union(v2);
+                    vWin._adj = larger;
+                    LRG vLose = v1.leader() ? v2 : v1;
+
+                    // LRGs below v2len were always in v2, but maybe not in v1.
+                    // LRGs above v2len are in v1 but were not in v2.
+
+                    // Walk v2's neighbors, replacing vLose with vWin.
+                    if( larger != null )
+                        for( LRG vn : larger ) {
+                            int idx = vn._adj.find(vLose);
+                            if( idx != -1 )
+                                vn._adj.del(idx);
+                            if( vn._adj.find(vWin) == -1 )
+                                vn._adj.add(vWin);
+                        }
+                } else {
+
+                    // Since `n` is dying, remove from the LRGS
+                    if( v1. _machDef==n ) v1. _machDef=null;
+                    if( v1. _machUse==n ) v1. _machUse=null;
+                    if( v1._splitDef==n ) v1._splitDef=null;
+                    if( v1._splitUse==n ) v1._splitUse=null;
+                }
+
+                // Remove junk split
+                n.removeSplit();
+                j--;
+            }
+        }
+
+        if( progress )
+            alloc.unify();
+
+        return true;
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/CodeGen.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/CodeGen.java
new file mode 100644
index 0000000..522cfd3
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/CodeGen.java
@@ -0,0 +1,389 @@
+package com.compilerprogramming.ezlang.compiler.codegen;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+import com.compilerprogramming.ezlang.compiler.print.*;
+import com.compilerprogramming.ezlang.compiler.sontypes.*;
+import com.compilerprogramming.ezlang.exceptions.CompilerException;
+
+import java.io.IOException;
+import java.util.BitSet;
+import java.util.HashMap;
+import java.util.IdentityHashMap;
+
+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,                  // Parse ASCII text into Sea-of-Nodes IR
+        Opto,                   // Run ideal optimizations
+        TypeCheck,              // Last check for bad programs
+        InstSelect,             // Convert to target hardware nodes
+        Schedule,               // Global schedule (code motion) nodes
+        LocalSched,             // Local schedule
+        RegAlloc,               // Register allocation
+        Encoding,               // Encoding
+        Export,                 // Export
+    }
+    public Phase _phase;
+
+    // ---------------------------
+    // Compilation source code
+    public final String _src;
+    // Compile-time known initial argument type
+    public final SONTypeInteger _arg;
+
+    // ---------------------------
+    public CodeGen( String src ) { this(src, SONTypeInteger.BOT, 123L ); }
+    public CodeGen( String src, SONTypeInteger 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 Compiler(this,arg);
+    }
+
+
+    // ---------------------------
+    /**
+     *  A counter, for unique node id generation.  Starting with value 1, to
+     *  avoid bugs confusing node ID 0 with uninitialized values.
+     */
+    public 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++; }
+
+
+    // 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;
+
+    // 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<SONTypeTuple,Integer> FIDXS = new HashMap<>();
+    public SONTypeFunPtr makeFun( SONTypeTuple sig, SONType 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 SONTypeFunPtr.make((byte)2,sig,ret, 1L<<fidx );
+    }
+    public SONTypeFunPtr makeFun2( SONTypeTuple sig, SONType 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 new SONTypeFunPtr((byte)2,sig,ret, 1L<<fidx );
+    }
+    // Signature for MAIN
+    public SONTypeFunPtr _main = makeFun(SONTypeTuple.MAIN,SONType.BOTTOM);
+    // Reverse from a constant function pointer to the IR function being called
+    public FunNode link( SONTypeFunPtr tfp ) {
+        assert tfp.isConstant();
+        return _linker.get(tfp.makeFrom(SONType.BOTTOM));
+    }
+
+    // Insert linker mapping from constant function signature to the function
+    // being called.
+    public void link(FunNode fun) {
+        _linker.put(fun.sig().makeFrom(SONType.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<SONTypeFunPtr,FunNode> _linker = new HashMap<>();
+
+    // Parser object
+    public final Compiler P;
+
+    // Parse ASCII text into Sea-of-Nodes IR
+    public int _tParse;
+    public CodeGen parse() {
+        assert _phase == null;
+        _phase = Phase.Parse;
+        long t0 = System.currentTimeMillis();
+        P.parse();
+        _tParse = (int)(System.currentTimeMillis() - t0);
+        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 int _tOpto;
+    public CodeGen opto() {
+        assert _phase == Phase.Parse;
+        _phase = Phase.Opto;
+        long t0 = System.currentTimeMillis();
+
+        // Pessimistic peephole optimization on a worklist
+        _iter.iterate(this);
+        _tOpto = (int)(System.currentTimeMillis() - t0);
+
+        // TODO:
+        // Optimistic
+        // TODO:
+        // loop unroll, peel, RCE, etc
+        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 int _tTypeCheck;
+    public CodeGen typeCheck() {
+        // Demand phase Opto for cleaning up dead control flow at least,
+        // required for the following GCM.
+        assert _phase.ordinal() <= Phase.Opto.ordinal();
+        _phase = Phase.TypeCheck;
+        long t0 = System.currentTimeMillis();
+
+        CompilerException err = _stop.walk( Node::err );
+        _tTypeCheck = (int)(System.currentTimeMillis() - t0);
+        if( err != null )
+            throw err;
+        return this;
+    }
+
+
+    // ---------------------------
+    // Code generation CPU target
+    public Machine _mach;
+    //
+    public String _callingConv;
+
+    // Convert to target hardware nodes
+    public int _tInsSel;
+    public CodeGen instSelect( String base, String cpu, String callingConv ) {
+        assert _phase.ordinal() <= Phase.TypeCheck.ordinal();
+        _phase = Phase.InstSelect;
+
+        _callingConv = callingConv;
+
+        // Not timing the class load...
+        // Look for CPU in fixed named place:
+        //   com.compilerprogramming.ezlang.compiler.node.cpus."cpu"."cpu.class"
+        String clzFile = base+"."+cpu+"."+cpu;
+        try { _mach = ((Class<Machine>) Class.forName( clzFile )).getDeclaredConstructor(new Class[]{CodeGen.class}).newInstance(this); }
+        catch( Exception e ) { throw new RuntimeException(e); }
+
+        // Convert to machine ops
+        long t0 = System.currentTimeMillis();
+        _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();
+        _tInsSel = (int)(System.currentTimeMillis() - t0);
+        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 MachNode already, 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 MachNode ) {
+            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(this);  // 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.
+    public int _tGCM;
+    public Ary<CFGNode> _cfg = new Ary<>(CFGNode.class);
+
+    // Global schedule (code motion) nodes
+    public CodeGen GCM() { return GCM(false); }
+    public CodeGen GCM( boolean show) {
+        assert _phase.ordinal() <= Phase.InstSelect.ordinal();
+        _phase = Phase.Schedule;
+        long t0 = System.currentTimeMillis();
+
+        // Build the loop tree, fix never-exit loops
+        _start.buildLoopTree(_stop);
+        GlobalCodeMotion.buildCFG(this);
+        _tGCM = (int)(System.currentTimeMillis() - t0);
+        if( show )
+            System.out.println(new GraphVisualizer().generateDotOutput(_stop,null,null));
+        return this;
+    }
+
+    // ---------------------------
+    // Local (basic block) scheduler phase, a classic list scheduler
+    public int _tLocal;
+    public CodeGen localSched() {
+        assert _phase == Phase.Schedule;
+        _phase = Phase.LocalSched;
+        long t0 = System.currentTimeMillis();
+        ListScheduler.sched(this);
+        _tLocal = (int)(System.currentTimeMillis() - t0);
+        return this;
+     }
+
+
+    // ---------------------------
+    // Register Allocation
+    public int _tRegAlloc;
+    public RegAlloc _regAlloc;
+    public CodeGen regAlloc() {
+        assert _phase == Phase.LocalSched;
+        _phase = Phase.RegAlloc;
+        long t0 = System.currentTimeMillis();
+        _regAlloc = new RegAlloc(this);
+        _regAlloc.regAlloc();
+        _tRegAlloc = (int)(System.currentTimeMillis() - t0);
+        return this;
+    }
+
+    public String reg(Node n) {
+        if( _phase.ordinal() >= Phase.RegAlloc.ordinal() ) {
+            String s = _regAlloc.reg(n);
+            if( s!=null ) return s;
+        }
+        return "N"+ n._nid;
+    }
+
+    // ---------------------------
+    // Encoding
+    public int _tEncode;
+    public Encoding _encoding;
+    public CodeGen encode() {
+        assert _phase == Phase.RegAlloc;
+        _phase = Phase.Encoding;
+        long t0 = System.currentTimeMillis();
+        _encoding = new Encoding(this);
+        _encoding.encode();
+        _tEncode = (int)(System.currentTimeMillis() - t0);
+        return this;
+    }
+    // Encoded binary, no relocation info
+    public byte[] binary() { return _encoding.bits(); }
+
+    // ---------------------------
+    // Exporting to external formats
+    public CodeGen exportELF(String fname) throws IOException {
+        assert _phase == Phase.Encoding;
+        _phase = Phase.Export;
+        ElfFile obj = new ElfFile(this);
+        obj.export(fname);
+        return this;
+    }
+
+    // ---------------------------
+    SB asm(SB sb) { return ASMPrinter.print(sb,this); }
+    public String asm() { return asm(new SB()).toString(); }
+
+
+    // Testing shortcuts
+    public Node ctrl() { return _stop.ret().ctrl(); }
+    public Node expr() { return _stop.ret().expr(); }
+    public String print() { return _stop.print(); }
+
+    // Debugging helper
+    @Override public String toString() {
+        return _phase.ordinal() > Phase.Schedule.ordinal()
+            ? IRPrinter._prettyPrint( this )
+            : _stop.p(9999);
+    }
+
+    // Debugging helper
+    public Node f(int idx) { return _stop.find(idx); }
+
+    public static void print_as_hex(Encoding enc) {
+        for (byte b : enc._bits.toByteArray()) {
+            System.out.print(String.format("%02X", b));
+        }
+        System.out.println();
+    }
+
+    public void print_as_hex() {
+        for (byte b : _encoding._bits.toByteArray()) {
+            System.out.print(String.format("%02X", b));
+        }
+        System.out.println();
+    }
+
+    //// Debug purposes for now
+//    public CodeGen printENCODING() {
+//        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+//
+//        for(Node bb : CodeGen.CODE._cfg) {
+//            for(Node n: bb.outs()) {
+//                if(n instanceof MachNode) {
+//                    ((MachNode) n).encoding(E);
+//                }
+//            }
+//        }
+//
+//        print_as_hex(outputStream);
+//        // Get the raw bytes from the output stream
+//        return this;
+//    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/ElfExpr.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/ElfExpr.java
new file mode 100644
index 0000000..f24dc4f
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/ElfExpr.java
@@ -0,0 +1,87 @@
+package com.compilerprogramming.ezlang.compiler.codegen;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+public class ElfExpr {
+    public static void main(String[] args) {
+        try (FileOutputStream fos = new FileOutputStream("hello.o")) {
+            ByteBuffer buffer = ByteBuffer.allocate(512);
+            buffer.order(ByteOrder.LITTLE_ENDIAN);
+
+            // === ELF HEADER (64 bytes) ===
+            buffer.put(new byte[]{
+                    0x7F, 'E', 'L', 'F',  // ELF Magic
+                    2, 1, 1, 0,          // 64-bit, little-endian, version
+                    0, 0, 0, 0, 0, 0, 0,  // ABI padding
+                    2, 0,                // Type: Executable (ET_EXEC)
+                    0x3E, 0x00,          // Machine: x86-64
+                    1, 0, 0, 0,          // ELF version
+                    0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Entry point
+                    0, 0, 0, 0, 0, 0, 0, 0,  // Program header offset
+                    64, 0, 0, 0, 0, 0, 0, 0,  // Section header offset
+                    0, 0, 0, 0,            // Flags
+                    64, 0,                 // ELF header size
+                    0, 0,                   // Program header entry size
+                    0, 0,                   // Number of program header entries
+                    40, 0,                  // Section header entry size
+                    2, 0,                   // Number of section headers
+                    1, 0                    // Section header string table index
+            });
+
+            // === .TEXT SECTION (Executable Code) ===
+            byte[] textSection = {
+                    // sys_write (write(1, message, 13))
+                    (byte) 0x48, (byte) 0xC7, (byte) 0xC0, 0x01, 0x00, 0x00, 0x00,  // mov rax, 1
+                    (byte) 0x48, (byte) 0xC7, (byte) 0xC7, 0x01, 0x00, 0x00, 0x00,  // mov rdi, 1
+                    (byte) 0x48, (byte) 0xBE, (byte)0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov rsi, message
+                    (byte) 0x48, (byte) 0xC7, (byte) 0xC2, 0x0D, 0x00, 0x00, 0x00,  // mov rdx, 13
+                    (byte) 0x0F, (byte) 0x05,  // syscall
+
+                    // sys_exit (exit(0))
+                    (byte) 0x48, (byte) 0xC7, (byte) 0xC0, 0x3C, 0x00, 0x00, 0x00,  // mov rax, 60
+                    (byte) 0x48, (byte) 0x31, (byte) 0xFF,  // xor rdi, rdi
+                    (byte) 0x0F, (byte) 0x05   // syscall
+            };
+
+            // Append section data
+            buffer.position(256);
+            buffer.put(textSection);
+
+            // === STRING MESSAGE (.data Section) ===
+            byte[] message = "Hello, World!\n".getBytes();
+            buffer.position(400);
+            buffer.put(message);
+
+            // === SECTION HEADERS ===
+            buffer.position(64);
+
+            // Null Section (required as first entry)
+            buffer.put(new byte[40]);
+
+            // .text Section
+            buffer.putInt(1);    // Offset in section string table (".text")
+            buffer.putInt(1);    // Type: SHT_PROGBITS
+            buffer.putLong(6);   // Flags: Executable | Allocatable
+            buffer.putLong(0);   // Virtual address
+            buffer.putLong(256); // Offset to section data
+            buffer.putLong(textSection.length); // Section size
+            buffer.putInt(0);    // Link
+            buffer.putInt(0);    // Info
+            buffer.putLong(4);   // Address alignment
+            buffer.putLong(0);   // No extra entries
+
+            // === SECTION HEADER STRING TABLE ===
+            buffer.position(500);
+            buffer.put(".text".getBytes());
+            buffer.put((byte) 0x00);  // Null terminator
+
+            fos.write(buffer.array());
+            System.out.println("ELF object file 'hello.o' created.");
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/ElfFile.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/ElfFile.java
new file mode 100644
index 0000000..eee43a1
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/ElfFile.java
@@ -0,0 +1,387 @@
+package com.compilerprogramming.ezlang.compiler.codegen;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+import com.compilerprogramming.ezlang.compiler.sontypes.*;
+
+import java.util.Map;
+import java.util.HashMap;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.io.IOException;
+import java.io.FileOutputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.BufferedOutputStream;
+
+public class ElfFile {
+
+    final CodeGen _code;
+    Ary<Section> _sections = new Ary<>(Section.class);
+
+    ElfFile( CodeGen code ) { _code = code; }
+
+    public static int writeCString(DataSection strtab, String name) {
+        // place name in the string table
+        int pos = strtab._contents.size();
+        byte[] name_bytes = name.getBytes();
+        strtab._contents.write(name_bytes, 0, name_bytes.length);
+        // the strings are null terminated
+        strtab._contents.write(0);
+        return pos;
+    }
+
+    public static void write4( ByteArrayOutputStream bits, int op ) {
+        bits.write(op    );
+        bits.write(op>> 8);
+        bits.write(op>>16);
+        bits.write(op>>24);
+    }
+    public static void write8( ByteArrayOutputStream bits, long i64 ) {
+        write4(bits, (int) i64     );
+        write4(bits, (int)(i64>>32));
+    }
+
+    public static final int SYMBOL_SIZE = 4+1+1+2+8+8;
+
+    public static final int SYM_BIND_LOCAL   = 0;
+    public static final int SYM_BIND_GLOBAL  = 1;
+    public static final int SYM_BIND_WEAK    = 2;
+
+    public static final int SYM_TYPE_NOTYPE  = 0;
+    public static final int SYM_TYPE_OBJECT  = 1;
+    public static final int SYM_TYPE_FUNC    = 2;
+    public static final int SYM_TYPE_SECTION = 3;
+
+    public static class Symbol {
+        String _name;
+        int _name_pos;
+
+        // index in the symbol table
+        int _index;
+
+        // which section is it in
+        int _parent;
+
+        // depends on the symbol type
+        //   TODO
+        int _value, _size;
+
+        // top 4bits are the "bind", bottom 4 are "type"
+        int _info;
+
+        Symbol(String name, int parent, int bind, int type) {
+            _name = name;
+            _parent = parent;
+            _info = (bind << 4) | (type & 0xF);
+        }
+
+        void writeHeader(ByteBuffer out) {
+            out.putInt(_name_pos);         // name
+            out.put((byte) _info);         // info
+            out.put((byte) 0);             // other
+            out.putShort((short) _parent); // shndx
+            out.putLong(_value);           // value
+            out.putLong(_size);            // size
+        }
+    }
+
+    public static final int SHF_WRITE     = 1;
+    public static final int SHF_ALLOC     = 2;
+    public static final int SHF_EXECINSTR = 4;
+    public static final int SHF_STRINGS   = 32;
+    public static final int SHF_INFO_LINK = 64;
+
+    public static final int SECTION_HDR_SIZE = 64;
+    abstract public static class Section {
+        String _name;
+        int _index;
+
+        int _type, _flags;
+        int _link, _info;
+
+        // where's the name in the string table
+        int _name_pos;
+        int _file_offset;
+
+        Section(String name, int type) {
+            _name = name;
+            _type = type;
+        }
+
+        void writeHeader(ByteBuffer out) {
+            out.putInt(_name_pos);     // name
+            out.putInt(_type);         // type
+            out.putLong(_flags);       // flags
+            out.putLong(0);            // addr
+            out.putLong(_file_offset); // offset
+            out.putLong(size());       // size
+            out.putInt(_link);         // link
+            out.putInt(_info);         // info
+            out.putLong(1);            // addralign
+            if (_type == 2) {
+                out.putLong(SYMBOL_SIZE);// entsize
+            } else if (_type == 4) {
+                out.putLong(24);         // entsize
+            } else {
+                out.putLong(0);          // entsize
+            }
+        }
+
+        abstract void write(ByteBuffer out);
+        abstract int size();
+    }
+
+    public static class SymbolSection extends Section {
+        Ary<Symbol> _symbols = new Ary<>(Symbol.class);
+        Ary<Symbol> _loc = new Ary<>(Symbol.class);
+
+        SymbolSection(String name, int type) {
+            super(name, type);
+        }
+
+        @Override
+            void writeHeader(ByteBuffer out) {
+            // points to string table section
+            _link = 1;
+
+            // number of non-local symbols
+            _info = _loc.len() + 1;
+
+            super.writeHeader(out);
+        }
+
+        void push(Symbol s) {
+            if ((s._info >> 4) != SYM_BIND_LOCAL) {
+                _symbols.push(s);
+            } else {
+                _loc.push(s);
+            }
+        }
+
+        @Override
+            void write(ByteBuffer out) {
+            for( int i = 0; i < SYMBOL_SIZE/4; i++ ) {
+                out.putInt(0);
+            }
+            int num=1;
+            for( Symbol s : _loc ) {
+                s._index = num++;
+                s.writeHeader(out);
+            }
+            for( Symbol s : _symbols ) {
+                s._index = num++;
+                s.writeHeader(out);
+            }
+        }
+
+        @Override
+            int size() {
+            return (1 + _symbols.len() + _loc.len()) * SYMBOL_SIZE;
+        }
+    }
+
+    public static class DataSection extends Section {
+        ByteArrayOutputStream _contents;
+
+        DataSection(String name, int type) {
+            super(name, type);
+            _contents = new ByteArrayOutputStream();
+        }
+
+        DataSection(String name, int type, ByteArrayOutputStream contents) {
+            super(name, type);
+            _contents = contents;
+        }
+
+        @Override
+            void write(ByteBuffer out) {
+            out.put(_contents.toByteArray());
+        }
+
+        @Override
+            int size() {
+            return _contents.size();
+        }
+    }
+
+    private void pushSection(Section s) {
+        _sections.push(s);
+        // ELF is effectively "1-based", due to the NULL section
+        s._index = _sections._len;
+    }
+
+    private final HashMap<SONTypeFunPtr,Symbol> _funcs = new HashMap<>();
+    private void encodeFunctions(SymbolSection symbols, DataSection text) {
+        int func_start = 0;
+        for( int i=0; i<_code._cfg._len; i++ ) {
+            Node bb = _code._cfg.at(i);
+            if( bb instanceof FunNode f ) {
+                // skip until the function ends
+                while( !(_code._cfg.at(i) instanceof ReturnNode) ) {
+                    i++;
+                }
+
+                Node r = _code._cfg.at(i);
+                int end = _code._encoding._opStart[r._nid] + _code._encoding._opLen[r._nid];
+
+                Symbol func = new Symbol(f._name, text._index, SYM_BIND_GLOBAL, SYM_TYPE_FUNC);
+                func._size = end - func_start;
+                func._value = func_start;
+                symbols.push(func);
+                _funcs.put(f.sig(), func);
+
+                // next function starts where the last one ends
+                func_start = end;
+            }
+        }
+    }
+
+    public final HashMap<SONType,Symbol> _bigCons = new HashMap<>();
+    private void encodeConstants(SymbolSection symbols, DataSection rdata) {
+        int cnt = 0;
+        for (Map.Entry<Node,SONType> e : _code._encoding._bigCons.entrySet()) {
+            if (_bigCons.get(e.getValue()) != null) {
+                continue;
+            }
+
+            Symbol glob = new Symbol("GLOB$"+cnt, rdata._index, SYM_BIND_GLOBAL, SYM_TYPE_FUNC);
+            glob._value = rdata._contents.size();
+            symbols.push(glob);
+
+            SONType t = e.getValue();
+            if ( t instanceof SONTypeFloat tf ) {
+                write8(rdata._contents, Double.doubleToLongBits(tf._con));
+            } else {
+                throw Utils.TODO();
+            }
+
+            glob._size = rdata._contents.size() - glob._value;
+            _bigCons.put(e.getValue(), glob);
+            cnt++;
+        }
+    }
+
+    public void export(String fname) throws IOException {
+        DataSection strtab = new DataSection(".strtab", 3 /* SHT_SYMTAB */);
+        // first byte is reserved for an empty string
+        strtab._contents.write(0);
+        pushSection(strtab);
+
+        // place all the symbols
+        SymbolSection symbols = new SymbolSection(".symtab", 2 /* SHT_STRTAB */);
+        pushSection(symbols);
+
+        // we've already constructed this entire section in the encoding phase
+        DataSection text = new DataSection(".text", 1 /* SHT_PROGBITS */, _code._encoding._bits);
+        text._flags = SHF_ALLOC | SHF_WRITE | SHF_EXECINSTR;
+        pushSection(text);
+
+        DataSection rdata = new DataSection(".rodata", 1 /* SHT_PROGBITS */);
+        rdata._flags = SHF_ALLOC;
+        pushSection(rdata);
+
+        // populate function symbols
+        encodeFunctions(symbols, text);
+        // populate big constants
+        // encodeConstants(symbols, rdata);
+
+        // create .text relocations
+        DataSection text_rela = new DataSection(".rela.text", 4 /* SHT_RELA */);
+        for( Node n : _code._encoding._externals.keySet()) {
+            int nid    = n._nid;
+            String extern = _code._encoding._externals.get(n);
+            int sym_id = _funcs.get(extern)._index;
+            int offset = _code._encoding._opStart[nid] + _code._encoding._opLen[nid] - 4;
+
+            // u64 offset
+            write8(text_rela._contents, offset);
+            // u64 info
+            write8(text_rela._contents, ((long)sym_id << 32L) | 4L /* PLT32 */);
+            // i64 addend
+            write8(text_rela._contents, -4);
+        }
+        // relocations to constants
+        if (false) for (Map.Entry<Node,SONType> e : _code._encoding._bigCons.entrySet()) {
+            int nid    = e.getKey()._nid;
+            int sym_id = _bigCons.get(e.getValue())._index;
+            int offset = _code._encoding._opStart[nid] + _code._encoding._opLen[nid] - 4;
+
+            // u64 offset
+            write8(text_rela._contents, offset);
+            // u64 info
+            write8(text_rela._contents, ((long)sym_id << 32L) | 1L /* PC32 */);
+            // i64 addend
+            write8(text_rela._contents, -4);
+        }
+        text_rela._flags = SHF_INFO_LINK;
+        text_rela._link = 2;
+        text_rela._info = text._index;
+        pushSection(text_rela);
+
+        // populate string table
+        for( Section s : _sections ) { s._name_pos = writeCString(strtab, s._name); }
+        for( Symbol s : symbols._symbols ) { s._name_pos = writeCString(strtab, s._name); }
+        for( Symbol s : symbols._loc ) { s._name_pos = writeCString(strtab, s._name); }
+
+        int idx = 1;
+        for( Section s : _sections ) {
+            Symbol sym = new Symbol(s._name, idx++, SYM_BIND_LOCAL, SYM_TYPE_SECTION);
+            // we can reuse the same name pos from the actual section
+            sym._name_pos = s._name_pos;
+            sym._size = s.size();
+            symbols.push(sym);
+        }
+
+        int size = 64; // ELF header
+        // size of all section data
+        for( Section s : _sections ) {
+            s._file_offset = size;
+            size += s.size();
+        }
+        // headers go at the very end
+        int shdr_offset = size;
+        // there's a "null" section at the start of the section headers
+        size += SECTION_HDR_SIZE*(1 + _sections.len());
+
+        // System.out.printf("Hello %d, %d, %s\n", size, shdr_offset);
+        ByteBuffer out = ByteBuffer.allocate(size);
+        out.order(ByteOrder.LITTLE_ENDIAN);
+
+        // ELF header
+        out.put(new byte[]{ 0x7F, 'E', 'L', 'F', 2, 1, 1, 0 });
+        out.put(new byte[8]);
+        out.putShort((short)1 /* ET_REL */); // type
+        out.putShort((short)62 /* AMD64 */); // machine
+        out.putInt(1);                // version
+        out.putLong(0);               // entry
+        out.putLong(0);               // phoff
+        out.putLong(shdr_offset);     // shoff
+        out.putInt(0);                // flags
+        out.putShort((short)64);      // ehsize
+        out.putShort((short)0);       // phentsize
+        out.putShort((short)0);       // phnum
+        out.putShort((short)64);      // shentsize
+        out.putShort((short) (1 + _sections.len())); // shnum
+        out.putShort((short)1);       // shstrndx
+
+        // raw section data
+        for( Section s : _sections ) {
+            s.write(out);
+        }
+
+        // "null" section
+        for( int i = 0; i < 16; i++ ) {
+            out.putInt(0);
+        }
+
+        for( Section s : _sections ) {
+            s.writeHeader(out);
+        }
+        assert out.position() == size;
+
+        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(fname));
+        bos.write(out.array());
+        bos.close();
+    }
+
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/Encoding.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/Encoding.java
new file mode 100644
index 0000000..a30447d
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/Encoding.java
@@ -0,0 +1,418 @@
+package com.compilerprogramming.ezlang.compiler.codegen;
+
+import com.compilerprogramming.ezlang.compiler.Ary;
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.Utils;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+import com.compilerprogramming.ezlang.compiler.sontypes.*;
+import java.io.ByteArrayOutputStream;
+import java.util.*;
+
+
+/**
+ *  Instruction encodings
+ * <p>
+ *  This class holds the encoding bits, plus the relocation information needed
+ *  to move the code to a non-zero offset, or write to an external file (ELF).
+ * <p>
+ *  There are also a bunch of generic utilities for managing bits and bytes
+ *  common in all encodings.
+ */
+public class Encoding {
+
+    // Top-level program graph structure
+    final CodeGen _code;
+
+    // Instruction bytes.  The registers are encoded already.  Relocatable data
+    // is in a fixed format depending on the kind of relocation.
+
+    // - RIP-relative offsets into the same chunk are just encoded with their
+    //   check relative offsets; no relocation info is required.
+
+    // - Fixed address targets in the same encoding will be correct for a zero
+    //   base; moving the entire encoding requires adding the new base address.
+
+    // - RIP-relative to external chunks have a zero offset; the matching
+    //   relocation info will be used to patch the correct value.
+
+    public static class BAOS extends ByteArrayOutputStream {
+        public byte[] buf() { return buf; }
+        void set( byte[] buf0, int count0 ) { buf=buf0; count=count0; }
+    };
+    final public BAOS _bits = new BAOS();
+
+    public int [] _opStart;     // Start  of opcodes, by _nid
+    public byte[] _opLen;       // Length of opcodes, by _nid
+
+    Encoding( CodeGen code ) { _code = code; }
+
+    // Shortcut to the defining register
+    public short reg(Node n) {
+        return _code._regAlloc.regnum(n);
+    }
+
+    public Encoding add1( int op ) { _bits.write(op); return this; }
+
+    public void add2( int op ) {
+        _bits.write(op    );
+        _bits.write(op>> 8);
+    }
+    // Little endian write of a 32b opcode
+    public void add4( int op ) {
+        _bits.write(op    );
+        _bits.write(op>> 8);
+        _bits.write(op>>16);
+        _bits.write(op>>24);
+    }
+    public void add8( long i64 ) {
+        add4((int) i64     );
+        add4((int)(i64>>32));
+    }
+    public int read4(int idx) {
+        byte[] buf = _bits.buf();
+        return buf[idx] | (buf[idx+1]&0xFF) <<8 | (buf[idx+2]&0xFF)<<16 | (buf[idx+3]&0xFF)<<24;
+    }
+    public long read8(int idx) {
+        return (read4(idx) & 0xFFFFFFFFL) | read4(idx+4);
+    }
+
+    // This buffer is invalid/moving until after all encodings are written
+    public byte[] bits() { return _bits.buf(); }
+
+    // 4 byte little-endian write
+    public void patch4( int idx, int val ) {
+        byte[] buf = _bits.buf();
+        buf[idx  ] = (byte)(val    );
+        buf[idx+1] = (byte)(val>> 8);
+        buf[idx+2] = (byte)(val>>16);
+        buf[idx+3] = (byte)(val>>24);
+    }
+
+    void pad8() {
+        while( (_bits.size()+7 & -8) > _bits.size() )
+            _bits.write(0);
+    }
+
+
+    // Nodes need "relocation" patching; things done after code is placed.
+    // Record src and dst Nodes.
+    private final HashMap<Node,CFGNode> _internals = new HashMap<>();
+    // Source is a Call, destination in the Fun.
+    public Encoding relo( CallNode call ) {
+        _internals.put(call,_code.link(call.tfp()));
+        return this;
+    }
+    public Encoding relo( ConstantNode con ) {
+        SONTypeFunPtr tfp = (SONTypeFunPtr)con._con;
+        _internals.put(con,_code.link(tfp));
+        return this;
+    }
+    public void jump( CFGNode jmp, CFGNode dst ) {
+        while( dst.nOuts() == 1 ) // Skip empty blocks
+            dst = dst.uctrl();
+        _internals.put(jmp,dst);
+    }
+
+
+    final HashMap<Node,String> _externals = new HashMap<>();
+    public Encoding external( Node call, String extern ) {
+        _externals.put(call,extern);
+        return this;
+    }
+
+    // Store t as a 32/64 bit constant in the code space; generate RIP-relative
+    // addressing to load it
+
+    public final HashMap<Node,SONType> _bigCons = new HashMap<>();
+    public final HashMap<Node,Integer> _cpool = new HashMap<>();
+    public void largeConstant( Node relo, SONType t ) {
+        assert t.isConstant();
+        _bigCons.put(relo,t);
+    }
+
+    void encode() {
+        // Basic block layout: invert branches to keep blocks in-order; insert
+        // unconditional jumps.  Attempt to keep backwards branches taken,
+        // forwards not-taken (this is the default prediction on most
+        // hardware).  Layout is still RPO but with more restrictions.
+        basicBlockLayout();
+
+        // Write encoding bits in order into a big byte array.
+        // Record opcode start and length.
+        writeEncodings();
+
+        // Write any large constants into a constant pool; they
+        // are accessed by RIP-relative addressing.
+        writeConstantPool();
+
+        // Short-form RIP-relative support: replace long encodings with short
+        // encodings and compact the code, changing all the offsets.
+        compactShortForm();
+
+        // Patch RIP-relative and local encodings now.
+        patchLocalRelocations();
+    }
+
+    // Basic block layout: invert branches to keep blocks in-order; insert
+    // unconditional jumps.  Attempt to keep backwards branches taken,
+    // forwards not-taken (this is the default prediction on most
+    // hardware).  Layout is still RPO but with more restrictions.
+    private void basicBlockLayout() {
+        Ary<CFGNode> rpo = new Ary<>(CFGNode.class);
+        BitSet visit = _code.visit();
+        for( Node n : _code._start._outputs )
+            if( n instanceof FunNode fun )
+                _rpo_cfg(fun, visit, rpo);
+        rpo.add(_code._start);
+
+        // Reverse in-place
+        for( int i=0; i< rpo.size()>>1; i++ )
+            rpo.swap(i,rpo.size()-1-i);
+        visit.clear();
+        _code._cfg = rpo;       // Save the new ordering
+    }
+
+    // Basic block layout.  Now that RegAlloc is finished, no more spill code
+    // will appear.  We can change our BB layout from RPO to something that
+    // minimizes actual branches, takes advantage of fall-through edges, and
+    // tries to help simple branch predictions: back branches are predicted
+    // taken, forward not-taken.
+    private void _rpo_cfg(CFGNode bb, BitSet visit, Ary<CFGNode> rpo) {
+        if( visit.get(bb._nid) ) return;
+        visit.set(bb._nid);
+        if( bb.nOuts()==0 ) return; // StopNode
+        if( !(bb instanceof IfNode iff) ) {
+            CFGNode next = bb instanceof ReturnNode ? (CFGNode)bb.out(bb.nOuts()-1) : bb.uctrl();
+            // If the *next* BB has already been visited, we may need an
+            // unconditional jump here
+            if( visit.get(next._nid) && !(next instanceof StopNode) ) {
+                // If all the blocks, in RPO order, to our target
+                // are empty, we will fall in and not need a jump.
+                if( next instanceof LoopNode || !isEmptyBackwardsScan(rpo,next) ) {
+                    CFGNode jmp = _code._mach.jump();
+                    jmp.setDefX(0,bb);
+                    next.setDef(next._inputs.find(bb),jmp);
+                    rpo.add(jmp);
+                }
+            }
+            _rpo_cfg(next,visit,rpo);
+        } else {
+            boolean invert = false;
+            // Pick out the T/F projections
+            CProjNode t = iff.cproj(0);
+            CProjNode f = iff.cproj(1);
+            int tld = t.loopDepth(), fld = f.loopDepth(), bld = bb.loopDepth();
+            // Decide entering or exiting a loop
+            if( tld==bld ) {
+                // if T is empty, keep
+                if( t.nOuts()>1 &&
+                    // Else swap so T is forward and exits, while F falls into next loop block
+                    ((fld<bld && t.out(0)!=t.loop()) ||
+                     // Jump to an empty block and fall into a busy block
+                     (fld==bld && f.nOuts()==1)) ) {
+                    invert = true;
+                } // Else nothing
+            } else if( tld > bld ) { // True enters a deeper loop
+                throw Utils.TODO();
+            } else if( fld == bld && f.out(0) instanceof LoopNode ) { // Else True exits a loop
+                // if false is the loop backedge, make sure its true/taken
+                invert = true;
+            } // always forward and good
+            // Invert test and Proj fields
+            if( invert ) {
+                iff.invert();
+                t.invert();
+                f.invert();
+                CProjNode tmp=f; f=t; t=tmp; // Swap t/f
+            }
+
+            // Always visit the False side last (so True side first), so that
+            // when the False RPO visit returns, the IF is immediately next.
+            // When the RPO is reversed, the fall-through path will always be
+            // following the IF.
+            _rpo_cfg(t,visit,rpo);
+            _rpo_cfg(f,visit,rpo);
+        }
+        rpo.add(bb);
+    }
+
+    private static boolean isEmptyBackwardsScan(Ary<CFGNode> rpo, CFGNode next) {
+        for( int i=rpo._len-1; rpo.at(i)!=next; i-- )
+            if( rpo.at(i).nOuts()!=1 )
+                return false;
+        return true;
+    }
+
+    // Write encoding bits in order into a big byte array.
+    // Record opcode start and length.
+    public FunNode _fun;        // Currently encoding function
+    private void writeEncodings() {
+        _opStart= new int [_code.UID()];
+        _opLen  = new byte[_code.UID()];
+        for( CFGNode bb : _code._cfg ) {
+            if( !(bb instanceof MachNode mach0) )
+                _opStart[bb._nid] = _bits.size();
+            else if( bb instanceof FunNode fun ) {
+                _fun = fun;     // Currently encoding function
+                _opStart[bb._nid] = _bits.size();
+                mach0.encoding( this );
+                _opLen[bb._nid] = (byte) (_bits.size() - _opStart[bb._nid]);
+            }
+            for( Node n : bb._outputs ) {
+                if( n instanceof MachNode mach && !(n instanceof FunNode) ) {
+                    _opStart[n._nid] = _bits.size();
+                    mach.encoding( this );
+                    _opLen[n._nid] = (byte) (_bits.size() - _opStart[n._nid]);
+                }
+            }
+        }
+        pad8();
+    }
+
+    // Write the constant pool
+    private void writeConstantPool() {
+        // TODO: Check for cpool dups
+        HashSet<SONType> ts = new HashSet<>();
+        for( SONType t : _bigCons.values() ) {
+            if( ts.contains(t) )
+                throw Utils.TODO(); // Dup!  Compress!
+            ts.add(t);
+        }
+
+        // Write the 8-byte constants
+        for( Node relo : _bigCons.keySet() ) {
+            SONType t = _bigCons.get(relo);
+            if( t.log_size()==3 ) {
+                // Map from relo to constant start
+                _cpool.put(relo,_bits.size());
+                long x = t instanceof SONTypeInteger ti
+                    ? ti.value()
+                    : Double.doubleToRawLongBits(((SONTypeFloat)t).value());
+                add8(x);
+            }
+        }
+
+        // Write the 4-byte constants
+        for( Node relo : _bigCons.keySet() ) {
+            SONType t = _bigCons.get(relo);
+            if( t.log_size()==2 ) {
+                // Map from relo to constant start
+                _cpool.put(relo,_bits.size());
+                int x = t instanceof SONTypeInteger ti
+                    ? (int)ti.value()
+                    : Float.floatToRawIntBits((float)((SONTypeFloat)t).value());
+                add4(x);
+            }
+        }
+    }
+
+    // Short-form RIP-relative support: replace short encodings with long
+    // encodings and expand the code, changing all the offsets.
+    private void compactShortForm() {
+        int len = _code._cfg._len;
+        int[] oldStarts = new int[len];
+        for( int i=0; i<len; i++ )
+            oldStarts[i] = _opStart[_code._cfg.at(i)._nid];
+
+        // TODO: Rewrite this algo to use the small "_jmps" list of just the
+        // jumps instead of walking all blocks.
+        // TODO2: Allow short encodings on non-jumps.  This algo doesn't
+        // really care jumps vs other rip-relative things.
+
+        // Check the size of short jumps, adjust the block starts for a whole
+        // pass over all blocks.  If some jumps go long, they might stretch the
+        // distance for other jumps, so another pass is needed.
+        int slide = -1;
+        while( slide != 0) {    // While no fails
+            slide = 0;
+            for( int i=0; i<len; i++ ) {
+                CFGNode bb = _code._cfg.at(i);
+                _opStart[bb._nid] += slide;
+                if( bb instanceof RIPRelSize riprel ) {
+                    CFGNode target = (CFGNode)bb.out(0);
+                    // Delta is from opStart to opStart.  X86 at least counts
+                    // the delta from the opEnd, but we don't have the end until
+                    // we decide the size - so the encSize has to deal
+                    assert _opStart[target._nid] > 0;
+                    int delta = _opStart[target._nid] - _opStart[bb._nid];
+                    byte opLen = riprel.encSize(delta);
+                    // Recorded size is smaller than the current size?
+                    if( _opLen[bb._nid] < opLen ) {
+                        // Start sliding the code down; record slide amount and new size
+                        slide += opLen - _opLen[bb._nid];
+                        _opLen[bb._nid] = opLen;
+                    }
+                }
+            }
+
+            // CPool padding is non-linear; in rare cases padding can force a
+            // larger size...  which will shrink the padding and allow the
+            // short form to work.  Too bad.
+            if( !_cpool.isEmpty() )
+                pad8();
+        }
+
+
+        // Copy/slide the bits to make space for all the longer branches
+        int grow = _opStart[_code._cfg.at(len-1)._nid] - oldStarts[len-1];
+        if( grow > 0 ) {        // If no short-form ops, nothing to do here
+            int end = _bits.size();
+            byte[] bits = new byte[end+grow];
+            for( int i=len-1; i>=0; i-- ) {
+                int start = oldStarts[i];
+                if( start==0 && i>1 ) continue;
+                int oldStart = oldStarts[i];
+                int newStart = _opStart[_code._cfg.at(i)._nid];
+                System.arraycopy(_bits.buf(),oldStart,bits,newStart,end-start);
+                end = start;
+            }
+            _bits.set(bits,bits.length);
+        }
+    }
+
+    // Patch local encodings now
+    private void patchLocalRelocations() {
+        // Walk the local code-address relocations
+        for( Node src : _internals.keySet() ) {
+            Node dst = _internals.get(src);
+            int target = _opStart[dst._nid];
+            int start  = _opStart[src._nid];
+            ((RIPRelSize)src).patch(this, start, _opLen[src._nid], target - start);
+        }
+
+        for( Node src : _cpool.keySet() ) {
+            int target = _cpool.get(src);
+            int start = _opStart[src._nid];
+            ((RIPRelSize)src).patch(this, start, _opLen[src._nid], target - start);
+        }
+    }
+
+
+    // Actual stack layout is up to each CPU.
+    // X86, with too many args & spills:
+    // | CALLER |
+    // |  argN  | // slot 1, required by callER
+    // +--------+
+    // |  RPC   | // slot 0, required by callER
+    // | callee | // slot 3, callEE
+    // | callee | // slot 2, callEE
+    // |  PAD16 |
+    // +--------+
+
+    // RISC/ARM, with too many args & spills:
+    // | CALLER |
+    // |  argN  | // slot 0, required by callER
+    // +--------+
+    // | callee | // slot 3, callEE: might be RPC
+    // | callee | // slot 2, callEE
+    // | callee | // slot 1, callEE
+    // |  PAD16 |
+    // +--------+
+    private void frameSize() {
+        for( Node n : _code._start.outs() ) {
+            if( n instanceof FunNode fun ) {
+                throw Utils.TODO();
+            }
+        }
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/GlobalCodeMotion.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/GlobalCodeMotion.java
new file mode 100644
index 0000000..f0af74b
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/GlobalCodeMotion.java
@@ -0,0 +1,281 @@
+package com.compilerprogramming.ezlang.compiler.codegen;
+
+import com.compilerprogramming.ezlang.compiler.Ary;
+import com.compilerprogramming.ezlang.compiler.IterPeeps.WorkList;
+import com.compilerprogramming.ezlang.compiler.Utils;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+import com.compilerprogramming.ezlang.compiler.sontypes.*;
+import java.util.*;
+
+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( 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++ )
+            rpo.swap(i,rpo.size()-1-i);
+        // Set global CFG
+        code._cfg = rpo;
+
+        schedEarly(code);
+
+        // Break up shared global constants by functions
+        breakUpGlobalConstants(code);
+
+        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 CallNode call ) call.unlink_all();
+        if( !(use instanceof CFGNode cfg) || visit.get(cfg._nid) )
+            return;             // Been there, done that
+        if( def instanceof ReturnNode && use instanceof CallEndNode )
+            return;
+        assert !( def instanceof CallNode && use instanceof FunNode );
+        visit.set(cfg._nid);
+        for( Node useuse : cfg._outputs )
+            _rpo_cfg(cfg,useuse,visit,rpo);
+        rpo.add(cfg);
+    }
+
+    // Break up shared global constants by functions
+    private static void breakUpGlobalConstants( CodeGen code ) {
+        // For all global constants
+        for( int i=0; i< code._start.nOuts(); i++ ) {
+            Node con = code._start.out(i);
+            if( con instanceof MachNode mach && mach.isClone() ) {
+                // While constant has users in different functions
+                while( true ) {
+                    // Find a function user, and another function
+                    FunNode fun = null;
+                    boolean done=true;
+                    for( Node use : con.outs() ) {
+                        FunNode fun2 = use.cfg0().fun();
+                        if( fun==null || fun==fun2 ) fun=fun2;
+                        else { done=false; break; }
+                    }
+                    // Single function user, so this constant is not shared
+                    if( done ) { i--; con.setDef(0,fun); break; }
+                    // Move function users to a private constant
+                    Node con2 = mach.copy();  // Private constant clone
+                    con2._inputs.set(0,null); // Preserve edge invariants from clone
+                    con2.setDef(0,fun);
+                    // Move function users to this private constant
+                    for( int j=0; j<con._outputs._len; j++ ) {
+                        Node use = con.out(j);
+                        FunNode fun2 = use.cfg0().fun();
+                        if( fun2==fun ) {
+                            use.setDef(use._inputs.find(con),con2);
+                            j--;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    // ------------------------------------------------------------------------
+    // Visit all nodes in CFG Reverse Post-Order, essentially defs before uses
+    // (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(CodeGen code) {
+        // Reverse Post-Order on CFG
+        for( CFGNode cfg : code._cfg ) {
+            cfg.loopDepth();
+            for( Node n : cfg._inputs )
+                _schedEarly(n,code );
+            if( cfg instanceof RegionNode )
+                for( Node phi : cfg._outputs )
+                    if( phi instanceof PhiNode )
+                        _schedEarly(phi,code );
+        }
+    }
+
+    private static void _schedEarly(Node n, CodeGen code) {
+        if( n==null || code._visit.get(n._nid) ) return; // Been there, done that
+        assert !(n instanceof CFGNode);
+        code._visit.set(n._nid);
+        // Schedule not-pinned not-CFG inputs before self.  Since skipping
+        // Pinned, this never walks the backedge of Phis (and thus spins around
+        // a data-only loop), eventually attempting relying on some pre-visited-
+        // not-post-visited data op with no scheduled control.
+        for( Node def : n._inputs )
+            if( def!=null && !(def instanceof PhiNode) )
+                _schedEarly(def,code);
+
+        // If not-pinned (e.g. constants, projections, phi) and not-CFG
+        if( !n.isPinned() ) {
+            // Schedule at deepest input
+            CFGNode early = code._start; // Maximally early, lowest idepth
+            if( n.in(0) instanceof CFGNode cfg ) early = cfg;
+            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);
+        }
+    }
+
+    // ------------------------------------------------------------------------
+    private static void schedLate( CodeGen code ) {
+        CFGNode[] late = new CFGNode[code.UID()];
+        Node[] ns = new Node[code.UID()];
+        // Breadth-first scheduling
+        breadth(code._stop,ns,late);
+
+        // Copy the best placement choice into the control slot
+        for( int i=0; i<late.length; i++ )
+            if( ns[i] != null && !(ns[i] instanceof ProjNode) )
+                ns[i].setDef(0,late[i]);
+    }
+
+    private static void breadth(Node stop, Node[] ns, CFGNode[] late) {
+        // Things on the worklist have some (but perhaps not all) uses done.
+        WorkList<Node> work = new WorkList<>();
+        work.push(stop);
+        Node n;
+        outer:
+        while( (n = work.pop()) != null ) {
+            assert late[n._nid]==null; // No double visit
+            // These I know the late schedule of, and need to set early for loops
+            if( n instanceof CFGNode cfg ) late[n._nid] = cfg.blockHead() ? cfg : cfg.cfg(0);
+            else if( n instanceof PhiNode phi ) late[n._nid] = phi.region();
+            else if( n instanceof ProjNode && n.in(0) instanceof CFGNode cfg ) late[n._nid] = cfg;
+            else {
+
+                // All uses done?
+                for( Node use : n._outputs )
+                    if( use!=null && late[use._nid]==null )
+                        continue outer; // Nope, await all uses done
+
+                // Loads need their memory inputs' uses also done
+                if( n instanceof LoadNode ld )
+                    for( Node memuse : ld.mem()._outputs )
+                        if( late[memuse._nid]==null &&
+                            // New makes new memory, never crushes load memory
+                            !(memuse instanceof NewNode) &&
+                            // Load-use directly defines memory
+                            (memuse._type instanceof SONTypeMem ||
+                             // Load-use indirectly defines memory
+                             (memuse._type instanceof SONTypeTuple tt && tt._types[ld._alias] instanceof SONTypeMem)) )
+                            continue outer;
+
+                // All uses done, schedule
+                _doSchedLate(n,ns,late);
+            }
+
+            // Walk all inputs and put on worklist, as their last-use might now be done
+            for( Node def : n._inputs )
+                if( def!=null && late[def._nid]==null ) {
+                    work.push(def);
+                    // if the def has a load use, maybe the load can fire
+                    for( Node ld : def._outputs )
+                        if( ld instanceof LoadNode && late[ld._nid]==null )
+                            work.push(ld);
+                }
+        }
+    }
+
+    private static void _doSchedLate(Node n, Node[] ns, CFGNode[] late) {
+        // Walk uses, gathering the LCA (Least Common Ancestor) of uses
+        CFGNode early = n.in(0) instanceof CFGNode cfg ? cfg : n.in(0).cfg0();
+        assert early != null;
+        CFGNode lca = null;
+        for( Node use : n._outputs )
+            if( use != null )
+              lca = use_block(n,use, late)._idom(lca,null);
+
+        // Loads may need anti-dependencies, raising their LCA
+        if( n instanceof LoadNode load )
+            lca = find_anti_dep(lca,load,early,late);
+
+        // Walk up from the LCA to the early, looking for best place.  This is
+        // the lowest execution frequency, approximated by least loop depth and
+        // deepest control flow.
+        CFGNode best = lca;
+        lca = lca.idom();       // Already found best for starting LCA
+        for( ; lca != early.idom(); lca = lca.idom() )
+            if( better(lca,best) )
+                best = lca;
+        assert !(best instanceof IfNode);
+        ns  [n._nid] = n;
+        late[n._nid] = best;
+    }
+
+    // Block of use.  Normally from late[] schedule, except for Phis, which go
+    // to the matching Region input.
+    private static CFGNode use_block(Node n, Node use, CFGNode[] late) {
+        if( !(use instanceof PhiNode phi) )
+            return late[use._nid];
+        CFGNode found=null;
+        for( int i=1; i<phi.nIns(); i++ )
+            if( phi.in(i)==n )
+                found = phi.region().cfg(i)._idom(found,null);
+
+        assert found!=null;
+        return found;
+    }
+
+
+    // Least loop depth first, then largest idepth
+    private static boolean better( CFGNode lca, CFGNode best ) {
+        return lca.loopDepth() < best.loopDepth() ||
+                (lca.idepth() > best.idepth() || best instanceof IfNode);
+    }
+
+    private static CFGNode find_anti_dep(CFGNode lca, LoadNode load, CFGNode early, CFGNode[] late) {
+        // 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() )
+            cfg._anti = load._nid;
+        // Walk load->mem uses, looking for Stores causing an anti-dep
+        for( Node mem : load.mem()._outputs ) {
+            switch( mem ) {
+            case StoreNode st:
+                assert late[st._nid]!=null;
+                lca = anti_dep(load,late[st._nid],st.cfg0(),lca,st);
+                break;
+            case CallNode st:
+                assert late[st._nid]!=null;
+                lca = anti_dep(load,late[st._nid],st.cfg0(),lca,st);
+                break;
+            case PhiNode phi:
+                // Repeat anti-dep for matching Phi inputs.
+                // No anti-dep edges but may raise the LCA.
+                for( int i=1; i<phi.nIns(); i++ )
+                    if( phi.in(i)==load.mem() )
+                        lca = anti_dep(load,phi.region().cfg(i),load.mem().cfg0(),lca,null);
+                break;
+            case NewNode st: break;
+            case LoadNode ld: break; // Loads do not cause anti-deps on other loads
+            case ReturnNode ret: break; // Load must already be ahead of Return
+            case MemMergeNode ret: break; // Mem uses now on ScopeMin
+            case NeverNode never: break;
+            default: throw Utils.TODO();
+            }
+        }
+        return lca;
+    }
+
+    //
+    private static CFGNode anti_dep( LoadNode load, CFGNode stblk, CFGNode defblk, CFGNode lca, Node st ) {
+        // Walk store blocks "reach" from its scheduled location to its earliest
+        for( ; stblk != defblk.idom(); stblk = stblk.idom() ) {
+            // Store and Load overlap, need anti-dependence
+            if( stblk._anti==load._nid ) {
+                lca = stblk._idom(lca,null); // Raise Loads LCA
+                if( lca == stblk && st != null && st._inputs.find(load) == -1 ) // And if something moved,
+                    st.addDef(load);   // Add anti-dep as well
+                return lca;            // Cap this stores' anti-dep to here
+            }
+        }
+        return lca;
+    }
+
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/IFG.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/IFG.java
new file mode 100644
index 0000000..38741ad
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/IFG.java
@@ -0,0 +1,526 @@
+package com.compilerprogramming.ezlang.compiler.codegen;
+
+import com.compilerprogramming.ezlang.compiler.Ary;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+import java.util.BitSet;
+import java.util.IdentityHashMap;
+
+// Interference Graph
+abstract public class IFG {
+    // Interference Graph Support
+
+    // Map from a Basic Block to Live-Out: {a map from a Live Range to a Def}
+    private static final IdentityHashMap<CFGNode,IdentityHashMap<LRG,Node>> BBOUTS = new IdentityHashMap<>();
+    static void resetBBLiveOut() {
+        for( IdentityHashMap<LRG,Node> bbout : BBOUTS.values() )
+            bbout.clear();
+    }
+    static final IdentityHashMap<LRG,Node> TMP = new IdentityHashMap<>();
+
+    // Inteference Graph: Array of Bitsets
+    static final Ary<BitSet> IFG = new Ary<>(BitSet.class);
+    static void resetIFG() {
+        for( BitSet bs : IFG )
+            if( bs!=null ) bs.clear();
+    }
+
+    // Set matching bit
+    private static void addIFG( LRG lrg0, LRG lrg1 ) {
+        short x0 = lrg0._lrg, x1 = lrg1._lrg;
+        // Triangulate
+        if( x0 < x1 ) _addIFG(x0,x1);
+        else          _addIFG(x1,x0);
+    }
+    // Add lrg1 to lrg0's conflict set
+    private static void _addIFG( short x0, short x1 ) {
+        BitSet ifg = IFG.atX(x0);
+        if( ifg==null )
+            IFG.setX(x0,ifg = new BitSet());
+        ifg.set(x1);
+    }
+
+
+    private static final Ary<CFGNode> WORK = new Ary<>(CFGNode.class);
+    static void pushWork(CFGNode bb) {
+        if( WORK.find(bb) == -1 )
+            WORK.push(bb);
+    }
+
+    // ------------------------------------------------------------------------
+    // Visit all blocks, using the live-out LRGs per-block and doing a backwards
+    // walk over each block.  At the end of the walk, push the live-out sets to
+    // prior blocks.  Due to loops, no single visitation order suffices.  This
+    // problem can be fairly efficiently solved with a proper LIVE calculation
+    // and bitsets.  In the interests of simplicity (and assumption of smaller
+    // programs) I am skipping LIVE and directly computing it during the IFG
+    // building.
+
+    // Start by setting the live-out of the exit block (to empty), and putting
+    // it on a worklist.
+
+    // - Pull from worklist a block who's live-out has changed
+    // - Walk backwards, adding interferences and computing live.
+    // - At block head, "push" the new live-out to prior blocks.
+    // - - If they pick up new live-outs, put them on the worklist.
+
+    public static boolean build( int round, RegAlloc alloc ) {
+        // Reset all to empty
+        resetBBLiveOut();
+        resetIFG();
+
+        // Last block has nothing live out
+        assert WORK.isEmpty();
+        for( CFGNode bb : alloc._code._cfg )
+            if( bb.blockHead())
+                WORK.push( bb );
+
+        // Process blocks until no more changes
+        while( !WORK.isEmpty() )
+            do_block(round,alloc,WORK.pop());
+
+        return alloc.success();
+    }
+
+    // Walk one block backwards, compute live-in from live-out, and build IFG
+    static void do_block( int round, RegAlloc alloc, CFGNode bb ) {
+        assert bb.blockHead();
+        IdentityHashMap<LRG,Node> live_out = BBOUTS.get(bb);
+        TMP.clear();
+        if( live_out != null )
+            TMP.putAll(live_out);   // Copy bits to temp
+
+        // A backwards walk over instructions in the basic block
+        for( int inum = bb.nOuts()-1; inum >= 0; inum-- ) {
+            Node n = bb.out(inum);
+            if( n.in(0) != bb ) continue;
+            // In a backwards walk, proj users come before the node itself
+            if( n instanceof MultiNode )
+                for( Node proj : n.outs() )
+                    if( proj instanceof ProjNode )
+                        do_node(alloc,proj);
+            if( n instanceof MachNode )
+                do_node(alloc,n);
+        }
+
+        // The bb head kills register, e.g. a CallEnd and caller-save registers
+        if( bb instanceof MachNode mach )
+            kills(alloc,mach);
+
+        // Push live-sets backwards to priors in CFG.
+        if( bb instanceof RegionNode )
+            for( int i=1; i<bb.nIns(); i++ )
+                mergeLiveOut(alloc,bb,i);
+        else
+            mergeLiveOut(alloc,bb,0);
+    }
+
+    private static void do_node(RegAlloc alloc, Node n) {
+        // Defining means killing live LRG
+        LRG lrg = alloc.lrg(n);
+        if( lrg != null ) {
+            // Check for def-side self-conflict live ranges.  These must split,
+            // and only happens during the first round a particular LRG splits.
+            selfConflict(alloc,n,lrg);
+            TMP.remove(lrg);    // Kill def
+        }
+
+        // Phis use and define the same live range, i.e. these LRGs already
+        // marked conflicted, no need to mark again
+        if( n instanceof PhiNode )
+            return;
+
+        // Kill any killed registers; milli-code routines like New can kill
+        // will not being a CFG.
+        if( n instanceof MachNode m )
+            kills(alloc,m);
+
+        // Interfere n with all live
+        if( lrg!=null ) {
+            // Interfere n with all live
+            for( LRG tlrg : TMP.keySet() ) {
+                assert tlrg.leader();
+                // Skip self
+                if( lrg != tlrg &&
+                    // And register sets overlap
+                    lrg._mask.overlap(tlrg._mask) )
+                    // Then tlrg and lrg interfere.  If lrg needs the single
+                    // last tlrg register at some point, either tlrg or lrg
+                    // must fail.  If *n* (a subset of lrg) needs the single
+                    // last tlrg register then only tlrg must fail.
+                    if( ((MachNode)n).outregmap().size1() ) {
+                        if( !tlrg.clr(lrg._mask.firstReg()) ) // Clear bit, no interference
+                            alloc.fail(tlrg);                 // Clearing drives mask to empty
+                    } else addIFG(lrg,tlrg); // Add interference
+            }
+        }
+
+
+        // Check for self-conflict live ranges.  These must split, and only
+        // happens during the first round when a particular LRG splits.
+        // Also record use-side spills for biased coloring.
+        // Then make all inputs live.
+        for( int i=1; i<n.nIns(); i++ ) {
+            Node def = n.in(i);
+            if( def==null ) continue;
+            LRG lrg1 = alloc.lrg(def);
+            if( lrg1==null ) continue; // Anti-dependence, etc, no register
+
+            // Check use-side for self-conflict; if so we MUST split
+            selfConflict(alloc,def,lrg1);
+
+            // Look for a must-use single register conflicting with some other must-def.
+            if( n instanceof MachNode m ) {
+                RegMask ni_mask = m.regmap(i);
+                if( ni_mask.size1() ) { // Must-use single register
+                    // Search all current live
+                    for( LRG tlrg : TMP.keySet() ) {
+                        assert tlrg.leader();
+                        Node live = TMP.get(tlrg);
+                        if( live != def && live instanceof MachNode lmach && lmach.outregmap().overlap(ni_mask) ) {
+                            // Deny the register, since it absolutely must be used here
+                            if( !tlrg.clr(ni_mask.firstReg()) )
+                                // Then direct reg-reg conflict between use here (at n.in(i)) and def (of tlrg) there.
+                                // Fail the older live range, it must move its register.
+                                alloc.fail( tlrg );
+                        }
+                    }
+                }
+            }
+
+            // All inputs live - except in self conflicts, where we keep the prior def alive
+            // until it goes.
+            TMP.put(lrg1,def);
+        }
+    }
+
+    // Single-register defines *must* have their register; other live ranges
+    // *must* avoid this register, so instead of interfering we remove the
+    // single register from the other rmask.
+    private static void kills( RegAlloc alloc, MachNode m ) {
+        RegMask killMask = m.killmap();
+        if( killMask==null ) return;
+        // Kill registers with all live
+        for( LRG tlrg : TMP.keySet() ) {
+            assert tlrg.leader();
+            // Always, tlrg cannot use kills
+            if( tlrg._mask.overlap(killMask) ) {
+                // Disallow clone-ables from killing registers.  Just fail
+                // them and re-clone closer to target... so no kill.
+                // Special case for Intel XOR used to zero.
+                Node n = (Node)m;
+                CFGNode effUseBlk = n.out(0) instanceof PhiNode phi ? phi.region().cfg(phi._inputs.find(n)) : n.out(0).cfg0();
+                if( m.isClone() &&  // Must be clonable
+                    (n.nOuts()>1 || // Has many users OR
+                     // Only 1 user but effective use is remote block
+                     effUseBlk != n.cfg0() ))
+                    // Then fail the clonable; it should split or move
+                    alloc.fail(alloc.lrg((Node)m));
+                // Else clonable cannot move
+                else if( !tlrg.sub(killMask) )
+                    alloc.fail(tlrg);
+            }
+        }
+    }
+
+    // Check for self-conflict live ranges.  These must split, and only happens
+    // during the first round a particular LRG splits.
+    private static void selfConflict(RegAlloc alloc, Node n, LRG lrg) {
+        selfConflict(alloc,n,lrg,TMP.get(lrg));
+    }
+    private static void selfConflict(RegAlloc alloc, Node n, LRG lrg, Node prior) {
+        if( prior!=null && prior != n ) {
+            lrg.selfConflict(prior);
+            lrg.selfConflict(n);
+            alloc.fail(lrg); // 2 unrelated values live at once same live range; self-conflict
+        }
+    }
+
+    // Merge TMP into bb's live-out set; if changes put bb on WORK
+    private static void mergeLiveOut( RegAlloc alloc, CFGNode priorbb, int i ) {
+        CFGNode bb = priorbb.cfg(i);
+        if( bb == null ) return; // Start has no prior
+        if( !bb.blockHead() ) bb = bb.cfg0();
+        //if( i==0 && !(bb instanceof StartNode) ) bb = bb.cfg0();
+        assert bb.blockHead();
+
+        // Lazy get live-out set for bb
+      IdentityHashMap<LRG, Node> lrgs = BBOUTS.computeIfAbsent( bb, k -> new IdentityHashMap<>() );
+
+      for( LRG lrg : TMP.keySet() ) {
+            Node def = TMP.get(lrg);
+            // Effective def comes from phi input from prior block
+            if( def instanceof PhiNode phi && phi.cfg0()==priorbb ) {
+                assert i!=0;
+                def = phi.in( i );
+            }
+            Node def_bb = lrgs.get(lrg);
+            if( def_bb==null ) {
+                lrgs.put(lrg,def);
+                pushWork(bb);
+            } else {
+                // Alive twice with different definitions; self-conflict
+                selfConflict(alloc,def,lrg,def_bb);
+            }
+        }
+    }
+
+
+    // ------------------------------------------------------------------------
+    // Color the inference graph.
+
+    // Coloring works by removing "trivial" live ranges from the IFG - those
+    // live ranges with fewer neighbors than available colors (registers).
+    // These are trivial because even if every neighbor takes a unique color,
+    // there's at least one more available to color this live range.
+
+    // If we hit a clique of non-trivial live ranges, we pick one to be "at
+    // risk" of not-coloring.  Good choices include live ranges with a large
+    // area and few hot uses.
+
+    // Then we reverse and put-back live ranges - picking a color as we go.
+    // If there's no spare color we'll have to spill this at-risk live range.
+
+    public static boolean color(int round, RegAlloc alloc) {
+        int maxlrg = alloc._LRGS.length, nlrgs=0;
+        for( int i=1; i<maxlrg; i++ )
+            if( alloc._LRGS[i] != null )
+                nlrgs++;
+
+        // Simplify
+
+        // Walk all the LRGS looking for some trivial ones to start with.
+        // During this pass all LRGS are broken into 3 disjoint sets:
+        // - trivial, removed from IFG;           color_stack[0 to sptr]
+        // - trivial, not (yet) removed from IFG; color_stack[sptr to swork]
+        // - unknown;                             color_stack[work to maxlrg]
+
+        // Gather all not-unified (not-null); separate trivial and non-trivial set.
+        int sptr = 0, swork = 0;
+        LRG[] color_stack = new LRG[nlrgs];
+        for( int i=0, j=0; i<maxlrg; i++ ) {
+            LRG lrg = alloc._LRGS[i];
+            if( lrg==null ) continue; // Unified lrgs are null here
+            color_stack[j++] = lrg;
+            if( lrg.lowDegree() ) swap(color_stack, swork++, j-1);
+        }
+
+        // Pull all lrgs from IFG, in trivial order if possible
+        while( sptr < color_stack.length ) {
+            // Swap best color up front
+            pickColor(color_stack,sptr,swork);
+
+            // Pick a trivial lrg, and (temporarily) remove from the IFG.
+            LRG lrg = color_stack[sptr++];
+            // If sptr was swork, then pulled an at-risk lrg
+            if( sptr > swork )
+                swork = sptr;
+
+            // Walk all neighbors and remove
+            if( lrg._adj != null ) {
+                for( LRG nlrg : lrg._adj ) {
+                    // Remove and compress out neighbor
+                    if( nlrg.removeCompress(lrg) ) {
+                        // Neighbor is just exactly going trivial as 'lrg' is removed from IFG
+                        // Find "j" position in the color_stack
+                        int jx = swork;  while( color_stack[jx] != nlrg ) jx++;
+                        // Add trivial neighbor to trivial list.  Pull lrg j out of
+                        // unknown set, since its now in the trivial set
+                        swap(color_stack, swork++, jx);
+                    }
+                }
+            }
+        }
+
+        // Reverse simplify (unstack the color stack), and set colors (registers) for live ranges
+        while( sptr > 0 ) {
+            LRG lrg = color_stack[--sptr];
+            if( lrg==null ) continue;
+            RegMaskRW rmask = lrg._mask.copy();
+            // Walk neighbors and remove adjacent colors
+            if( lrg._adj!=null ) {
+                for( LRG nlrg : lrg._adj ) {
+                    nlrg.reinsert(lrg);
+                    int reg = nlrg._reg;
+                    if( reg!= -1 ) // Failed neighbors do not count
+                        rmask.clr(reg); // Remove neighbor from my choices
+                }
+            }
+            // At-risk live-range did not color?
+            if( rmask.isEmpty() ) {
+                alloc.fail(lrg);
+                lrg._reg = -1;
+            } else {
+                // Pick first available register
+                short reg = rmask.firstReg();
+                // Pick a "good" color from the choices.  Typically, biased-coloring
+                // removes some over-spilling.
+                if( rmask.size() > 1 ) reg = biasColor(alloc,lrg,reg,rmask);
+                lrg._reg = reg; // Assign the register
+            }
+        }
+
+        return alloc.success();
+    }
+
+    // Pick LRG from color stack
+    private static void pickColor(LRG[] color_stack, int sptr, int swork) {
+        // Out of trivial colorable, pick an at-risk to pull
+        if( sptr==swork )
+            swap(color_stack,sptr,pickRisky(color_stack,sptr));
+        // When coloring, we'd like to give more choices; so when coloring we'd
+        // like to see the single-def first (since no choices anyway), then
+        // non-split related (so more live ranges get colored), then
+        // split-related last, so they have more colors to bias towards.
+
+        // Working in reverse, pick first split-related with many regs, then
+        // those with some regs, then single-def.
+        int bidx=sptr;
+        LRG best=color_stack[bidx];
+        for( int idx = sptr+1; idx < swork; idx++ )
+            if( betterLRG(best,color_stack[idx]) )
+                best = color_stack[bidx=idx];
+        if( bidx != sptr )
+            swap(color_stack,sptr,bidx); // Pick best at sptr
+    }
+
+    private static boolean betterLRG( LRG best, LRG lrg ) {
+        // If single-def varies, keep the not-single-def
+        if( best.size1() != lrg.size1() )
+            return best.size1();
+        // If hasSplit varies, keep the hasSplit
+        if( best.hasSplit() != lrg.hasSplit() )
+            return lrg.hasSplit();
+        // Keep large register count
+        return best.size() < lrg.size();
+    }
+
+    // Pick a live range that hasn't already spilled, or has a single-def-
+    // single-use that are not adjacent.
+    private static int pickRisky( LRG[] color_stack, int sptr ) {
+        int best=sptr;
+        int bestScore = pickRiskyScore(color_stack[best]);
+        for( int i=sptr+1; i<color_stack.length; i++ ) {
+            if( bestScore == 1000000 ) return best; // Already max score
+            int iScore = pickRiskyScore(color_stack[i]);
+            if( iScore > bestScore )
+                { best = i; bestScore = iScore; }
+        }
+        return best;
+    }
+
+    // Pick a live range to pull, that might not color.
+    //
+    // Picking a live range with a very large span, with defs and uses outside
+    // loops means spilling a relative cheap live range and getting that
+    // register over a large area.
+    //
+    // Picking a live range that is very close to coloring might allow it to
+    // color despite being risky.
+    private static int pickRiskyScore( LRG lrg ) {
+        // Pick single-def clonables that are not right next to their single-use.
+        // Failing to color these will clone them closer to their uses.
+        if( !lrg._multiDef && lrg._machDef.isClone() ) {
+            Node def = ((Node)lrg._machDef);
+            Node use = ((Node)lrg._machUse);
+            CFGNode cfg = def.cfg0();
+            if( cfg != use.cfg0() || // Different blocks OR
+              // Same block, but not close
+              cfg._outputs.find(def) < cfg._outputs.find(use)+1 )
+                return 1000000;
+        }
+
+        // Always pick callee-save registers as being very large area recovered
+        // and very cheap to spill.
+        if( lrg._machDef instanceof CalleeSaveNode )
+            return 1000000-2;
+        if( lrg._splitDef != null && lrg._splitDef.in(1) instanceof CalleeSaveNode &&
+            lrg._splitUse != null && lrg._splitUse.out(0) instanceof ReturnNode )
+            return 1000000-1;
+
+        // TODO: cost/benefit model.  Perhaps counting loop-depth (freq) of def/use for cost
+        // and "area" for benefit
+        return 1000;
+    }
+
+    private static short biasColor( RegAlloc alloc, LRG lrg, short reg, RegMask mask ) {
+        if( mask.size1() ) return reg;
+        // Check chain of splits up the def-chain.  Take first allocated
+        // register, and if it's available in the mask, take it.
+        Node defSplit = lrg._splitDef, useSplit = lrg._splitUse;
+        int tidx, cnt=0;
+
+        while( (tidx=biasable(defSplit)) != 0 || biasable(useSplit) != 0 ) {
+            if( cnt++ > 10 ) break;
+
+            if( tidx != 0 ) {
+                short bias = biasColor( alloc, defSplit, mask );
+                if( bias >= 0 ) return bias; // Good bias
+                if( bias == -2 ) defSplit = null; // Kill this side, no more searching
+            } else defSplit = null;
+
+            if( biasable(useSplit) != 0 ) {
+                short bias = biasColor( alloc, useSplit, mask );
+                if( bias >= 0 ) return bias; // Good bias
+                if( bias == -2 ) useSplit = null; // Kill this side, no more searching
+            } else useSplit = null;
+
+            if( defSplit != null ) {
+                short bias = biasColorNeighbors( alloc, defSplit, mask );
+                if( bias >= 0 ) return bias;
+                // Advance def side
+                defSplit = defSplit.in(tidx);
+            }
+
+            if( useSplit != null ) {
+                short bias = biasColorNeighbors( alloc, useSplit, mask );
+                if( bias >= 0 ) return bias;
+                useSplit = useSplit.out(0);
+            }
+
+        }
+        return mask.firstReg();
+    }
+
+    private static int biasable(Node split) {
+        if( split instanceof SplitNode ) return 1; // Yes biasable, advance is slot 1
+        if( split instanceof PhiNode ) return 1;   // Yes biasable, advance is slot 1
+        if( !(split instanceof MachNode mach) ) return 0; // Not biasable
+        return mach.twoAddress();                         // Only biasable if 2-addr
+    }
+
+    // 3-way return:
+    // - good bias reg, take it & exit
+    // - this path is cutoff; do not search here anymore
+    // - advance this side
+    private static short biasColor( RegAlloc alloc, Node split, RegMask mask ) {
+        short bias = alloc.lrg(split)._reg;
+        if( bias != -1 ) {
+            if( mask.test(bias) ) return bias; // Good bias
+            else return -2;                    // Kill this side
+        } else return -1;                      // Advance this side
+    }
+
+    // Check if we can match "split" color, or else trim "mask" to colors
+    // "split" might get.
+    private static short biasColorNeighbors( RegAlloc alloc, Node split, RegMask mask ) {
+        LRG slrg = alloc.lrg(split);
+        if( slrg._adj == null ) return -1; // No trimming
+
+        // Can I limit my own choices to valid neighbor choices?
+        for( LRG alrg : slrg._adj ) {
+            int reg = alrg._reg;
+            if( reg == -1 && alrg._mask.size1() )
+                reg = alrg._mask.firstReg();
+            if( reg != -1 ) {
+                mask.clr(alrg._reg);
+                if( mask.size1() )
+                    return mask.firstReg();
+            }
+        }
+        return -1;              // No obvious color choice, but mask got trimmed
+    }
+
+    private static void swap( LRG[] ary, int x, int y ) {
+        LRG tmp = ary[x]; ary[x] = ary[y]; ary[y] = tmp;
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/LRG.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/LRG.java
new file mode 100644
index 0000000..0662c5f
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/LRG.java
@@ -0,0 +1,235 @@
+package com.compilerprogramming.ezlang.compiler.codegen;
+
+import com.compilerprogramming.ezlang.compiler.Ary;
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+import java.util.IdentityHashMap;
+
+/** A Live Range
+ * <p>
+ *  A live range is a set of nodes and edges which must get the same register.
+ *  Live ranges form an interconnected web with almost no limits on their
+ *  shape.  Live ranges also gather the set of register constraints from all
+ *  their parts.
+ * <p>
+ *  Most of the fields in this class are for spill heuristics, but there are a
+ *  few key ones that define what a LRG is.  LRGs have a unique dense integer
+ *  `_lrg` number which names the LRG.  New `_lrg` numbers come from the
+ *  `RegAlloc._lrg_num` counter.  LRGs can be unioned together -
+ *  (<a href="https://en.wikipedia.org/wiki/Disjoint-set_data_structure">this is the Union-Find algorithm</a>)
+ *  - and when this happens the lower numbered `_lrg` wins.  Unioning only
+ *  happens during `BuildLRG` and happens because either a `Phi` is forcing all
+ *  its inputs and outputs into the same register, or because of a 2-address
+ *  instruction.  LRGs have matching `union` and `find` calls, and a set
+ *  `_leader` field.
+ */
+public class LRG {
+
+    // Dense live range numbers
+    final short _lrg;
+
+    // U-F leader; null if leader
+    LRG _leader;
+
+    // Choosen register
+    short _reg;
+
+    // Count of single-register defs and uses
+    short _1regDefCnt, _1regUseCnt;
+    // Count of all defs, uses.  Mostly interested 1 vs many
+    boolean _multiDef, _multiUse;
+
+    // A sample MachNode def in the live range
+    public MachNode _machDef, _machUse;
+    short _uidx;                // _machUse input
+
+    // Mask set to empty via a kill-mask.  This is usually a capacity kill,
+    // which means we need to split and spill into memory - which means even if
+    // the def-side has many registers, it MUST spill.
+    boolean _killed;
+
+    // Some splits used in biased coloring
+    MachConcreteNode _splitDef, _splitUse;
+
+    // All the self-conflicting defs for this live range
+    IdentityHashMap<Node,String> _selfConflicts;
+
+    // AND of all masks involved; null if none have been applied yet
+    RegMask _mask;
+
+    // Adjacent Live Range neighbors.  Only valid during coloring
+    Ary<LRG> _adj;
+
+    void addNeighbor(LRG lrg) {
+        if( _adj==null ) _adj = new Ary<>(LRG.class);
+        _adj.push(lrg);
+    }
+
+    // Remove and compress neighbor list; store ex-neighbor past
+    // end for unwinding Simplify.  True if exactly going low-degree
+    boolean removeCompress( LRG lrg ) {
+        _adj.swap(_adj.find(lrg),_adj._len-1);
+        return _adj._len-- == _mask.size();
+    }
+
+    void reinsert( LRG lrg ) {
+        assert _adj._es[_adj._len]==lrg;
+        _adj._len++;
+    }
+
+    // More registers than neighbors
+    boolean lowDegree() { return (_adj==null ? 0 : _adj._len) < _mask.size(); }
+
+    LRG( short lrg ) { _lrg = lrg; _reg = -1; }
+
+    boolean leader() { return _leader == null; }
+
+    // Fast-path Find from the Union-Find algorithm
+    LRG find() {
+        if( _leader==null ) return this; // I am the leader
+        if( _leader._leader==null ) // I point to the leader
+            return _leader;
+        return _rollup();
+    }
+    // Slow-path rollup of U-F
+    private LRG _rollup() {
+        LRG ldr = _leader._leader;
+        // Roll-up
+        while( ldr._leader!=null ) ldr = ldr._leader;
+        LRG l2 = this;
+        while( l2 != ldr ) {
+            LRG l3 = l2._leader;
+            l2._leader = ldr;
+            l2 = l3;
+        }
+        return ldr;
+    }
+
+    // Union `this` and `lrg`, keeping the lower numbered _lrg.
+    // Includes a number of fast-path cutouts.
+    LRG union( LRG lrg ) {
+        assert leader();
+        if( lrg==null ) return this;
+        lrg = lrg.find();
+        if( lrg==this ) return this;
+        return _lrg < lrg._lrg ? _union(lrg) : lrg._union(this);
+    }
+    // Union `this` and `lrg`, folding together all stats.
+    private LRG _union( LRG lrg ) {
+        // Set U-F leader
+        lrg._leader = this;
+        // Fold together stats
+        if( _machDef==null ) {
+            _machDef = lrg._machDef;
+        } else if( lrg._machDef!=null ) {
+            if( _machDef != lrg._machDef ) _multiDef=true;
+            if( _1regDefCnt==0 )
+                _machDef = lrg._machDef;
+            else if( _machDef==lrg._machDef )
+                _1regDefCnt--;
+        }
+        _1regDefCnt += lrg._1regDefCnt;
+        _multiDef |= lrg._multiDef;
+
+        if( _machUse==null ) {
+            _machUse = lrg._machUse;
+        } else if( lrg._machUse!=null ) {
+            if( _machUse != lrg._machUse ) _multiUse=true;
+            if( _1regUseCnt==0 ) {
+                _machUse = lrg._machUse;
+                _uidx = lrg._uidx;
+            }
+            else if( _machUse==lrg._machUse )
+                _1regUseCnt--;
+        }
+        _1regUseCnt += lrg._1regUseCnt;
+        _multiUse |= lrg._multiUse;
+
+        // Fold deepest Split
+        _splitDef = deepSplit(_splitDef,lrg._splitDef);
+        _splitUse = deepSplit(_splitUse,lrg._splitUse);
+
+        // Fold together masks
+        RegMask mask = _mask.and(lrg._mask);
+        if( mask==null )
+            mask = _mask.copy().and(lrg._mask);
+        _mask = mask;
+        return this;
+    }
+
+    private static MachConcreteNode deepSplit( MachConcreteNode s0, MachConcreteNode s1 ) {
+        return s0==null || (s1!=null && s0.cfg0().loopDepth() < s1.cfg0().loopDepth()) ? s1 : s0;
+    }
+
+    // Record any Mach def for spilling heuristics
+    LRG machDef( MachNode def, boolean size1 ) {
+        if( _machDef!=null && _machDef!=def )
+            _multiDef = true;
+        if( _machDef==null || size1 )
+            _machDef = def;
+        if( size1 )
+            _1regDefCnt++;
+        if( def instanceof SplitNode split )
+            _splitDef = deepSplit(_splitDef,split);
+        return this;
+    }
+
+    // Record any Mach use for spilling heuristics
+    LRG machUse( MachNode use, short uidx, boolean size1 ) {
+        if( _machUse!=null && _machUse!=use )
+            _multiUse = true;
+        if( _machUse==null || size1 )
+            { _machUse = use; _uidx = uidx; }
+        if( size1 )
+            _1regUseCnt++;
+        if( use instanceof SplitNode split )
+            _splitUse = deepSplit(_splitUse,split);
+        return this;
+    }
+
+    boolean hasSplit() { return _splitDef != null || _splitUse != null; }
+    short size() { return _mask.size(); }
+    boolean size1() { return _mask.size1(); }
+
+
+    void selfConflict( Node def ) {
+        if( _selfConflicts == null ) _selfConflicts = new IdentityHashMap<>();
+        _selfConflicts.put(def,"");
+    }
+
+
+    // Record intersection of all register masks.
+    // True if still has registers
+    boolean and( RegMask mask ) {
+        RegMask mask2 = mask.and(_mask);
+        if( mask2==null )
+            mask2 = _mask.copy().and(mask);
+        _mask = mask2;
+        return !_mask.isEmpty();
+    }
+    // Remove this singular register
+    // True if still has registers
+    boolean clr( int reg ) {
+        if( _mask.clr(reg) ) return true;
+        _mask = _mask.copy();   // Need a mutable copy
+        return _mask.clr(reg);
+    }
+    // Subtract mask (AND complement)
+    // True if still has registers
+    boolean sub( RegMask mask ) {
+        RegMask mask2 = _mask.sub(mask);
+        if( mask2==null )
+            mask2 = _mask.copy().sub(mask);
+        _mask = mask2;
+        return !_mask.isEmpty();
+    }
+
+    @Override public String toString() { return toString(new SB()).toString(); }
+
+    public SB toString( SB sb ) {
+        sb.p("V").p(_lrg);
+        if( _mask!=null ) _mask.toString(sb);
+        return sb;
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/ListScheduler.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/ListScheduler.java
new file mode 100644
index 0000000..220e7fb
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/ListScheduler.java
@@ -0,0 +1,258 @@
+package com.compilerprogramming.ezlang.compiler.codegen;
+
+import com.compilerprogramming.ezlang.compiler.Ary;
+import com.compilerprogramming.ezlang.compiler.IterPeeps.WorkList;
+import com.compilerprogramming.ezlang.compiler.Utils;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+import com.compilerprogramming.ezlang.compiler.sontypes.*;
+import java.util.*;
+
+public abstract class ListScheduler {
+
+    // eXtra per-node stuff for scheduling.
+    // Nodes are all block-local, and projections fold into their parent.
+    private static final IdentityHashMap<Node,XSched> XS = new IdentityHashMap<>();
+    private static class XSched {
+
+        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
+
+        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); }
+
+        static XSched get(Node n) { return n==null ? null : XS.get(n instanceof ProjNode && !(n.in(0) instanceof CallEndNode) ? n.in(0) : n); }
+
+        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 if( !def.isMem() ) _ruse = true; // Remote register user
+                    }
+            // Count remote outputs and allowed registers
+            if( n instanceof MultiNode )
+                for( Node use : n._outputs )
+                    computeSingleRDef(bb,use);
+            else
+                computeSingleRDef(bb,n);
+            return this;
+        }
+
+        private void computeSingleRDef(CFGNode bb, Node n) {
+            // See if this is 2-input, and that input is single-def
+            if( n instanceof MachNode mach && mach.twoAddress() != 0 ) {
+                XSched xs = XS.get(n.in(mach.twoAddress()));
+                if( xs != null )
+                    _single = xs._single;
+            }
+            // Visit all outputs
+            RegMaskRW rmask = !_single && 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();
+        }
+    }
+
+
+    // List schedule every block
+    public static void sched( CodeGen code ) {
+        XS.clear();
+        for( CFGNode bb : code._cfg )
+            if( bb.blockHead() )
+                local(bb,false);
+    }
+
+    // Classic list scheduler
+    private static void local( CFGNode bb, boolean trace ) {
+        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.
+        int 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 bcnts[]. Once
+        // that gets to 0, all the nodes using it are scheduled, so it's ready.
+        int sched = 0;
+        while( sched < len ) {
+            assert sched < ready;
+            int  pick = best(bb._outputs,sched,ready,trace);  // 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 )
+                    for( Node useuse : use._outputs )
+                        ready = ready(bb,useuse,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) && 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( Ary<Node> blk, int sched, int ready, boolean trace ) {
+        int pick = sched;
+        Node p = blk.at(pick);
+        int score = score(p);
+        if( trace ) { System.out.printf("%4d N%d ",score,p._nid); System.out.println(p); }
+        for( int i=sched+1; i<ready; i++ ) {
+            Node n = blk.at(i);
+            int nscore = score(n);
+            if( trace ) { System.out.printf("%4d N%d ",nscore,n._nid); System.out.println(n); }
+            if( nscore > score )
+                { score = nscore; pick = i; p = n; }
+        }
+        if( trace ) { System.out.printf("Pick: N%d %s\n\n",p._nid,p); }
+        return pick;
+    }
+
+    // Highest score wins.  Max at 1000, min at 10, except specials.
+    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 (delay) 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 defining a remote value, just generically stall alot.  Value is
+        // used in a later block, can we delay until the end of this block?
+        if( xn._rdef ) score = 200; // If defining a remote value, just generically stall alot
+        boolean flags = false;
+        if( n instanceof MultiNode ) {
+            for( Node use : n._outputs )
+                flags |= singleUseNotReady( use, xn._single );
+        } else
+            flags |= singleUseNotReady( n, xn._single );
+        score +=  -10 * Math.min( CNT[1], 2 );
+        score += -100 * Math.min( CNT[2], 2 );
+        if( flags ) return 10;
+
+        // 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 && n.in(i).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
+        assert 10 <= score && score <= 990;
+        return score;
+    }
+
+    // Single use, and that use is not ready to fire once this one does.  If
+    // true, stalling 'n' might reduce 'n's lifetime.  Stall by 1 (2 if single)
+    // per not-ready.
+
+    // If CFG user in this block, it must be last, so definitely stall
+    // single-def (which stalls flags on x86,arm until the jmp).
+    private static boolean singleUseNotReady( Node n, boolean single ) {
+        // If n can rematerialize, assume allocator will split into private uses
+        // as needed.  No impact on local scheduling.
+        if( n.nOuts()>1 && n instanceof MachNode mach && mach.isClone() )
+            return false;
+        for( Node use : n.outs() ) {
+            XSched xu = XSched.get(use);
+            if( xu != null && xu._n instanceof CFGNode ) return true;
+            if( xu != null && xu._bcnt > 0 )
+                CNT[single ? 2 : 1]++; // Since bcnt>0 or CFG, stall until user is more ready
+        }
+        return false;
+    }
+
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/Machine.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/Machine.java
new file mode 100644
index 0000000..273ac75
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/Machine.java
@@ -0,0 +1,48 @@
+package com.compilerprogramming.ezlang.compiler.codegen;
+
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+abstract public class Machine {
+    // Human readable machine name.  Something like "x86-64" or "arm" or "risc5"
+    public abstract String name();
+    // Default opcode size for printing; 4 bytes for most 32-bit risc chips
+    public int defaultOpSize() { return 4; }
+    // Create a split op; any register to any register, including stack slots
+    public abstract SplitNode split( String kind, byte round, LRG lrg);
+    // List of caller-save registers
+    public abstract RegMask callerSave();
+    // List of callee-save registers
+    public abstract RegMask calleeSave();
+    // 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 );
+
+    // Convert a register to a zero-based stack *slot*, or -1.
+    // Stack slots are assumed 8 bytes each.
+    // Actual stack layout is up to each CPU.
+    // X86, with too many args & spills:
+    // | CALLER |
+    // |  argN  | // slot 1, required by callER
+    // +--------+
+    // |  RPC   | // slot 0, required by callER
+    // | callee | // slot 3, callEE
+    // | callee | // slot 2, callEE
+    // |  PAD16 |
+    // +--------+
+
+    // RISC/ARM, with too many args & spills:
+    // | CALLER |
+    // |  argN  | // slot 0, required by callER
+    // +--------+
+    // | callee | // slot 3, callEE: might be RPC
+    // | callee | // slot 2, callEE
+    // | callee | // slot 1, callEE
+    // |  PAD16 |
+    // +--------+
+    public abstract int stackSlot( int reg );
+    // Human-readable name for a register number, e.g. "RAX" or "R0"
+    public abstract String reg( int reg );
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/RIPRelSize.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/RIPRelSize.java
new file mode 100644
index 0000000..0d35458
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/RIPRelSize.java
@@ -0,0 +1,14 @@
+package com.compilerprogramming.ezlang.compiler.codegen;
+
+// This Node rip-relative encodings has sizes that vary by delta
+public interface RIPRelSize {
+    // delta is the distance from the opcode *start* to the target start.  Each
+    // hardware target adjusts as needed, X86 measures from the opcode *end*
+    // and small jumps are 2 bytes, so they'll need to subtract 2.
+    byte encSize(int delta);
+
+    // Patch full encoding in.  Used for both short and long forms.  delta
+    // again is from the opcode *start*.
+    void patch( Encoding enc, int opStart, int opLen, int delta );
+
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/RegAlloc.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/RegAlloc.java
new file mode 100644
index 0000000..634ebf1
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/RegAlloc.java
@@ -0,0 +1,526 @@
+package com.compilerprogramming.ezlang.compiler.codegen;
+
+import com.compilerprogramming.ezlang.compiler.Ary;
+import com.compilerprogramming.ezlang.compiler.Utils;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+import java.util.Arrays;
+import java.util.IdentityHashMap;
+
+/**
+  * "Briggs/Chaitin/Click".
+  * Graph coloring.
+  * <p>
+  * Every def and every use have a bit-set of allowed registers.
+  * Fully general to bizarre chips.
+  * Multiple outputs fully supported, e.g. add-with-carry or combined div/rem or "xor rax,rax" setting both rax and flags.
+  * <p>
+  * Intel 2-address accumulator-style ops fully supported.
+  * Op-to-stack-spill supported.
+  * All addressing modes supported.
+  * Register pairs could be supported with some grief.
+  * <p>
+  * Splitting instead of spilling (simpler implementation).
+  * Stack slots are "just another register" (tiny stack frames, simpler implementation).
+  * Stack/unstack during coloring with a marvelous trick (simpler implementation).
+  * <p>
+  * Both bitset and adjacency list formats for the interference graph; one of
+  * the few times it's faster to change data structures mid-flight rather than
+  * just wrap one of the two.
+  * <p>
+  * Liveness computation and interference graph built in the same one pass (one
+  * fewer passes per round of coloring).
+  * <p>
+  * Single-register def or use live ranges deny neighbors their required
+  * register and thus do not interfere, vs interfering and denying the color
+  * and coloring time.  Basically all function calls do this, but many oddball
+  * registers also, e.g. older div/mod/mul ops. (5x smaller IFG, the only
+  * O(n^2) part of this operation).
+  */
+
+public class RegAlloc {
+    // Main Coloring Algorithm:
+    // Repeat until colored:
+    //   Build Live Ranges (LRGs)
+    //   - Intersect allowed registers
+    //   If hard conflicts (LRGs with no allowed registers)
+    //   - Pre-split conflicted LRGs, and repeat
+    //   Build Interference Graph (IFG)
+    //   - Self conflicts split now
+    //   Color (remove trivial first then any until empty; reverse assign colors
+    //   - If color fails:
+    //   - - Split uncolorable LRGs
+
+    // Top-level program graph structure
+    final CodeGen _code;
+
+    public int _spills, _spillScaled;
+
+    // -----------------------
+    // Live ranges with self-conflicts or no allowed registers
+    private final IdentityHashMap<LRG,String> _failed = new IdentityHashMap<>();
+    void fail( LRG lrg ) {
+        assert lrg.leader();
+        _failed.put(lrg,"");
+    }
+    boolean success() { return _failed.isEmpty(); }
+
+
+    // -----------------------
+    // Map from Nodes to Live Ranges
+    private final IdentityHashMap<Node,LRG> _lrgs = new IdentityHashMap<>();
+    short _lrg_num;
+
+    // Define a new LRG, and assign n
+    LRG newLRG( Node n ) {
+        LRG lrg = lrg(n);
+        if( lrg!=null ) return lrg;
+        lrg = new LRG(_lrg_num++);
+        LRG old = _lrgs.put(n,lrg); assert old==null;
+        return lrg;
+    }
+
+    // LRG for n
+    LRG lrg( Node n ) {
+        LRG lrg = _lrgs.get(n);
+        if( lrg==null ) return null;
+        LRG lrg2 = lrg.find();
+        if( lrg != lrg2 )
+            _lrgs.put(n,lrg2);
+        return lrg2;
+    }
+
+    // Find LRG for n.in(idx), and also map n to it
+    LRG lrg2( Node n, int idx ) {
+        LRG lrg = lrg(n.in(idx));
+        return union(lrg,n);
+    }
+
+    // Union any lrg for n with lrg and map to the union
+    LRG union( LRG lrg, Node n ) {
+        LRG lrgn = _lrgs.get(n);
+        LRG lrg3 = lrg.union(lrgn);
+        _lrgs.put(n,lrg3);
+        return lrg3;
+    }
+
+    // Force all unified to roll up; collect live ranges
+    LRG[] _LRGS;
+    void unify() {
+        Ary<LRG> lrgs = new Ary<>(LRG.class);
+        for( Node n : _lrgs.keySet() ) {
+            LRG lrg = lrg(n);
+            lrgs.setX(lrg._lrg,lrg);
+        }
+        _LRGS = lrgs.asAry();
+        // Remove unified lrgs from failed set also
+        for( LRG lrg : _failed.keySet().toArray(new LRG[0]) ) {
+            if( !lrg.leader() ) {
+                LRG lrg2 = lrg.find();
+                _failed.remove(lrg);
+                _failed.put(lrg2,"");
+            }
+        }
+    }
+
+
+    public short regnum( Node n ) {
+        LRG lrg = lrg(n);
+        return lrg==null ? -1 : lrg._reg;
+    }
+
+    // Printable register number for node n
+    String reg( Node n ) {
+        LRG lrg = lrg(n);
+        if( lrg==null ) return null;
+        if( lrg._reg == -1 ) return "V"+lrg._lrg;
+        return _code._mach.reg(lrg._reg);
+    }
+
+    // -----------------------
+    RegAlloc( CodeGen code ) { _code = code; }
+
+    public void regAlloc() {
+        // Insert callee-save registers
+        FunNode lastFun=null;
+        for( CFGNode bb : _code._cfg ) {
+            if( bb instanceof FunNode fun )
+                insertCalleeSave(lastFun=fun);
+            // Leaf routine, or not?
+            // X86 requires 16b RSP aligned if NOT leaf
+            if( bb instanceof CallNode ) lastFun._hasCalls = true;
+        }
+
+
+        // Top driver: repeated rounds of coloring and splitting.
+        byte round=0;
+        while( !graphColor(round) ) {
+            split(round);
+            if( round >= 7 )    // Really expect to be done soon
+                throw Utils.TODO("Allocator taking too long");
+            round++;
+        }
+        postColor();                       // Remove no-op spills
+    }
+
+    private boolean graphColor(byte round) {
+        _failed.clear();
+        _lrgs.clear();
+        _lrg_num = 1;
+        _LRGS=null;
+
+        return
+            // Build Live Ranges
+            BuildLRG.run(round,this) && // if no hard register conflicts
+            // Build Interference Graph
+            IFG.build(round,this) &&    // If no self conflicts or uncolorable
+            // Conservative coalesce copies
+            Coalesce.coalesce(round,this) &&
+            // Color attempt
+            IFG.color(round,this);      // If colorable
+    }
+
+    // Insert callee-save registers.  Walk the callee-save RegMask ignoring any
+    // Parms, then insert a Parm and an edge from the Ret to the Parm with the
+    // callee-save register.
+    private void insertCalleeSave( FunNode fun ) {
+        RegMask saves = _code._mach.calleeSave();
+        ReturnNode ret = fun.ret();
+
+        for( short reg = saves.firstReg(); reg != -1; reg = saves.nextReg(reg) ) {
+            ret.addDef(new CalleeSaveNode(fun,reg,_code._mach.reg(reg)));
+            assert ((MachNode)ret).regmap(ret.nIns()-1).firstReg()==reg;
+        }
+    }
+
+
+    // -----------------------
+    // Split conflicted live ranges.
+    void split(byte round) {
+
+        // In C2, all splits are handling in one pass over the program.  Here,
+        // in the name of clarity, we'll handle each failing live range
+        // independently... which generally requires a full pass over the
+        // program for each failing live range.  i.e., might be a lot of
+        // passes.
+        for( LRG lrg : _failed.keySet() )
+            split(round,lrg);
+    }
+
+    // Split this live range
+    boolean split( byte round, LRG lrg ) {
+        assert lrg.leader();  // Already rolled up
+
+        if( lrg._selfConflicts != null )
+            return splitSelfConflict(round,lrg);
+
+        // Register mask when empty; split around defs and uses with limited
+        // register masks.
+        if( lrg._mask.isEmpty() && (!lrg._multiDef || lrg._1regUseCnt==1) ) {
+            if( lrg._1regDefCnt <= 1 &&
+                lrg._1regUseCnt <= 1 &&
+                (lrg._1regDefCnt + lrg._1regUseCnt) > 0 )
+                return splitEmptyMaskSimple(round,lrg);
+            // Default to splitByLoop
+            //return splitEmptyMask(round,lrg);
+        }
+
+        // Generic split-by-loop depth.
+        return splitByLoop(round,lrg);
+    }
+
+    // Split live range with an empty mask.  Specifically forces splits at
+    // single-register defs or uses and not elsewhere.
+    boolean splitEmptyMaskSimple( byte round, LRG lrg ) {
+        // Live range has a single-def single-register, and/or a single-use
+        // single-register.  Split after the def and before the use.  Does not
+        // require a full pass.
+
+        // Split just after def
+        if( lrg._1regDefCnt==1 && !lrg._machDef.isClone() )
+            // Force must-split, even if a prior split same block because register
+            // conflicts.  Example:
+            //   alloc
+            //     V1/rax - forced by alloc
+            //   alloc
+            //     V2/rax - kills prior RAX
+            //   st4 [V1],len - No good, must split around
+            makeSplit("def/empty1",round,lrg).insertAfter((Node)lrg._machDef, false/*true*/);
+        // Split just before use
+        if( lrg._1regUseCnt==1 || (lrg._1regDefCnt==1 && ((Node)lrg._machDef).nOuts()==1) )
+            insertBefore((Node)lrg._machUse,lrg._uidx,"use/empty1",round,lrg,true);
+        return true;
+    }
+
+    // Split live range with an empty mask.  Specifically forces splits at
+    // single-register defs or uses everywhere.
+    boolean splitEmptyMask( byte round, LRG lrg ) {
+        findAllLRG(lrg);
+        // If no single-use or single-def, assume this is a complete register
+        // kill and force spilling everywhere.
+        boolean all = lrg._killed || (lrg._1regDefCnt + lrg._1regUseCnt)==0;
+        for( Node n : _ns ) {
+            if( !(n instanceof MachNode mach) ) continue;
+            // Find def of spilling live range; spilling everywhere, OR
+            // single-register DEF and not cloneable (since these will clone
+            // before every use)
+            if( lrg(n)==lrg && (all || (!mach.isClone() && mach.outregmap().size1() )) )
+                makeSplit(n,"def/empty2",round,lrg).insertAfter(n,true);
+            // Find all uses
+            for( int i=1; i<n.nIns(); i++ ) {
+                Node def = n.in(i);
+                // Skip any new splits inserted just this pass
+                while( def instanceof SplitNode && lrg(def)==null )
+                    def = def.in(1);
+                // Main def (past splits) is of the spilling lrg, and spilling
+                // single-register USE (or everywhere)
+                if( lrg(def)==lrg && (all || mach.regmap(i).size1()) )
+                    insertBefore(n,i,"use/empty2",round,lrg);
+            }
+        }
+        return true;
+    }
+
+    // Self conflicts require Phis (or two-address).
+    // Insert a split after every def.
+    boolean splitSelfConflict( byte round, LRG lrg ) {
+        // Sort conflict set, so we're deterministic
+        Node[] conflicts = lrg._selfConflicts.keySet().toArray(new Node[0]);
+        Arrays.sort(conflicts, (x,y) -> x._nid - y._nid );
+
+        // For all conflicts
+        for( Node def : conflicts ) {
+            assert lrg(def)==lrg; // Might be conflict use-side
+            // Split before each use that extends the live range; i.e. is a
+            // Phi or two-address
+            for( int i=0; i<def._outputs._len; i++ ) {
+                Node use = def.out(i);
+                if( (use instanceof PhiNode phi && !(phi.region() instanceof LoopNode && phi.in(2)==def ) ) ||
+                        (use instanceof MachNode mach && mach.twoAddress()!=0 && use.in(mach.twoAddress())==def) )
+                    insertBefore( use, use._inputs.find(def), "use/self/use",round,lrg );
+            }
+            // Split after the Phi which extends the LRG.  Split also before
+            // Phi slot 1 (and not all inputs), because Phis extend the live range.
+            // TODO: split before all inputs (except the last; at least 1 split here must be extra)
+            if( def instanceof PhiNode phi && !(def instanceof ParmNode) ) {
+                SplitNode split = makeSplit("def/self",round,lrg);
+                split.insertAfter(def,false);
+                if( split.nOuts()==0 )
+                    split.kill();
+                insertBefore(phi,1,"use/self/phi",round,lrg);
+            }
+            // Split before two-address ops which extend the live range
+            if( def instanceof MachNode mach && mach.twoAddress()!= 0 )
+                insertBefore(def,mach.twoAddress(),"use/self/two",round,lrg);
+        }
+        return true;
+    }
+
+
+    // Generic: split around the outermost loop with non-split def/uses.  This
+    // covers both self-conflicts (once we split deep enough) and register
+    // pressure issues.
+    boolean splitByLoop( byte round, LRG lrg ) {
+        findAllLRG(lrg);
+
+        // Find min loop depth for all non-split defs and uses.
+        long ld = (-1L<<32) | 9999;
+        for( Node n : _ns ) {
+            if( lrg(n)==lrg ) // This is a LRG def
+                ld = ldepth(ld,n,n.cfg0());
+            // PhiNodes check all CFG inputs
+            if( n instanceof PhiNode phi ) {
+                for( int i=1; i<n.nIns(); i++ )
+                    ld = ldepth(ld, phi.in(i), phi.region().cfg(i));
+            } else {
+                // Others check uses
+                for( int i=1; i<n.nIns(); i++ )
+                    if( lrgSame(n.in(i),lrg) ) // This is a LRG use
+                        ld = ldepth(ld,n,n.cfg0());
+            }
+        }
+        int min = (int)ld;
+        int max = (int)(ld>>32);
+
+
+        // If the minLoopDepth is less than the maxLoopDepth: for-all defs and
+        // uses, if at minLoopDepth or lower, split after def and before use.
+        for( Node n : _ns ) {
+            if( n instanceof SplitNode ) continue; // Ignoring splits; since spilling need to split in a deeper loop
+            if( n.isDead() ) continue; // Some Clonable went dead by other spill changes
+            // If this is a 2-address commutable op (e.g. AddX86, MulX86) and the rhs has only a single user,
+            // commute the inputs... which chops the LHS live ranges' upper bound to just the RHS.
+            if( n instanceof MachNode mach && lrg(n)==lrg && mach.twoAddress()==1 && mach.commutes() && n.in(2).nOuts()==1 )
+                n.swap12();
+
+            if( lrg(n)==lrg && // This is a LRG def
+                // At loop boundary, or splitting in inner loop
+                (min==max || n.cfg0().loopDepth() <= min) ) {
+                // Cloneable constants will be cloned at uses, not after def
+                if( !(n instanceof MachNode mach && mach.isClone()) &&
+                    // Single user is already a split
+                    !(n.nOuts()==1 && n.out(0) instanceof SplitNode) )
+                    // Split after def in min loop nest
+                    makeSplit("def/loop",round,lrg).insertAfter(n,false);
+            }
+
+            // PhiNodes check all CFG inputs
+            if( n instanceof PhiNode phi && !(n instanceof ParmNode)) {
+                for( int i=1; i<n.nIns(); i++ )
+                    // No split in front of a split
+                    if( !(n.in(i) instanceof SplitNode) &&
+                        // splitting in inner loop or at loop border
+                        (min==max || phi.region().cfg(i).loopDepth() <= min) &&
+                        // and not around the backedge of a loop (bad place to force a split, hard to remove)
+                        !(phi.region() instanceof LoopNode && i==2) )
+                        // Split before phi-use in prior block
+                        insertBefore(phi,i, "use/loop/phi",round,lrg);
+
+            } else {
+                // Others check uses
+                for( int i=1; i<n.nIns(); i++ ) {
+                    boolean sizeN=false;
+                    // This is a LRG use
+                    // splitting in inner loop or at loop border
+                    if( lrgSame( n.in( i ), lrg ) &&
+                        (min == max || (n.in(i) instanceof MachNode mach && mach.isClone()) || n.cfg0().loopDepth() <= min) )
+                        // Not a single-use split same block already
+                        //!(n.in(i) instanceof SplitNode && n.in(i).nOuts()==1 && n.cfg0()==n.in(i).cfg0() && (sizeN=!((MachNode)n).regmap(i).size1())) )
+                        // Split before in this block
+                        insertBefore( n, i, "use/loop/use", round,lrg, sizeN );
+                }
+            }
+        }
+        return true;
+    }
+
+    private boolean lrgSame(Node x, LRG lrg) {
+        LRG xlrg = lrg(x);
+        while( xlrg==null && x instanceof SplitNode )
+            xlrg=lrg(x = x.in(1));
+        return xlrg==lrg;
+    }
+
+    private static long ldepth( long ld, Node n, CFGNode cfg ) {
+        // Do not count splits
+        if( n instanceof SplitNode ) return ld;
+        // Collect min/max loop depth
+        int min = (int)ld;
+        int max = (int)(ld>>32);
+        int d = cfg.loopDepth();
+        min = Math.min(min,d);
+        max = Math.max(max,d);
+        return ((long)max<<32) | min;
+    }
+
+    // Find all members of a live range, both defs and uses
+    private final Ary<Node> _ns = new Ary<>(Node.class);
+    void findAllLRG( LRG lrg ) {
+        _ns.clear();
+        int wd = 0;
+        _ns.push((Node)lrg._machDef);
+        _ns.push((Node)lrg._machUse);
+        while( wd < _ns._len ) {
+            Node n = _ns.at(wd++);
+            if( lrg(n)!=lrg ) continue;
+            for( Node def : n._inputs )
+                if( lrg(def)==lrg && _ns.find(def)== -1 )
+                    _ns.push(def);
+            for( Node use : n._outputs )
+                if( _ns.find(use)== -1 )
+                    _ns.push(use);
+        }
+        for( Node n : _ns ) assert !n.isDead();
+    }
+
+    void insertBefore(Node n, int i, String kind, byte round, LRG lrg, boolean skip) {
+        Node def = n.in(i);
+        // Effective block for use
+        CFGNode cfg = n instanceof PhiNode phi ? phi.region().cfg(i) : n.cfg0();
+        // Def is a split ?
+        if( skip && def instanceof SplitNode ) {
+            boolean singleReg = n instanceof MachNode mach && mach.regmap(i).size1();
+            // Same block, multiple registers, split is only used by n,
+            // assume this is good enough and do not split again.
+            if( cfg==def.cfg0() && def.nOuts()==1 && !singleReg )
+                return;
+        }
+        makeSplit(def,kind,round,lrg).insertBefore(n, i);
+        // Skip split-of-split same block
+        if( skip && def instanceof SplitNode && cfg==def.cfg0() )
+            n.in(i).setDefOrdered(1,def.in(1));
+    }
+    void insertBefore(Node n, int i, String kind, byte round, LRG lrg) {
+        insertBefore(n,i,kind,round,lrg,true);
+    }
+
+    private Node makeSplit( Node def, String kind, byte round, LRG lrg ) {
+        Node split = def instanceof MachNode mach && mach.isClone()
+            ? mach.copy()
+            : _code._mach.split(kind,round,lrg);
+        _lrgs.put(split,lrg);
+        return split;
+    }
+    private SplitNode makeSplit( String kind, byte round, LRG lrg ) {
+        SplitNode split = _code._mach.split(kind,round,lrg);
+        _lrgs.put(split,lrg);
+        return split;
+    }
+
+
+    // -----------------------
+    // POST PASS: Remove empty spills that biased-coloring made
+    private void postColor() {
+        int maxSlot = -1;
+        for( CFGNode bb : _code._cfg ) { // For all ops
+            if( bb instanceof FunNode )
+                maxSlot = -1;
+            if( bb instanceof ReturnNode ret )
+                ret.fun()._maxSlot = (short)maxSlot;
+            for( int j=0; j<bb.nOuts(); j++ ) {
+                Node n = bb.out(j);
+                if( lrg(n)!=null ) {
+                    int slot = _code._mach.stackSlot(lrg(n)._reg);
+                    maxSlot = Math.max(maxSlot,slot);
+                }
+
+                if( !(n instanceof SplitNode lo) ) continue;
+                int defreg = lrg(n     )._reg;
+                int usereg = lrg(n.in(1))._reg;
+                // Attempt to bypass split
+                if( defreg != usereg && splitBypass(bb,j,n,defreg) )
+                    usereg = lrg(n.in(1))._reg;
+
+                // Split has same reg?  Useless!  Can remove it!
+                if( defreg == usereg ) {
+                    n.removeSplit();
+                    j--;
+                    continue;
+                }
+                // Split is kept; count against split score
+                _spills++;
+                _spillScaled += (1<<bb.loopDepth()*3);
+                assert _spillScaled >= 0;
+            }
+        }
+    }
+
+    private boolean splitBypass( CFGNode bb, int j, Node lo, int defreg ) {
+        // Attempt to bypass split
+        Node hi = lo.in(1);
+        while( true ) {
+            if( !(hi instanceof SplitNode && lo.cfg0() == hi.cfg0()) )
+                return false;
+            if( lrg(hi.in(1))._reg==defreg )
+                break;
+            hi = hi.in(1);
+        }
+        // Check no clobbers
+        for( int idx = j-1; bb.out(idx) != hi; idx++) {
+            Node n = bb.out(idx);
+            if( lrg(n)!=null && lrg(n)._reg == defreg )
+                return false;   // Clobbered
+        }
+        lo.setDefOrdered(1,hi.in(1));
+        return true;
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/RegMask.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/RegMask.java
new file mode 100644
index 0000000..a566187
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/RegMask.java
@@ -0,0 +1,142 @@
+package com.compilerprogramming.ezlang.compiler.codegen;
+
+import com.compilerprogramming.ezlang.compiler.Utils;
+import com.compilerprogramming.ezlang.compiler.SB;
+
+/** RegMask
+ *  A "register mask" - 1 bit set for each allowed register.  In addition
+ *  "stack slot" registers may be allowed, effectively making the set infinite.
+ * <p>
+ *  For smaller and simpler machines it suffices to make such masks an i64 or
+ *  i128 (64 or 128 bit integers), and this presentation is by far the better
+ *  way to go... if all register allocations can fit in this bit limitation.
+ *  The allocator will need bits for stack-based parameters and for splits
+ *  which cannot get a register.  For a 32-register machine like the X86, add 1
+ *  for flags - gives 33 registers.  Using a Java `long` has 64 bits, leaving
+ *  31 for spills and stack passing.  This is adequate for nearly all
+ *  allocations; only the largest allocations will run this out.  However, if
+ *  we move to a chip with 64 registers we'll immediately run out, and need at
+ *  least a 128 bit mask.  Since you cannot *return* a 128 bit value directly
+ *  in Java, Simple will pick up a `RegMask` class object.
+*/
+public class RegMask {
+
+    long _bits0, _bits1;
+
+    private static final RegMask EMPTY = new RegMask(0L);
+    public  static final RegMask FULL = new RegMask(-1L);
+
+    public RegMask(int bit) {
+        if( bit < 64 ) _bits0 = 1L<<bit;
+        else _bits1 = 1L<<(bit=64);
+    }
+    public RegMask(long bits ) { _bits0 = bits; }
+    public RegMask(long bits0, long bits1 ) { _bits0 = bits0; _bits1 = bits1; }
+    private RegMask() { _bits0 = _bits1 = 0; }
+
+    // AND, with copy-on-write if changing
+    RegMask and( RegMask mask ) {
+        if( mask==null ) return this;
+        long bits0 = _bits0 & mask._bits0;
+        long bits1 = _bits1 & mask._bits1;
+        if( bits0==_bits0 && bits1==_bits1 ) return this;
+        if( bits0==mask._bits0 && bits1==mask._bits1 ) return mask;
+        if( bits0==0 && bits1==0 ) return EMPTY;
+        // Update-in-place a mutable mask, or make a defensive copy
+        return null;
+    }
+    RegMask sub( RegMask mask ) {
+        if( mask==null ) return this;
+        long bits0 = _bits0 & ~mask._bits0;
+        long bits1 = _bits1 & ~mask._bits1;
+        if( bits0==_bits0 && bits1==_bits1 ) return this;
+        if( bits0==mask._bits0 && bits1==mask._bits1 ) return mask;
+        if( bits0==0 && bits1==0 ) return EMPTY;
+        // Update-in-place a mutable mask, or make a defensive copy
+        return null;
+    }
+
+    // Fails if bit is set, because this is immutable
+    public boolean clr( int reg ) {
+        if( reg <  64 ) return ((_bits0 >> reg)&1)==0;
+        if( reg < 128 ) return ((_bits1 >> reg)&1)==0;
+        return true;
+    }
+
+
+    public short firstReg() {
+        return (short)(_bits0 != 0
+                       ? Long.numberOfTrailingZeros(_bits0)
+                       : Long.numberOfTrailingZeros(_bits1)+64);
+    }
+    public short nextReg(short reg) {
+        if( reg<64 ) {
+            long bits0 = _bits0 >> (reg+1);
+            if( bits0!=0 )
+                return (short)(reg+1+Long.numberOfTrailingZeros(bits0));
+            reg=64;
+        }
+        long bits1 = _bits1 >> (reg-64+1);
+        if( bits1!=0 )
+            return (short)(reg+1+Long.numberOfTrailingZeros(bits1));
+        return -1;
+    }
+
+    boolean isEmpty() { return _bits0==0 && _bits1==0; }
+
+    public boolean test( int reg ) {
+        return ((reg<64 ? (_bits0 >> reg) : (_bits1 >> (reg-64))) & 1)  != 0;
+    }
+
+    // checks if the 2 masks have at least 1 bit in common
+    public boolean overlap( RegMask mask ) { return (_bits0 & mask._bits0)!=0 || (_bits1 & mask._bits1)!=0; }
+
+    // Defensive writable copy
+    public RegMaskRW copy() { return new RegMaskRW( _bits0, _bits1 ); }
+
+    // Has exactly 1 bit set
+    public boolean size1() {
+        return ((_bits0 & -_bits0)==_bits0 && _bits1==0) ||
+               ((_bits1 & -_bits1)==_bits1 && _bits0==0);
+    }
+
+    // Cardinality
+    public short size() { return (short)(Long.bitCount(_bits0)+Long.bitCount(_bits1)); }
+
+    @Override public String toString() { return toString(new SB()).toString(); }
+    public SB toString(SB sb) {
+        Machine mach = CodeGen.CODE._mach;
+        if( _bits0==0 && _bits1==0 ) return sb.p("[]");
+        sb.p("[");
+        for( int i=0; i<64; i++ )
+            if( ((_bits0 >> i)&1) != 0 )
+                sb.p(mach.reg(i)).p(",");
+        for( int i=0; i<64; i++ )
+            if( ((_bits1 >> i)&1) != 0 )
+                sb.p(mach.reg(i+64)).p(",");
+        return sb.unchar().p("]");
+    }
+}
+
+// Mutable regmask(writable)
+class RegMaskRW extends RegMask {
+    public RegMaskRW(long x, long y) { super(x,y);  }
+    // clears bit at position r. Returns true if the mask is still not empty.
+    public boolean clr(int r) {
+        if( r < 64 ) _bits0 &= ~(1L<<(r   ));
+        else         _bits1 &= ~(1L<<(r-64));
+        return _bits0!=0 || _bits1!=0;
+    }
+    @Override RegMaskRW and( RegMask mask ) {
+        if( mask==null ) return this;
+        _bits0 &= mask._bits0;
+        _bits1 &= mask._bits1;
+        return this;
+    }
+    @Override RegMaskRW sub( RegMask mask ) {
+        if( mask==null ) return this;
+        _bits0 &= ~mask._bits0;
+        _bits1 &= ~mask._bits1;
+        return this;
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/AddFNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/AddFNode.java
new file mode 100644
index 0000000..d8ecc97
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/AddFNode.java
@@ -0,0 +1,44 @@
+package com.compilerprogramming.ezlang.compiler.nodes;
+
+import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFloat;
+
+import java.util.BitSet;
+
+public class AddFNode extends Node {
+    public AddFNode(Node lhs, Node rhs) { super(null, lhs, rhs); }
+
+    @Override public String label() { return "AddF"; }
+
+    @Override public String glabel() { return "+"; }
+
+    @Override
+    public StringBuilder _print1(StringBuilder sb, BitSet visited) {
+        in(1)._print0(sb.append("("), visited);
+        in(2)._print0(sb.append("+"), visited);
+        return sb.append(")");
+    }
+
+    @Override
+    public SONType compute() {
+        if (in(1)._type instanceof SONTypeFloat i0 &&
+            in(2)._type instanceof SONTypeFloat i1) {
+            if (i0.isConstant() && i1.isConstant())
+                return SONTypeFloat.constant(i0.value()+i1.value());
+        }
+        return in(1)._type.meet(in(2)._type);
+    }
+
+    @Override
+    public Node idealize() {
+        Node lhs = in(1);
+        SONType t2 = in(2)._type;
+
+        // Add of 0.
+        if ( t2.isConstant() && t2 instanceof SONTypeFloat i && i.value()==0 )
+            return lhs;
+
+        return null;
+    }
+    @Override Node copy(Node lhs, Node rhs) { return new AddFNode(lhs,rhs); }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/AddNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/AddNode.java
new file mode 100644
index 0000000..0098b61
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/AddNode.java
@@ -0,0 +1,200 @@
+package com.compilerprogramming.ezlang.compiler.nodes;
+
+import com.compilerprogramming.ezlang.compiler.Compiler;
+import com.compilerprogramming.ezlang.compiler.sontypes.*;
+
+import java.util.BitSet;
+
+import static com.compilerprogramming.ezlang.compiler.Compiler.con;
+
+public class AddNode extends Node {
+    public AddNode(Node lhs, Node rhs) { super(null, lhs, rhs); }
+
+    @Override public String label() { return "Add"; }
+
+    @Override public String glabel() { return "+"; }
+
+    @Override
+    public StringBuilder _print1(StringBuilder sb, BitSet visited) {
+        in(1)._print0(sb.append("("), visited);
+        in(2)._print0(sb.append("+"), visited);
+        return sb.append(")");
+    }
+
+
+    @Override
+    public SONType compute() {
+        SONType t1 = in(1)._type, t2 = in(2)._type;
+        if( t1.isHigh() || t2.isHigh() )
+            return SONTypeInteger.TOP;
+        if( t1 instanceof SONTypeInteger i1 &&
+            t2 instanceof SONTypeInteger i2 ) {
+            if (i1.isConstant() && i2.isConstant())
+                return SONTypeInteger.constant(i1.value()+i2.value());
+            // Fold ranges like {0-1} + {2-3} into {2-4}.
+            if( !overflow(i1._min,i2._min) &&
+                !overflow(i1._max,i2._max) )
+                return SONTypeInteger.make(i1._min+i2._min,i1._max+i2._max);
+        }
+        return SONTypeInteger.BOT;
+    }
+
+    static boolean overflow( long x, long y ) {
+        if( (x ^    y ) < 0 ) return false; // unequal signs, never overflow
+        return (x ^ (x + y)) < 0; // sum has unequal signs, so overflow
+    }
+    @Override
+    public Node idealize () {
+        Node lhs = in(1);
+        Node rhs = in(2);
+        if( rhs instanceof AddNode add && add.err()!=null )
+            return null;
+        SONType t2 = rhs._type;
+
+        // Add of 0.  We do not check for (0+x) because this will already
+        // canonicalize to (x+0)
+        if( t2 == SONTypeInteger.ZERO )
+            return lhs;
+
+        // Add of same to a multiply by 2
+        if( lhs==rhs )
+            return new ShlNode(lhs,con(1));
+
+        // Goal: a left-spine set of adds, with constants on the rhs (which then fold).
+
+        // Move non-adds to RHS
+        if( !(lhs instanceof AddNode) && rhs instanceof AddNode )
+            return swap12();
+
+        // x+(-y) becomes x-y
+        if( rhs instanceof MinusNode minus )
+            return new SubNode(lhs,minus.in(1));
+
+        // Now we might see (add add non) or (add non non) or (add add add) but never (add non add)
+
+        // Do we have  x + (y + z) ?
+        // Swap to    (x + y) + z
+        // Rotate (add add add) to remove the add on RHS
+        if( rhs instanceof AddNode add )
+            return new AddNode(new AddNode(lhs,add.in(1)).peephole(), add.in(2));
+
+        // Now we might see (add add non) or (add non non) but never (add non add) nor (add add add)
+        if( !(lhs instanceof AddNode) )
+            // Rotate; look for (add (phi cons) con/(phi cons))
+            return spine_cmp(lhs,rhs,this) ? swap12() : phiCon(this,true);
+
+        // Now we only see (add add non)
+
+        // Dead data cycle; comes about from dead infinite loops.  Do nothing,
+        // the loop will peep as dead after a bit.
+        if( lhs.in(1) == lhs )
+            return null;
+
+        // Do we have (x + con1) + con2?
+        // Replace with (x + (con1+con2) which then fold the constants
+        // lhs.in(2) is con1 here
+        // If lhs.in(2) is not a constant, we add ourselves as a dependency
+        // because if it later became a constant then we could make this
+        // transformation.
+        if( lhs.in(2).addDep(this)._type.isConstant() && rhs._type.isConstant() )
+            return new AddNode(lhs.in(1),new AddNode(lhs.in(2),rhs).peephole());
+
+
+        // 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...)
+        Node phicon = phiCon(this,true);
+        if( phicon!=null ) return phicon;
+
+        // Now we sort along the spine via rotates, to gather similar things together.
+
+        // Do we rotate (x + y) + z
+        // into         (x + z) + y ?
+        if( spine_cmp(lhs.in(2),rhs,this) )
+            return new AddNode(new AddNode(lhs.in(1),rhs).peephole(),lhs.in(2));
+
+        return null;
+    }
+
+    // Rotation is only valid for associative ops, e.g. Add, Mul, And, Or, Xor.
+    // Do we have ((phi cons)|(x + (phi cons)) + con|(phi cons)) ?
+    // Push constant up through the phi: x + (phi con0+con0 con1+con1...)
+    static Node phiCon(Node op, boolean rotate) {
+        Node lhs = op.in(1);
+        Node rhs = op.in(2);
+        if( rhs._type== SONTypeInteger.TOP ) return null;
+        // LHS is either a Phi of constants, or another op with Phi of constants
+        PhiNode lphi = pcon(lhs,op);
+        if( rotate && lphi==null && lhs.nIns() > 2 ) {
+            // Only valid to rotate constants if both are same associative ops
+            if( lhs.getClass() != op.getClass() ) return null;
+            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 )
+            return null;
+
+        // If both are Phis, must be same Region
+        if( rhs instanceof PhiNode && lphi.in(0) != rhs.in(0) )
+            return null;
+
+        // Note that this is the exact reverse of Phi pulling a common op down
+        // to reduce total op-count.  We don't get in an endless push-up
+        // push-down peephole cycle because the constants all fold first.
+        Node[] ns = new Node[lphi.nIns()];
+        ns[0] = lphi.in(0);
+        // Push constant up through the phi: x + (phi con0+con0 con1+con1...)
+        for( int i=1; i<ns.length; i++ )
+            ns[i] = op.copy(lphi.in(i), rhs instanceof PhiNode ? rhs.in(i) : rhs).peephole();
+        String label = lphi._label + (rhs instanceof PhiNode rphi ? rphi._label : "");
+        Node phi = new PhiNode(label,lphi._declaredType,ns).peephole();
+        // Rotate needs another op, otherwise just the phi
+        return lhs==lphi ? phi : op.copy(lhs.in(1),phi);
+    }
+
+    /**
+     * Tests if the op is a phi and has all constant inputs.
+     * If not, returns null.
+     * If op is a phi, but its inputs are not all constants, then dep is added as
+     * a dependency to the phi's non-const input, because if later the input turn into a constant
+     * dep can make progress.
+     */
+    static PhiNode pcon(Node op, Node dep) {
+        return op instanceof PhiNode phi && phi.allCons(dep) ? phi : null;
+    }
+
+    // Compare two off-spine nodes and decide what order they should be in.
+    // Do we rotate ((x + hi) + lo) into ((x + lo) + hi) ?
+    // Generally constants always go right, then Phi-of-constants, then muls, then others.
+    // Ties with in a category sort by node ID.
+    // TRUE if swapping hi and lo.
+    static boolean spine_cmp( Node hi, Node lo, Node dep ) {
+        if( lo._type.isConstant() ) return false;
+        if( hi._type.isConstant() ) return true ;
+
+        if( lo instanceof PhiNode lphi && lphi.region()._type== SONType.XCONTROL ) return false;
+        if( hi instanceof PhiNode hphi && hphi.region()._type== SONType.XCONTROL ) return false;
+
+        if( lo instanceof PhiNode && lo.allCons(dep) ) return false;
+        if( hi instanceof PhiNode && hi.allCons(dep) ) return true ;
+
+        if( lo instanceof PhiNode && !(hi instanceof PhiNode) ) return true;
+        if( hi instanceof PhiNode && !(lo instanceof PhiNode) ) return false;
+
+        // Same category of "others"
+        return lo._nid > hi._nid;
+    }
+
+    @Override Node copy(Node lhs, Node rhs) { return new AddNode(lhs,rhs); }
+    @Override Node copyF() { return new AddFNode(null,null); }
+// FIXME Dibyendu
+//    @Override public Parser.ParseException err() {
+//        if( in(1)._type.isHigh() || in(2)._type.isHigh() ) return null;
+//        if( !(in(1)._type instanceof SONTypeInteger) ) return Parser.error("Cannot '"+label()+"' " + in(1)._type,null);
+//        if( !(in(2)._type instanceof SONTypeInteger) ) return Parser.error("Cannot '"+label()+"' " + in(2)._type,null);
+//        return null;
+//    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/AndNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/AndNode.java
new file mode 100644
index 0000000..f36997f
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/AndNode.java
@@ -0,0 +1,58 @@
+package com.compilerprogramming.ezlang.compiler.nodes;
+
+import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeInteger;
+
+public class AndNode extends LogicalNode {
+    public AndNode(Node lhs, Node rhs) { super(lhs, rhs); }
+
+    @Override public String label() { return "And"; }
+    @Override public String op() { return "&"; }
+
+    @Override public String glabel() { return "&"; }
+
+    @Override
+    public SONType compute() {
+        SONType t1 = in(1)._type, t2 = in(2)._type;
+        if( t1.isHigh() || t2.isHigh() )
+            return SONTypeInteger.TOP;
+        if( t1 instanceof SONTypeInteger i0 &&
+            t2 instanceof SONTypeInteger i1 ) {
+            if( i0.isConstant() && i1.isConstant() )
+                return SONTypeInteger.constant(i0.value()&i1.value());
+            // Sharpen allowed bits if either value is narrowed
+            long mask = i0.mask() & i1.mask();
+            return mask < 0 ? SONTypeInteger.BOT : SONTypeInteger.make(0,mask);
+        }
+        return SONTypeInteger.BOT;
+    }
+
+    @Override
+    public Node idealize() {
+        Node lhs = in(1);
+        Node rhs = in(2);
+        SONType t1 = lhs._type;
+        SONType t2 = rhs._type;
+        if( !(t1 instanceof SONTypeInteger && t2 instanceof SONTypeInteger t2i) )
+            return null; // Malformed, e.g. (17 & 3.14)
+
+        // And of -1.  We do not check for (-1&x) because this will already
+        // canonicalize to (x&-1).  We do not check for zero, because
+        // the compute() call will return a zero already.
+        if( t2i.isConstant() && t2i.value()==-1 )
+            return lhs;
+
+        // Move constants to RHS: con*arg becomes arg*con
+        if( t1.isConstant() && !t2.isConstant() )
+            return swap12();
+
+        // 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...)
+        Node phicon = AddNode.phiCon(this,true);
+        if( phicon!=null ) return phicon;
+
+        return null;
+    }
+    @Override Node copy(Node lhs, Node rhs) { return new AndNode(lhs,rhs); }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/BoolNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/BoolNode.java
new file mode 100644
index 0000000..9c9b744
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/BoolNode.java
@@ -0,0 +1,135 @@
+package com.compilerprogramming.ezlang.compiler.nodes;
+
+import com.compilerprogramming.ezlang.compiler.Compiler;
+import com.compilerprogramming.ezlang.compiler.Utils;
+import com.compilerprogramming.ezlang.compiler.sontypes.*;
+import java.util.BitSet;
+import static com.compilerprogramming.ezlang.compiler.sontypes.SONTypeInteger.*;
+
+abstract public class BoolNode extends Node {
+
+    public BoolNode(Node lhs, Node rhs) {
+        super(null, lhs, rhs);
+    }
+
+    abstract public String op(); // String opcode name
+
+    @Override
+    public String label() { return getClass().getSimpleName(); }
+
+    @Override
+    public String glabel() { return op(); }
+
+    @Override
+    public StringBuilder _print1(StringBuilder sb, BitSet visited) {
+        in(1)._print0(sb.append("("), visited);
+        in(2)._print0(sb.append(op()), visited);
+        return sb.append(")");
+    }
+
+    @Override
+    public SONTypeInteger compute() {
+        SONType t1 = in(1)._type;
+        SONType t2 = in(2)._type;
+        // Exactly equals?
+        if( t1.isHigh() || t2.isHigh() )
+            return BOOL.dual();
+        if( in(1)==in(2) )
+            // LT fails, both EQ and LE succeed
+            return this instanceof LT ? FALSE : TRUE;
+        if( t1 instanceof SONTypeInteger i1 &&
+            t2 instanceof SONTypeInteger i2 )
+            return doOp(i1,i2);
+        if( t1 instanceof SONTypeFloat f1 &&
+            t2 instanceof SONTypeFloat f2 &&
+            f1.isConstant() && f2.isConstant() )
+            return doOp(f1.value(), f2.value()) ? TRUE : FALSE;
+        return BOOL;
+    }
+
+    SONTypeInteger doOp(SONTypeInteger t1, SONTypeInteger t2) { throw Utils.TODO(); }
+    boolean doOp(double lhs, double rhs) { throw Utils.TODO(); }
+    Node copyF(Node lhs, Node rhs) { return null; }
+    public boolean isFloat() { return false; }
+
+    @Override
+    public Node idealize() {
+        // Compare of same
+        if( in(1)==in(2) )
+            return this instanceof LT ? Compiler.ZERO : new ConstantNode(TRUE);
+
+        // Equals pushes constant to the right; 5==X becomes X==5.
+        if( this instanceof EQ ) {
+            if( !(in(2) instanceof ConstantNode) ) {
+                // con==noncon becomes noncon==con
+                if( in(1) instanceof ConstantNode || in(1)._nid > in(2)._nid )
+                // Equals sorts by NID otherwise: non.high == non.low becomes non.low == non.high
+                    return in(1)._type instanceof SONTypeFloat ? new EQF(in(2),in(1)) : new EQ(in(2),in(1));
+            }
+            // Equals X==0 becomes a !X
+            if( (in(2)._type == ZERO || in(2)._type == SONType.NIL) )
+                return new NotNode(in(1));
+        }
+
+        // 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...)
+        Node phicon = AddNode.phiCon(this,this instanceof EQ);
+        if( phicon!=null ) return phicon;
+
+        return null;
+    }
+
+    public static class EQ extends BoolNode {
+        public EQ(Node lhs, Node rhs) { super(lhs,rhs); }
+        public String op() { return "=="; }
+        SONTypeInteger doOp(SONTypeInteger i1, SONTypeInteger i2) {
+            if( i1==i2 && i1.isConstant() ) return TRUE;
+            if( i1._max < i2._min || i1._min > i2._max ) return FALSE;
+            return BOOL;
+        }
+        Node copy(Node lhs, Node rhs) { return new EQ(lhs,rhs); }
+        Node copyF() { return new EQF(null,null); }
+    }
+    public static class LT extends BoolNode {
+        public LT(Node lhs, Node rhs) { super(lhs,rhs); }
+        public String op() { return "<" ; }
+        public String glabel() { return "&lt;"; }
+        SONTypeInteger doOp(SONTypeInteger i1, SONTypeInteger i2) {
+            if( i1._max <  i2._min ) return TRUE;
+            if( i1._min >= i2._max ) return FALSE;
+            return BOOL;
+        }
+        Node copy(Node lhs, Node rhs) { return new LT(lhs,rhs); }
+        Node copyF() { return new LTF(null,null); }
+    }
+    public static class LE extends BoolNode {
+        public LE(Node lhs, Node rhs) { super(lhs,rhs); }
+        public String op() { return "<="; }
+        public String glabel() { return "&lt;="; }
+        SONTypeInteger doOp(SONTypeInteger i1, SONTypeInteger i2) {
+            if( i1._max <= i2._min ) return TRUE;
+            if( i1._min >  i2._max ) return FALSE;
+            return BOOL;
+        }
+        Node copy(Node lhs, Node rhs) { return new LE(lhs,rhs); }
+        Node copyF() { return new LEF(null,null); }
+    }
+
+    public static class EQF extends EQ { public EQF(Node lhs, Node rhs) { super(lhs,rhs); } boolean doOp(double lhs, double rhs) { return lhs == rhs; } public boolean isFloat() { return true; } }
+    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 boolean isFloat() { return true; } }
+    public static class LEF extends LE { public LEF(Node lhs, Node rhs) { super(lhs,rhs); } boolean doOp(double lhs, double rhs) { return lhs <= rhs; } public boolean isFloat() { return true; } }
+
+    // Unsigned less that, for range checks.  Not directly user writable.
+    public static class ULT extends BoolNode {
+        public ULT(Node lhs, Node rhs) { super(lhs,rhs); }
+        public String op() { return "u<" ; }
+        public String glabel() { return "u&lt;"; }
+        SONTypeInteger doOp(SONTypeInteger i1, SONTypeInteger 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/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CFGNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CFGNode.java
new file mode 100644
index 0000000..3876fd2
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CFGNode.java
@@ -0,0 +1,181 @@
+package com.compilerprogramming.ezlang.compiler.nodes;
+
+import com.compilerprogramming.ezlang.compiler.Compiler;
+import com.compilerprogramming.ezlang.compiler.sontypes.*;
+import java.util.BitSet;
+
+/** Control Flow Graph Nodes
+ * <p>
+ *  CFG nodes have a immediate dominator depth (idepth) and a loop nesting
+ *  depth(loop_depth).
+ * <p>
+ *  idepth is computed lazily upon first request, and is valid even in the
+ *  Parser, and is used by peepholes during parsing and afterward.
+ * <p>
+ *  loop_depth is computed after optimization as part of scheduling.
+ *
+ *  Start - Block head; has constants and trailing Funs
+ *  Region - Block head; has Phis.  Includes Fun and Parms.
+ *  CallEnd - Block head; followed by CProj and Proj; also linked RPC Parms
+ *  CProj - Block head
+ *
+ *  If - Block tail; followed by CProj same block
+ *  Call - Block tail; followed by CallEnd and linked Funs
+ *  New/intrinsic - If followed by CProj: block tail else mid-block; followed by Proj
+ *
+ */
+public abstract class CFGNode extends Node {
+
+    public CFGNode(Node...   nodes) { super(nodes); }
+    public CFGNode(CFGNode cfg) {
+        super(cfg);
+        if( cfg != null ) {
+            _idepth = cfg._idepth;
+            _ltree = cfg._ltree;
+            _pre = cfg._pre;
+        }
+    }
+
+    public CFGNode cfg(int idx) { return (CFGNode)in(idx); }
+
+    // Block head is Start, Region, CProj, but not e.g. If, Return, Stop
+    public boolean blockHead() { return false; }
+
+    // Get the one control following; error to call with more than one e.g. an
+    // IfNode or other multi-way branch.
+    public CFGNode uctrl() {
+        CFGNode c = null;
+        for( Node n : _outputs )
+            if( n instanceof CFGNode cfg )
+                {  assert c==null;  c = cfg; }
+        return c;
+    }
+
+
+    // ------------------------------------------------------------------------
+    /**
+     * Immediate dominator tree depth, used to approximate a real IDOM during
+     * parsing where we do not have the whole program, and also peepholes
+     * change the CFG incrementally.
+     * <p>
+     * See {@link <a href="https://en.wikipedia.org/wiki/Dominator_(graph_theory)">...</a>}
+     */
+    public int _idepth;
+    public int idepth() { return _idepth==0 ? (_idepth=idom().idepth()+1) : _idepth; }
+
+    // Return the immediate dominator of this Node and compute dom tree depth.
+    public CFGNode idom(Node dep) { return cfg(0); }
+    public final CFGNode idom() { return idom(null); }
+
+    // Return the LCA of two idoms
+    public CFGNode _idom(CFGNode rhs, Node dep) {
+        if( rhs==null ) return this;
+        CFGNode lhs = this;
+        while( lhs != rhs ) {
+            var comp = lhs.idepth() - rhs.idepth();
+            if( comp >= 0 ) lhs = ((CFGNode)lhs.addDep(dep)).idom();
+            if( comp <= 0 ) rhs = ((CFGNode)rhs.addDep(dep)).idom();
+        }
+        return lhs;
+    }
+
+    // Anti-dependence field support
+    public int _anti;           // Per-CFG field to help find anti-deps
+
+    // Find nearest enclosing FunNode
+    public FunNode fun() {
+        CFGNode cfg = this;
+        while( !(cfg instanceof FunNode fun) )
+            cfg = cfg.idom();
+        return fun;
+    }
+
+    // ------------------------------------------------------------------------
+    // Loop nesting
+    public LoopNode loop() { return _ltree._head; }
+    public int loopDepth() { return _ltree==null ? 0 : _ltree.depth(); }
+
+    LoopTree _ltree;
+    public int _pre;            // Pre-order numbers for loop tree finding
+    private static class LoopTree {
+        LoopTree _par;
+        final LoopNode _head;
+        int _depth;
+        LoopTree(LoopNode head) { _head = head; }
+        @Override public String toString() { return "LOOP"+_head._nid; }
+        int depth() {
+            return _depth==0 ? (_par==null ? 0 : (_depth = _par.depth()+1)) : _depth;
+        }
+    }
+
+    // ------------------------------------------------------------------------
+    // Tag all CFG Nodes with their containing LoopNode; LoopNodes themselves
+    // also refer to *their* containing LoopNode, as well as have their depth.
+    // Start is a LoopNode which contains all at depth 1.
+    public void buildLoopTree(StopNode stop) {
+        _ltree = stop._ltree = Compiler.XCTRL._ltree = new LoopTree((StartNode)this);
+        _bltWalk(2,null,stop, new BitSet());
+    }
+    int _bltWalk( int pre, FunNode fun, StopNode stop, BitSet post ) {
+        // Pre-walked?
+        if( _pre!=0 ) return pre;
+        _pre = pre++;
+        // Pre-walk
+        for( Node use : _outputs )
+            if( use instanceof CFGNode usecfg && !skip( usecfg ) )
+                pre = usecfg._bltWalk( pre, use instanceof FunNode fuse ? fuse : fun, stop, post );
+
+        // Post-order work: find innermost loop
+        LoopTree inner = null, ltree;
+        for( Node use : _outputs ) {
+            if( !(use instanceof CFGNode usecfg) ) continue;
+            if( skip(usecfg) ) continue;
+            if( usecfg._type == SONType.XCONTROL ||       // Do not walk dead control
+                usecfg._type == SONTypeTuple.IF_NEITHER ) // Nor dead IFs
+                continue;
+            // Child visited but not post-visited?
+            if( !post.get(usecfg._nid) ) {
+                // Must be a backedge to a LoopNode then
+                ltree = usecfg._ltree = new LoopTree((LoopNode)usecfg);
+            } else {
+                // Take child's loop choice, which must exist
+                ltree = usecfg._ltree;
+                // If falling into a loop, use the target loop's parent instead
+                if( ltree._head == usecfg ) {
+                    if( ltree._par == null )
+                        // This loop never had an If test choose to take its
+                        // exit, i.e. it is a no-exit infinite loop.
+                        ltree._par = ltree._head.forceExit(fun,stop)._ltree;
+                    ltree = ltree._par;
+                }
+            }
+            // Sort inner loops.  The decision point is some branch far removed
+            // from either loop head OR either backedge so requires pre-order
+            // numbers to figure out innermost.
+            if( inner == null ) { inner = ltree; continue; }
+            if( inner == ltree ) continue; // No change
+            LoopTree outer = ltree._head._pre > inner._head._pre ? inner : ltree;
+            inner =          ltree._head._pre > inner._head._pre ? ltree : inner;
+            inner._par = outer;
+        }
+        // Set selected loop
+        if( inner!=null )
+            _ltree = inner;
+        // Tag as post-walked
+        post.set(_nid);
+        return pre;
+    }
+
+    private boolean skip(CFGNode usecfg) {
+        // Only walk control users that are alive.
+        // Do not walk from a Call to linked Fun's.
+        return usecfg instanceof XCtrlNode ||
+                (this instanceof CallNode && usecfg instanceof FunNode) ||
+                (this instanceof ReturnNode && usecfg instanceof CallEndNode);
+    }
+
+
+    public String label( CFGNode target ) {
+        return (target instanceof LoopNode ? "LOOP" : "L")+target._nid;
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CProjNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CProjNode.java
new file mode 100644
index 0000000..9e2f384
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CProjNode.java
@@ -0,0 +1,66 @@
+package com.compilerprogramming.ezlang.compiler.nodes;
+
+import com.compilerprogramming.ezlang.compiler.Compiler;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeTuple;
+import java.util.BitSet;
+
+public class CProjNode extends CFGNode {
+
+    // Which slice of the incoming multipart value
+    public int _idx;
+
+    // Debugging label
+    public String _label;
+
+    public CProjNode(Node ctrl, int idx, String label) {
+        super(ctrl);
+        _idx = idx;
+        _label = label;
+    }
+    public CProjNode(CProjNode c) { super(c); _idx = c._idx; _label = c._label; }
+
+    @Override public String label() { return _label; }
+
+    @Override public StringBuilder _print1(StringBuilder sb, BitSet visited) { return sb.append(_label); }
+
+    @Override public boolean blockHead() { return true; }
+
+    public CFGNode ctrl() { return cfg(0); }
+
+    @Override
+    public SONType compute() {
+        SONType t = ctrl()._type;
+        return t instanceof SONTypeTuple tt ? tt._types[_idx] : SONType.BOTTOM;
+    }
+
+    @Override
+    public Node idealize() {
+        if( ctrl()._type instanceof SONTypeTuple tt ) {
+            if( tt._types[_idx]== SONType.XCONTROL )
+                return Compiler.XCTRL; // We are dead
+            if( ctrl() instanceof IfNode && tt._types[1-_idx]== SONType.XCONTROL ) // Only true for IfNodes
+                return ctrl().in(0);               // We become our input control
+        }
+
+        // Flip a negating if-test, to remove the not
+        if( ctrl() instanceof IfNode iff && iff.pred().addDep(this) instanceof NotNode not )
+            return new CProjNode(new IfNode(iff.ctrl(),not.in(1)).peephole(),1-_idx,_idx==0 ? "False" : "True");
+
+        // Copy of some other input
+        return ((MultiNode)ctrl()).pcopy(_idx);
+    }
+
+    // Only called during basic-block layout, inverts a T/F CProj
+    public void invert() {
+        _label = _idx == 0 ? "False" : "True";
+        _idx = 1-_idx;
+    }
+
+    @Override
+    boolean eq( Node n ) { return _idx == ((CProjNode)n)._idx; }
+
+    @Override
+    int hash() { return _idx; }
+
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CallEndNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CallEndNode.java
new file mode 100644
index 0000000..c705f53
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CallEndNode.java
@@ -0,0 +1,100 @@
+package com.compilerprogramming.ezlang.compiler.nodes;
+
+import com.compilerprogramming.ezlang.compiler.Compiler;
+import com.compilerprogramming.ezlang.compiler.codegen.CodeGen;
+import com.compilerprogramming.ezlang.compiler.sontypes.*;
+import java.util.BitSet;
+
+/**
+ *  CallEnd
+ */
+public class CallEndNode extends CFGNode implements MultiNode {
+
+    // When set true, this Call/CallEnd/Fun/Return is being trivially inlined
+    private boolean _folding;
+    public final SONTypeRPC _rpc;
+
+    public CallEndNode(CallNode call) { super(new Node[]{call}); _rpc = SONTypeRPC.constant(_nid); }
+    public CallEndNode(CallEndNode cend) { super(cend); _rpc = cend._rpc; }
+
+    @Override public String label() { return "CallEnd"; }
+    @Override public boolean blockHead() { return true; }
+
+    public CallNode call() { return (CallNode)in(0); }
+
+    @Override
+    public StringBuilder _print1(StringBuilder sb, BitSet visited) {
+        sb.append("cend( ");
+        sb.append( in(0) instanceof CallNode ? "Call, " : "----, ");
+        for( int i=1; i<nIns()-1; i++ )
+            in(i)._print0(sb,visited).append(",");
+        sb.setLength(sb.length()-1);
+        return sb.append(")");
+    }
+
+    @Override
+    public SONType compute() {
+        if( !(in(0) instanceof CallNode call) )
+            return SONTypeTuple.RET.dual();
+        SONType ret = SONType.BOTTOM;
+        SONTypeMem mem = SONTypeMem.BOT;
+        if( call.fptr().addDep(this)._type instanceof SONTypeFunPtr tfp ) {
+            ret = tfp.ret();
+            // Here, if I can figure out I've found *all* callers, then I can meet
+            // across the linked returns and join with the function return type.
+            if( tfp.isConstant() && nIns()>1 ) {
+                assert nIns()==2;     // Linked exactly once for a constant
+                ret = ((SONTypeTuple)in(1)._type).ret(); // Return type
+            }
+        }
+        return SONTypeTuple.make(call._type, SONTypeMem.BOT,ret);
+    }
+
+    @Override
+    public Node idealize() {
+
+        // Trivial inlining: call site calls a single function; single function
+        // is only called by this call site.
+        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
+                // Function is being called, and its not-null
+                fptr._type instanceof SONTypeFunPtr tfp && tfp.notNull() &&
+                // Arguments are correct
+                call.err()==null ) {
+                ReturnNode ret = (ReturnNode)in(1);
+                FunNode fun = ret.fun();
+                // Expecting Start, and the Call
+                if( fun.nIns()==3 ) {
+                    assert fun.in(1) instanceof StartNode && fun.in(2)==call;
+                    // Disallow self-recursive inlining (loop unrolling by another name)
+                    CFGNode idom = call;
+                    while( !(idom instanceof FunNode fun2) )
+                        idom = idom.idom();
+                    if( idom != fun ) {
+                        // Trivial inline: rewrite
+                        _folding = true;
+                        // Rewrite Fun so the normal RegionNode ideal collapses
+                        fun._folding = true;
+                        fun.setDef(1, Compiler.XCTRL); // No default/unknown StartNode caller
+                        fun.setDef(2,call.ctrl());  // Bypass the Call;
+                        fun.ret().setDef(3,null);   // Return is folding also
+                        CodeGen.CODE.addAll(fun._outputs);
+                        return this;
+                    }
+                } else {
+                    fun.addDep(this);
+                }
+            } else { // Function ptr has multiple users (so maybe multiple call sites)
+                fptr.addDep(this);
+            }
+        }
+
+        return null;
+    }
+
+    @Override public Node pcopy(int idx) {
+        return _folding ? in(1).in(idx) : null;
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CallNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CallNode.java
new file mode 100644
index 0000000..52b3bf1
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CallNode.java
@@ -0,0 +1,165 @@
+package com.compilerprogramming.ezlang.compiler.nodes;
+
+import com.compilerprogramming.ezlang.compiler.codegen.CodeGen;
+import com.compilerprogramming.ezlang.compiler.IterPeeps;
+import com.compilerprogramming.ezlang.compiler.Compiler;
+import com.compilerprogramming.ezlang.compiler.Utils;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFunPtr;
+import com.compilerprogramming.ezlang.exceptions.CompilerException;
+
+import java.util.BitSet;
+
+/**
+ *  Call
+ */
+public class CallNode extends CFGNode {
+
+    public CallNode(Node... nodes) { super(nodes); }
+    public CallNode(CallNode call) { super(call); }
+
+    @Override public String label() { return "Call"; }
+
+    @Override public StringBuilder _print1(StringBuilder sb, BitSet visited) {
+        String fname = name();
+        if( fname == null ) fptr()._print0(sb,visited);
+        else sb.append(fname);
+        sb.append("( ");
+        for( int i=2; i<nIns()-1; i++ )
+            in(i)._print0(sb,visited).append(",");
+        sb.setLength(sb.length()-1);
+        return sb.append(")");
+    }
+    public String name() {
+        if( fptr()._type instanceof SONTypeFunPtr tfp && tfp.isConstant() )
+            return CodeGen.CODE.link(tfp)._name;
+        return null;
+    }
+
+
+
+    Node ctrl() { return in(0); }
+    Node mem () { return in(1); }
+    // Args mapped 1-to-1 on inputs, so conceptually start at 2
+    public Node arg(int idx) { return in(idx); }
+    // Same arg accounting as TFPs, although the numbering starts at 2
+    public 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 SONTypeFunPtr tfp() { return (SONTypeFunPtr)fptr()._type; }
+    // Call is to an externally supplied code
+    public boolean external() { return false; }
+
+    // Find the Call End from the Call
+    public CallEndNode cend() {
+        // Always in use slot 0
+        if( nOuts()>0 && out(0) instanceof CallEndNode cend ) {
+            assert _cend()==cend;
+            return cend;
+        } else {
+            assert _cend()==null;
+            return null;
+        }
+    }
+    private CallEndNode _cend() {
+        CallEndNode cend=null;
+        for( Node n : _outputs )
+            if( n instanceof CallEndNode cend0 )
+                { assert cend == null; cend = cend0; }
+        return cend;
+    }
+
+    // Get the one control following; error to call with more than one e.g. an
+    // IfNode or other multi-way branch.
+    @Override public CFGNode uctrl() { return cend(); }
+
+
+    @Override
+    public SONType compute() {
+        return ctrl()._type;
+    }
+
+    @Override
+    public Node idealize() {
+        CallEndNode cend = cend();
+        if( cend==null ) return null; // Still building
+
+        // Link: call calls target function.  Linking makes the target FunNode
+        // point to this Call, and all his Parms point to the call arguments;
+        // also the CallEnd points to the Return.
+        Node progress = null;
+        if( fptr()._type instanceof SONTypeFunPtr tfp && tfp.nargs() == nargs() ) {
+            // If fidxs is negative, then infinite unknown functions
+            long fidxs = tfp.fidxs();
+            if( fidxs > 0 ) {
+                // Wipe out the return which matching in the linker table
+                // Walk the (63 max) bits and link
+                for( ; fidxs!=0; fidxs = SONTypeFunPtr.nextFIDX(fidxs) ) {
+                    int fidx = Long.numberOfTrailingZeros(fidxs);
+                    SONTypeFunPtr tfp0 = tfp.makeFrom(fidx);
+                    FunNode fun = CodeGen.CODE.link(tfp0);
+                    if( fun!=null && !fun._folding && !linked(fun) )
+                        progress = link(fun);
+                }
+            }
+        }
+
+        return progress;
+    }
+
+    // True if Fun is linked to this Call
+    boolean linked( FunNode fun ) {
+        for( Node n : fun._inputs )
+            if( n == this )
+                return true;
+        return false;
+    }
+
+
+    // Link so this calls fun
+    private Node link( FunNode fun ) {
+        assert !linked(fun);
+        fun.addDef(this);
+        for( Node use : fun._outputs )
+            if( use instanceof ParmNode parm )
+                parm.addDef(parm._idx==0 ? new ConstantNode(cend()._rpc).peephole() : arg(parm._idx));
+        // Call end points to function return
+        CodeGen.CODE.add(cend()).addDef(fun.ret());
+        return this;
+    }
+
+    // Unlink all linked functions
+    public void unlink_all() {
+        for( int i=0; i<_outputs._len; i++ )
+            if( out(i) instanceof FunNode fun ) {
+                assert linked(fun);
+                int idx = fun._inputs.find(this);
+                for( Node use : fun._outputs )
+                    if( use instanceof ParmNode parm )
+                        use.delDef(idx);
+                fun.delDef(idx);
+                cend().delDef(cend()._inputs.find(fun.ret()));
+                assert !linked(fun);
+                i--;
+            }
+    }
+
+    @Override
+    public CompilerException err() {
+        if( !(fptr()._type instanceof SONTypeFunPtr tfp) )
+            throw Utils.TODO();
+        if( !tfp.notNull() )
+            return Compiler.error( "Might be null calling "+tfp);
+        if( nargs() != tfp.nargs() )
+            return Compiler.error( "Expecting "+tfp.nargs()+" arguments, but found "+nargs());
+
+        // Check for args
+        for( int i=0; i<tfp.nargs(); i++ )
+            if( !arg(i+2)._type.isa(tfp.arg(i)) )
+                return Compiler.error( "Argument #"+i+" isa "+arg(i+2)._type+", but must be a "+tfp.arg(i));
+
+        return null;
+    }
+
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CalleeSaveNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CalleeSaveNode.java
new file mode 100644
index 0000000..a003903
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CalleeSaveNode.java
@@ -0,0 +1,26 @@
+package com.compilerprogramming.ezlang.compiler.nodes;
+
+import com.compilerprogramming.ezlang.compiler.Utils;
+import com.compilerprogramming.ezlang.compiler.codegen.Encoding;
+import com.compilerprogramming.ezlang.compiler.codegen.RegMask;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
+import java.util.BitSet;
+import java.io.ByteArrayOutputStream;
+
+public class CalleeSaveNode extends ProjNode implements MachNode {
+    final RegMask _mask;
+    public CalleeSaveNode(FunNode fun, int reg, String label) {
+        super(fun,reg,label);
+        fun._outputs.pop();
+        int i=0;
+        while( fun.out(i) instanceof PhiNode )  i++;
+        fun._outputs.insert(this,i);
+        _mask = new RegMask(reg);
+    }
+    @Override public RegMask regmap(int i) { return null; }
+    @Override public RegMask outregmap() { return _mask; }
+    @Override public void encoding( Encoding enc ) { }
+
+    @Override public SONType compute() { return SONType.BOTTOM; }
+    @Override public Node idealize() { return null; }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CastNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CastNode.java
new file mode 100644
index 0000000..5808cd8
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CastNode.java
@@ -0,0 +1,64 @@
+package com.compilerprogramming.ezlang.compiler.nodes;
+
+import com.compilerprogramming.ezlang.compiler.Utils;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
+import com.compilerprogramming.ezlang.exceptions.CompilerException;
+
+import java.util.BitSet;
+
+// Upcast (join) the input to a t.  Used after guard test to lift an input.
+// Can also be used to make a type-assertion if ctrl is null.
+public class CastNode extends Node {
+    public SONType _t;
+    public CastNode(SONType t, Node ctrl, Node in) {
+        super(ctrl, in);
+        _t = t;
+        setType(compute());
+    }
+
+    public CastNode(CastNode c) {
+        super(c.in(0), c.in(1)); // Call parent constructor
+        this._t = c._t;          // Copy the Type field
+        setType(compute());      // Ensure type is recomputed
+    }
+
+    @Override public String label() { return "("+_t.str()+")"; }
+
+    @Override
+    public String uniqueName() { return "Cast_" + _nid; }
+
+    @Override public boolean isPinned() { return true; }
+    @Override public boolean isConst() { return true; }
+
+    @Override
+    public StringBuilder _print1(StringBuilder sb, BitSet visited) {
+        return in(1)._print0(sb.append(label()), visited);
+    }
+
+    @Override
+    public SONType compute() {
+        return in(1)._type.join(_t);
+    }
+
+    @Override
+    public Node idealize() {
+        return in(1)._type.isa(_t) ? in(1) : null;
+    }
+
+    @Override
+    boolean eq(Node n) {
+        CastNode cast = (CastNode)n; // Contract
+        return _t==cast._t;
+    }
+
+    @Override
+    int hash() { return _t.hashCode(); }
+
+    @Override
+    public CompilerException err() {
+        // Has a condition to test, so OK
+        if( in(0) != null ) return null;
+        // No condition to test, so this must optimize away
+        throw Utils.TODO();
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ConstantNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ConstantNode.java
new file mode 100644
index 0000000..e858248
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ConstantNode.java
@@ -0,0 +1,69 @@
+package com.compilerprogramming.ezlang.compiler.nodes;
+
+import com.compilerprogramming.ezlang.compiler.codegen.CodeGen;
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFunPtr;
+import java.util.BitSet;
+
+/**
+ * A Constant node represents a constant value.  At present, the only constants
+ * that we allow are integer literals; therefore Constants contain an integer
+ * value.  As we add other types of constants, we will refactor how we represent
+ * Constants.
+ * <p>
+ * Constants have no semantic inputs. However, we set Start as an input to
+ * Constants to enable a forward graph walk.  This edge carries no semantic
+ * meaning, and it is present <em>solely</em> to allow visitation.
+ * <p>
+ * The Constant's value is the value stored in it.
+ */
+
+public class ConstantNode extends Node {
+    public final SONType _con;
+    public ConstantNode( SONType type ) {
+        super(new Node[]{CodeGen.CODE._start});
+        _con = _type = type;
+    }
+    public ConstantNode( Node con, SONType t ) { super(con);  _con = t;  }
+    public ConstantNode( ConstantNode con ) { super(con);  _con = con._type;  }
+
+    public static Node make( SONType type ) {
+        if( type== SONType. CONTROL ) return new CtrlNode();
+        if( type== SONType.XCONTROL ) return new XCtrlNode();
+        return new ConstantNode(type);
+    }
+
+    @Override public String  label() { return "#"+_con; }
+    @Override public String glabel() { return _con.gprint(new SB().p("#")).toString(); }
+
+    @Override
+    public String uniqueName() { return "Con_" + _nid; }
+
+    @Override
+    public StringBuilder _print1(StringBuilder sb, BitSet visited) {
+        if( _con instanceof SONTypeFunPtr tfp && tfp.isConstant() ) {
+            FunNode fun = CodeGen.CODE.link(tfp);
+            if( fun._name != null )
+                return sb.append("{ ").append(fun._name).append("}");
+        }
+        return sb.append(_con.print(new SB()));
+    }
+
+    @Override public boolean isConst() { return true; }
+
+    @Override
+    public SONType compute() { return _con; }
+
+    @Override
+    public Node idealize() { return null; }
+
+    @Override
+    boolean eq(Node n) {
+        ConstantNode con = (ConstantNode)n; // Contract
+        return _con==con._con;
+    }
+
+    @Override
+    int hash() { return _con.hashCode(); }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CtrlNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CtrlNode.java
new file mode 100644
index 0000000..6450cdd
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CtrlNode.java
@@ -0,0 +1,14 @@
+package com.compilerprogramming.ezlang.compiler.nodes;
+
+import com.compilerprogramming.ezlang.compiler.codegen.CodeGen;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
+import java.util.BitSet;
+
+public class CtrlNode extends CFGNode {
+    public CtrlNode() { super(CodeGen.CODE._start); }
+    @Override public String label() { return "Ctrl"; }
+    @Override public StringBuilder _print1(StringBuilder sb, BitSet visited) { return sb.append("Cctrl"); }
+    @Override public boolean isConst() { return true; }
+    @Override  public SONType compute() { return SONType.CONTROL; }
+    @Override public Node idealize() { return null; }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/DivFNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/DivFNode.java
new file mode 100644
index 0000000..0813fa8
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/DivFNode.java
@@ -0,0 +1,41 @@
+package com.compilerprogramming.ezlang.compiler.nodes;
+
+import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFloat;
+
+import java.util.BitSet;
+
+public class DivFNode extends Node {
+    public DivFNode(Node lhs, Node rhs) { super(null, lhs, rhs); }
+
+    @Override public String label() { return "DivF"; }
+
+    @Override public String glabel() { return "/"; }
+
+    @Override
+    public StringBuilder _print1(StringBuilder sb, BitSet visited) {
+        in(1)._print0(sb.append("("), visited);
+        in(2)._print0(sb.append("/"), visited);
+        return sb.append(")");
+    }
+
+    @Override
+    public SONType compute() {
+        if (in(1)._type instanceof SONTypeFloat i0 &&
+            in(2)._type instanceof SONTypeFloat i1) {
+            if (i0.isConstant() && i1.isConstant())
+                return SONTypeFloat.constant(i0.value()/i1.value());
+        }
+        return in(1)._type.meet(in(2)._type);
+    }
+
+    @Override
+    public Node idealize() {
+        // Div of constant
+        if( in(2)._type instanceof SONTypeFloat f && f.isConstant() )
+            return new MulFNode(in(1),new ConstantNode(SONTypeFloat.constant(1.0/f.value())).peephole());
+
+        return null;
+    }
+    @Override Node copy(Node lhs, Node rhs) { return new DivFNode(lhs,rhs); }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/DivNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/DivNode.java
new file mode 100644
index 0000000..9c51b6e
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/DivNode.java
@@ -0,0 +1,46 @@
+package com.compilerprogramming.ezlang.compiler.nodes;
+
+import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeInteger;
+import java.util.BitSet;
+
+public class DivNode extends Node {
+    public DivNode(Node lhs, Node rhs) { super(null, lhs, rhs); }
+
+    @Override public String label() { return "Div"; }
+
+    @Override public String glabel() { return "//"; }
+
+    @Override
+    public StringBuilder _print1(StringBuilder sb, BitSet visited) {
+        in(1)._print0(sb.append("("), visited);
+        in(2)._print0(sb.append("/"), visited);
+        return sb.append(")");
+    }
+
+    @Override
+    public SONType compute() {
+        SONType t1 = in(1)._type, t2 = in(2)._type;
+        if( t1.isHigh() || t2.isHigh() )
+            return SONTypeInteger.TOP;
+        if( t1 instanceof SONTypeInteger i1 &&
+            t2 instanceof SONTypeInteger i2 ) {
+            if (i1.isConstant() && i2.isConstant())
+                return i2.value() == 0
+                    ? SONTypeInteger.ZERO
+                    : SONTypeInteger.constant(i1.value()/i2.value());
+        }
+        return SONTypeInteger.BOT;
+    }
+
+    @Override
+    public Node idealize() {
+        // Div of 1.
+        if( in(2)._type == SONTypeInteger.TRUE )
+            return in(1);
+        return null;
+    }
+
+    @Override Node copy(Node lhs, Node rhs) { return new DivNode(lhs,rhs); }
+    @Override Node copyF() { return new DivFNode(null,null); }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/FRefNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/FRefNode.java
new file mode 100644
index 0000000..a48814f
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/FRefNode.java
@@ -0,0 +1,37 @@
+package com.compilerprogramming.ezlang.compiler.nodes;
+
+import com.compilerprogramming.ezlang.compiler.Compiler;
+import com.compilerprogramming.ezlang.compiler.Var;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
+import com.compilerprogramming.ezlang.exceptions.CompilerException;
+
+import java.util.BitSet;
+
+/**
+ *  A Forward Reference.  Its any final constant, including functions.  When
+ *  the Def finally appears its plugged into the forward reference, which then
+ *  peepholes to the Def.
+ */
+public class FRefNode extends ConstantNode {
+    public static final SONType FREF_TYPE = SONType.BOTTOM;
+    public final Var _n;
+    public FRefNode( Var n ) { super(FREF_TYPE); _n = n; }
+
+    @Override public String label() { return "FRef"+_n; }
+
+    @Override public String uniqueName() { return "FRef_" + _nid; }
+
+    @Override public StringBuilder _print1(StringBuilder sb, BitSet visited) {
+        return sb.append("FRef_").append(_n);
+    }
+
+    @Override public Node idealize() {
+        // When FRef finds its definition, idealize to it
+        return nIns()==1 ? null : in(1);
+    }
+
+    public CompilerException err() { return Compiler.error("Undefined name '"+_n._name+"'"); }
+
+    @Override boolean eq(Node n) { return this==n; }
+    @Override int hash() { return _n._idx; }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/FunNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/FunNode.java
new file mode 100644
index 0000000..a0eb0be
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/FunNode.java
@@ -0,0 +1,199 @@
+package com.compilerprogramming.ezlang.compiler.nodes;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFunPtr;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeTuple;
+
+import java.util.BitSet;
+import static com.compilerprogramming.ezlang.compiler.codegen.CodeGen.CODE;
+
+public class FunNode extends RegionNode {
+
+    // When set true, this Call/CallEnd/Fun/Return is being trivially inlined
+    boolean _folding;
+
+    private SONTypeFunPtr _sig;    // Initial signature
+    private ReturnNode _ret;    // Return pointer
+
+    public String _name;        // Debug name
+
+    public FunNode(SONTypeFunPtr sig, Node... nodes ) { super(nodes); _sig = sig; }
+    public FunNode( FunNode fun ) {
+        super( fun );
+        if( fun!=null ) {
+            _sig = fun.sig();
+            _name = fun._name;
+        } else {
+            _sig = SONTypeFunPtr.BOT;
+            _name = "";
+        }
+    }
+
+    @Override
+    public String label() { return _name == null ? "$fun"+_sig.fidx() : _name; }
+
+    // Find the one CFG user from Fun.  It's not always the Return, but always
+    // the Return *is* a CFG user of Fun.
+    @Override public CFGNode uctrl() {
+        for( Node n : _outputs )
+            if( n instanceof CFGNode cfg &&
+                (cfg instanceof RegionNode || cfg.cfg0()==this) )
+                return cfg;
+        return null;
+    }
+
+    public ParmNode rpc() {
+        ParmNode rpc = null;
+        for( Node n : _outputs )
+            if( n instanceof ParmNode parm && parm._idx==0 )
+                { assert rpc==null; rpc=parm; }
+        return rpc;
+    }
+
+    // Cannot create the Return and Fun at the same time; one has to be first.
+    // So setting the return requires a second step.
+    public void setRet(ReturnNode ret) { _ret=ret; }
+    public ReturnNode ret() { assert _ret!=null; return _ret; }
+
+    // Signature can improve over time
+    public SONTypeFunPtr sig() { return _sig; }
+    public void setSig( SONTypeFunPtr sig ) {
+        assert sig.isa(_sig);
+        if( _sig != sig ) {
+            CODE.add(this);
+            _sig = sig;
+        }
+    }
+
+    @Override
+    public SONType compute() {
+        // Only dead if no callers after SCCP
+        return SONType.CONTROL;
+    }
+
+    @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;
+        }
+
+        // Upgrade inferred or user-written return type to actual
+        if( _ret!=null && _ret._type instanceof SONTypeTuple tt && tt.ret() != _sig.ret() )
+            //throw Utils.TODO();
+            return null;
+
+        // 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.
+        // This in turn is valid arguments to every callable function.
+        //
+        // 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;
+
+        // 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 null;
+    }
+
+    // Bypass Region idom, always assume depth == 1, one more than Start
+    @Override public int idepth() { return (_idepth=1); }
+    // Bypass Region idom, always assume idom is Start
+    @Override public CFGNode idom(Node dep) { return cfg(1); }
+
+    // Always in-progress until we run out of unknown callers
+    public boolean unknownCallers() { return in(1) instanceof StartNode; }
+
+    @Override public boolean inProgress() { return unknownCallers(); }
+
+    // Add a new function exit point.
+    public void addReturn(Node ctrl, Node mem, Node rez) {  _ret.addReturn(ctrl,mem,rez);  }
+
+    // Build the function body
+    public BitSet body() {
+
+        // Reverse up (stop to start) CFG only, collect bitmap.
+        BitSet cfgs = new BitSet();
+        cfgs.set(_nid);
+        walkUp(ret(),cfgs );
+
+        // Top down (start to stop) all flavors.  CFG limit to bitmap.
+        // If data use bottoms out in wrong CFG, returns false - but tries all outputs.
+        // If any output hits an in-CFG use (e.g. phi), then keep node.
+        BitSet body = new BitSet();
+        walkDown(this, cfgs, body, new BitSet());
+        return body;
+    }
+
+    private static void walkUp(CFGNode n, BitSet cfgs) {
+        if( cfgs.get(n._nid) ) return;
+        cfgs.set(n._nid);
+        if( n instanceof RegionNode r )
+            for( int i=1; i<n.nIns(); i++ )
+                walkUp(n.cfg(i),cfgs);
+        else walkUp(n.cfg0(),cfgs);
+    }
+
+    private static boolean walkDown( Node n, BitSet cfgs, BitSet body, BitSet visit ) {
+        if( visit.get(n._nid) ) return body.get(n._nid);
+        visit.set(n._nid);
+
+        if( n instanceof CFGNode && !cfgs.get(n._nid) )
+            return false;
+        if( n.in(0)!=null && !cfgs.get(n.in(0)._nid) )
+            return false;
+        boolean in = n.in(0)!=null || n instanceof CFGNode;
+        for( Node use : n._outputs )
+            in |= walkDown(use,cfgs,body,visit);
+        if( in ) body.set(n._nid);
+        return in;
+    }
+
+
+    // | CALLER |
+    // |        |
+    // | argN+1 | // slot 1, callER +32
+    // | argN   | // slot 0, callER +24
+    // +--------+ // OLD FRAME
+    // |  PAD   | // Alignment pad  +16
+    // | callee | // slot 3, callEE + 8
+    // | callee | // slot 2, callEE + 0
+    // +--------+ // RSP
+
+    // TODO: lower this field into a generic FunMachNode.
+    // Replace all FunXXX variants with FunMachNode.
+
+    // Function may call other functions?  X86 requires 16b aligned SP on
+    // outbound calls, but not leaf.  Set during an unrelated RA scan.
+    public boolean _hasCalls;
+
+    // Max observed spill slot, set during RA postcolor
+    public short _maxSlot = -1;
+    // Max argument slot, set during early Encoding.
+    public short _maxArgSlot = -1;
+    // Frame adjust, including padding, st during early Encoding
+    public short _frameAdjust;
+
+
+    // Convert a stack slot, based on the frame, into an offset.  Stack layout
+    // is the same for all CPUs; X86 will pre-spill the RPC.
+    public final int computeStackSlot(int slotN) {
+        return slotN < _maxArgSlot
+            ? slotN + _frameAdjust
+            : slotN - _maxArgSlot;
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/IfNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/IfNode.java
new file mode 100644
index 0000000..5a5145c
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/IfNode.java
@@ -0,0 +1,82 @@
+package com.compilerprogramming.ezlang.compiler.nodes;
+
+import com.compilerprogramming.ezlang.compiler.Utils;
+import com.compilerprogramming.ezlang.compiler.codegen.CodeGen;
+import com.compilerprogramming.ezlang.compiler.IterPeeps;
+import com.compilerprogramming.ezlang.compiler.sontypes.*;
+import java.util.BitSet;
+
+public class IfNode extends CFGNode implements MultiNode {
+
+    public IfNode(Node ctrl, Node pred) {
+        super(ctrl, pred);
+        CodeGen.CODE.add(this); // Because idoms are complex, just add it
+    }
+    public IfNode(IfNode iff) { super(iff); }
+
+    @Override
+    public String label() { return "If"; }
+
+    @Override
+    public StringBuilder _print1(StringBuilder sb, BitSet visited) {
+        sb.append("if( ");
+        return in(1)._print0(sb, visited).append(" )");
+    }
+
+    public Node ctrl() { return in(0); }
+    public Node pred() { return in(1); }
+
+    // No one unique control follows
+    @Override public CFGNode uctrl() { return null; }
+
+    @Override
+    public SONType compute() {
+        // If the If node is not reachable then neither is any following Proj
+        if (ctrl()._type != SONType.CONTROL && ctrl()._type != SONType.BOTTOM )
+            return SONTypeTuple.IF_NEITHER;
+        Node pred = pred();
+        SONType t = pred._type;
+        // High types mean NEITHER side is reachable.
+        // Wait until the type falls to decide which way to go.
+        if( t.isHigh() )
+            return SONTypeTuple.IF_NEITHER;
+        // If constant is 0 then false branch is reachable
+        // Else true branch is reachable
+        if( t.isConstant() )
+            return (t== SONType.NIL || t== SONTypeInteger.ZERO || (t instanceof SONTypeFunPtr tfp && tfp._fidxs==0) ) ? SONTypeTuple.IF_FALSE : SONTypeTuple.IF_TRUE;
+        // If adding a zero makes a difference, the predicate must not have a zero/null
+        if( !t.makeZero().isa(t) )
+            return SONTypeTuple.IF_TRUE;
+
+        return SONTypeTuple.IF_BOTH;
+    }
+
+    @Override
+    public Node idealize() {
+        // Hunt up the immediate dominator tree.  If we find an identical if
+        // test on either the true or false branch, that side wins.
+        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,con( prj._idx==0 ? 1 : 0 ));
+                    return this;
+                }
+        return null;
+    }
+
+    // MachNode variants need to support this and invert the conditional test.
+    // The following CProjs will be inverted by the caller.
+    public void invert() { throw Utils.TODO(); }
+
+    public static String invert( String bop ) {
+        return switch( bop ) {
+        case "<"  -> ">=";
+        case "<=" -> ">" ;
+        case "==" -> "!=";
+        case "!=" -> "==";
+        case ">"  -> "<=";
+        case ">=" -> "<" ;
+        default -> throw Utils.TODO();
+        };
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/LoadNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/LoadNode.java
new file mode 100644
index 0000000..fdec37a
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/LoadNode.java
@@ -0,0 +1,209 @@
+package com.compilerprogramming.ezlang.compiler.nodes;
+
+import com.compilerprogramming.ezlang.compiler.Utils;
+import com.compilerprogramming.ezlang.compiler.sontypes.*;
+import java.util.BitSet;
+
+/**
+ * Load represents extracting a value from inside a memory object,
+ * in chapter 10 this means Struct fields.
+ */
+public class LoadNode extends MemOpNode {
+
+    /**
+     * Load a value from a ptr.field.
+     *
+     * @param name  The field we are loading
+     * @param mem   The memory alias node - this is updated after a Store
+     * @param ptr   The ptr to the struct base from where we load a field
+     * @param off   The offset inside the struct base
+     */
+    public LoadNode(String name, int alias, SONType glb, Node mem, Node ptr, Node off) {
+        super(name, alias, glb, mem, ptr, off);
+    }
+
+    // GraphVis DOT code (must be valid Java identifiers) and debugger labels
+    @Override public String  label() { return "ld_"+mlabel(); }
+    // GraphVis node-internal labels
+    @Override public String glabel() { return "." +_name; }
+
+    @Override
+    public StringBuilder _print1(StringBuilder sb, BitSet visited) { return sb.append(".").append(_name); }
+
+    @Override
+    public SONType compute() {
+        if( mem()._type instanceof SONTypeMem mem ) {
+            // Update declared forward ref to the actual
+            if( _declaredType.isFRef() && mem._t instanceof SONTypeMemPtr tmp && !tmp.isFRef() )
+                _declaredType = tmp;
+            // No lifting if ptr might null-check
+            if( err()==null )
+                return _declaredType.join(mem._t);
+        }
+        return _declaredType;
+    }
+
+    @Override
+    public Node idealize() {
+        Node ptr = ptr();
+        Node mem = mem();
+
+        // Simple Load-after-Store on same address.
+        if( mem instanceof StoreNode st &&
+            ptr == st.ptr() && off() == st.off() ) { // Must check same object
+            assert _name.equals(st._name); // Equiv class aliasing is perfect
+            return extend(st.val());
+        }
+
+        // Simple Load-after-New on same address.
+        if( mem instanceof ProjNode p && p.in(0) instanceof NewNode nnn &&
+            ptr == nnn.proj(1) ) // Must check same object
+            return zero(nnn);   // Load zero from new
+
+        // Uplift control to a prior dominating load.
+        for( Node memuse : mem._outputs )
+            // Find a prior load, has same mem,ptr,off but higher ctrl
+            if( memuse != this && memuse instanceof LoadNode ld && ptr==ld.ptr() && off()==ld.off() &&
+                cfg0()!=null && cfg0()._idom(ld.cfg0(),this) == ld.cfg0() ) // Higher control means load is legal earlier
+                return ld;
+
+        // Load-after-Store on same address, but bypassing provably unrelated
+        // stores.  This is a more complex superset of the above two peeps.
+        // "Provably unrelated" is really weak.
+        if( ptr instanceof ReadOnlyNode ro )
+            ptr = ro.in(1);
+        outer:
+        while( true ) {
+            switch( mem ) {
+            case StoreNode st:
+                if( ptr == st.ptr().addDep(this) && off() == st.off() )
+                    return extend(castRO(st.val())); // Proved equal
+                // Can we prove unequal?  Offsets do not overlap?
+                if( !off()._type.join(st.off()._type).isHigh() && // Offsets overlap
+                    !neverAlias(ptr,st.ptr()) )                   // And might alias
+                    break outer; // Cannot tell, stop trying
+                // Pointers cannot overlap
+                mem = st.mem(); // Proved never equal
+                break;
+            case PhiNode phi:
+                // Assume related
+                phi.addDep(this);
+                break outer;
+            case ConstantNode top: break outer;  // Assume shortly dead
+            case ProjNode mproj: // Memory projection
+                switch( mproj.in(0) ) {
+                case NewNode nnn1:
+                    if( ptr instanceof ProjNode pproj && pproj.in(0) == mproj.in(0) )
+                        return zero(nnn1);
+                    if( !(ptr instanceof ProjNode pproj && pproj.in(0) instanceof NewNode) )
+                        break outer; // Cannot tell, ptr not related to New
+                    mem = nnn1.in(nnn1.findAlias(_alias));// Bypass unrelated New
+                    break;
+                case StartNode  start: break outer;
+                case CallEndNode cend: break outer; // TODO: Bypass no-alias call
+                default: throw Utils.TODO();
+                }
+                break;
+            case MemMergeNode merge:  mem = merge.alias(_alias);  break;
+
+            default:
+                throw Utils.TODO();
+            }
+        }
+
+        // Push a Load up through a Phi, as long as it collapses on at least
+        // one arm.  If at a Loop, the backedge MUST collapse - else we risk
+        // spinning the same transform around the loop indefinitely.
+        //   BEFORE (2 Sts, 1 Ld):          AFTER (1 St, 0 Ld):
+        //   if( pred ) ptr.x = e0;         val = pred ? e0
+        //   else       ptr.x = e1;                    : e1;
+        //   val = ptr.x;                   ptr.x = val;
+        if( mem() instanceof PhiNode memphi && memphi.region()._type == SONType.CONTROL && memphi.nIns()== 3 &&
+            // Offset can be hoisted
+            off() instanceof ConstantNode &&
+            // Pointer can be hoisted
+            hoistPtr(ptr,memphi)  ) {
+
+            // Profit on RHS/Loop backedge
+            if( profit(memphi,2) ||
+                // Else must not be a loop to count profit on LHS.
+                (!(memphi.region() instanceof LoopNode) && profit(memphi,1)) ) {
+                Node ld1 = ld(1);
+                Node ld2 = ld(2);
+                return new PhiNode(_name,_declaredType,memphi.region(),ld1,ld2);
+            }
+        }
+
+        return null;
+    }
+
+    // Load a flavored zero from a New
+    private Node zero(NewNode nnn) {
+        SONTypeStruct ts = nnn._ptr._obj;
+        SONType zero = ts._fields[ts.findAlias(_alias)]._type.makeZero();
+        return castRO(new ConstantNode(zero).peephole());
+    }
+
+    private Node ld( int idx ) {
+        Node mem = mem(), ptr = ptr();
+        return new LoadNode(_name,_alias,_declaredType,mem.in(idx),ptr instanceof PhiNode && ptr.in(0)==mem.in(0) ? ptr.in(idx) : ptr, off()).peephole();
+    }
+
+    private static boolean neverAlias( Node ptr1, Node ptr2 ) {
+        return ptr1.in(0) != ptr2.in(0) &&
+            // Unrelated allocations
+            ptr1 instanceof ProjNode && ptr1.in(0) instanceof NewNode &&
+            ptr2 instanceof ProjNode && ptr2.in(0) instanceof NewNode;
+    }
+
+    private static boolean hoistPtr(Node ptr, PhiNode memphi ) {
+        // Can I hoist ptr above the Region?
+        if( !(memphi.region() instanceof RegionNode r) )
+            return false;       // Dead or dying Region/Phi
+        // If ptr from same Region, then yes, just use hoisted split pointers
+        if( ptr instanceof PhiNode pphi && pphi.region() == r )
+            return true;
+
+        // No, so can we lift this ptr?
+        CFGNode cptr = ptr.cfg0();
+        if( cptr != null )
+            // Pointer is controlled high
+            // TODO: Really needs to be the LCA of all inputs is high
+            return cptr.idepth() <= r.idepth();
+
+        // Dunno without a longer walk
+        return false;
+    }
+
+    // Profitable if we find a matching Store on this Phi arm.
+    private boolean profit(PhiNode phi, int idx) {
+        Node px = phi.in(idx);
+        if( px==null ) return false;
+        if( px._type instanceof SONTypeMem mem && mem._t.isHighOrConst() ) return true;
+        if( px instanceof StoreNode st1 && ptr()==st1.ptr() && off()==st1.off() ) return true;
+        px.addDep(this);
+        return false;
+    }
+
+    // Read-Only is a deep property, and cannot be cast-away
+    private Node castRO(Node rez) {
+        if( ptr()._type.isFinal() && !rez._type.isFinal() )
+            return new ReadOnlyNode(rez).peephole();
+        return rez;
+    }
+
+    // When a load bypasses a store, the store might truncate bits - and the
+    // load will need to zero/sign-extend.
+    private Node extend(Node val) {
+        if( !(_declaredType instanceof SONTypeInteger ti) ) return val;
+        if( ti._min==0 )        // Unsigned
+            return new AndNode(val,con(ti._max));
+        // Signed extension
+        int shift = Long.numberOfLeadingZeros(ti._max)-1;
+        Node shf = con(shift);
+        if( shf._type== SONTypeInteger.ZERO )
+            return val;
+        Node shl = new ShlNode(val,shf.keep()).peephole();
+        return new SarNode(shl,shf.unkeep());
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/LogicalNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/LogicalNode.java
new file mode 100644
index 0000000..1b93813
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/LogicalNode.java
@@ -0,0 +1,28 @@
+package com.compilerprogramming.ezlang.compiler.nodes;
+
+import com.compilerprogramming.ezlang.compiler.Compiler;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeInteger;
+import com.compilerprogramming.ezlang.exceptions.CompilerException;
+
+import java.util.BitSet;
+
+public abstract class LogicalNode extends Node {
+
+    public LogicalNode(Node lhs, Node rhs) { super(null, lhs, rhs); }
+    abstract String op();
+
+    @Override
+    public StringBuilder _print1(StringBuilder sb, BitSet visited) {
+        in(1)._print0(sb.append("("), visited);
+        in(2)._print0(sb.append(op()), visited);
+        return sb.append(")");
+    }
+
+
+    @Override public CompilerException err() {
+        if( in(1)._type.isHigh() || in(2)._type.isHigh() ) return null;
+        if( !(in(1)._type instanceof SONTypeInteger) ) return Compiler.error("Cannot '"+op()+"' " + in(1)._type);
+        if( !(in(2)._type instanceof SONTypeInteger) ) return Compiler.error("Cannot '"+op()+"' " + in(2)._type);
+        return null;
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/LoopNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/LoopNode.java
new file mode 100644
index 0000000..721926c
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/LoopNode.java
@@ -0,0 +1,80 @@
+package com.compilerprogramming.ezlang.compiler.nodes;
+
+import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
+
+public class LoopNode extends RegionNode {
+    public LoopNode( Node entry ) { super(null,entry,null); }
+    public LoopNode( LoopNode loop ) { super(loop); }
+
+    public CFGNode entry() { return cfg(1); }
+    public CFGNode back () { return cfg(2); }
+
+    @Override
+    public String label() { return "Loop"; }
+
+    @Override
+    public SONType compute() {
+        if( inProgress() ) return SONType.CONTROL;
+        return entry()._type;
+    }
+
+    // Bypass Region idom, same as the default idom() using use in(1) instead of in(0)
+    @Override public int idepth() { return _idepth==0 ? (_idepth=idom().idepth()+1) : _idepth; }
+    // Bypass Region idom, same as the default idom() using use in(1) instead of in(0)
+    @Override public CFGNode idom(Node dep) { return entry(); }
+
+    // If this is an unreachable loop, it may not have an exit.  If it does not
+    // (i.e., infinite loop), force an exit to make it reachable.
+    public StopNode forceExit( FunNode fun, StopNode stop ) {
+        // Walk the backedge, then immediate dominator tree util we hit this
+        // Loop again.  If we ever hit a CProj from an If (as opposed to
+        // directly on the If) we found our exit.
+        CFGNode x = back();
+        while( x != this ) {
+            if( x instanceof CProjNode exit && exit.in(0) instanceof IfNode iff ) {
+                CFGNode other = iff.cproj(1-exit._idx);
+                if( other!=null && other.loopDepth() < loopDepth() )
+                    return stop; // Found an exit, not an infinite loop
+            }
+            x = x.idom();
+        }
+        // Found a no-exit loop.  Insert an exit
+        NeverNode iff = new NeverNode(back());
+        for( Node use : _outputs )
+            if( use instanceof PhiNode )
+                iff.addDef(use);
+        CProjNode t = new CProjNode(iff,0,"True" ).init();
+        CProjNode f = new CProjNode(iff,1,"False").init();
+        setDef(2,f);
+
+        // Now fold control into the exit.  Might have 1 valid exit, or an
+        // XCtrl or a bunch of prior NeverNode exits.
+        Node top = new ConstantNode(SONType.TOP).peephole();
+        ReturnNode ret = fun.ret();
+        Node ctrl = ret.ctrl(), mem = ret.mem(), expr = ret.expr();
+        if( ctrl._type != SONType.XCONTROL ) {
+            // Perfect aligned exit?
+            if( !(ctrl instanceof RegionNode r &&
+                  mem  instanceof PhiNode pmem && pmem.region()==r &&
+                  expr instanceof PhiNode prez && prez.region()==r ) ) {
+                // Nope, insert an aligned exit layer
+                ctrl = new RegionNode(null,ctrl).init();
+                mem  = new    PhiNode((RegionNode)ctrl,mem ).init();
+                expr = new    PhiNode((RegionNode)ctrl,expr).init();
+            }
+            // Append new Never exit
+            ctrl.addDef(t  );
+            mem .addDef(top);
+            expr.addDef(top);
+        } else {
+            ctrl = t;
+            mem  = top;
+            expr = top;
+        }
+        ret.setDef(0,ctrl);
+        ret.setDef(1,mem );
+        ret.setDef(2,expr);
+
+        return stop;
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MachConcreteNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MachConcreteNode.java
new file mode 100644
index 0000000..82aa6e0
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MachConcreteNode.java
@@ -0,0 +1,27 @@
+package com.compilerprogramming.ezlang.compiler.nodes;
+
+import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
+import com.compilerprogramming.ezlang.compiler.Utils;
+
+import java.util.BitSet;
+
+// Generic machine-specific class, has a few Node implementations that have to
+// exist (abstract) but are not useful past the optimizer.
+public abstract class MachConcreteNode extends Node implements MachNode {
+
+    public MachConcreteNode(Node node) { super(node); }
+    public MachConcreteNode(Node[]nodes) { super(nodes); }
+
+    @Override public String label() { return op(); }
+    @Override public SONType compute () { throw Utils.TODO(); }
+    @Override public Node idealize() { throw Utils.TODO(); }
+
+    @Override public StringBuilder _print1(StringBuilder sb, BitSet visited) {
+        sb.append("(").append(op()).append(",");
+        for( int i=1; i<nIns(); i++ )
+            (in(i)==null ? sb.append(" ---") :  in(i)._print0(sb, visited)).append(",");
+        sb.setLength(sb.length()-1);
+        return sb.append(")");
+    }
+
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MachNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MachNode.java
new file mode 100644
index 0000000..7012534
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MachNode.java
@@ -0,0 +1,62 @@
+package com.compilerprogramming.ezlang.compiler.nodes;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.Encoding;
+import com.compilerprogramming.ezlang.compiler.codegen.Machine;
+import com.compilerprogramming.ezlang.compiler.codegen.CodeGen;
+import com.compilerprogramming.ezlang.compiler.codegen.RegMask;
+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( CodeGen code ) { }
+
+    // Register mask allowed on input i.  0 for no register.
+    RegMask regmap( int i );
+    // Register mask allowed as a result.  0 for no register.
+    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(); }
+
+    // Set of killed registers
+    default RegMask killmap() { return null; }
+
+    // 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; }
+    // Ok to switch arguments
+    default boolean commutes() { return false; }
+
+    // Instructions cheaper to recreate than to spill, such as loading small constants
+    default boolean isClone() { return false; }
+
+    // Make a clone of a cheap instruction
+    default Node copy() { return null; }
+
+    // Encoding is appended, relocations gathered.
+    void encoding( Encoding enc );
+
+    // 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/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MemMergeNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MemMergeNode.java
new file mode 100644
index 0000000..09487c9
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MemMergeNode.java
@@ -0,0 +1,155 @@
+package com.compilerprogramming.ezlang.compiler.nodes;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.CodeGen;
+import com.compilerprogramming.ezlang.compiler.sontypes.*;
+import java.util.*;
+
+/**
+ *  Memory Merge - a merge of many aliases into a "fat memory".  All aliases
+ *  are here, but most will be lazy - take the default fat memory.
+ */
+public class MemMergeNode extends Node {
+
+    /*
+     *  In-Progress means this is being used by the Parser to track memory
+     *  aliases.  No optimizations are allowed.  When no longer "in progress"
+     *  normal peeps work.
+     */
+    public final boolean _inProgress;
+
+    public MemMergeNode( boolean inProgress) { _type = SONTypeMem.BOT; _inProgress = inProgress; }
+    public MemMergeNode(MemMergeNode mem) { super(mem); _inProgress = false; }
+
+
+    // If being used by a Scope, this is "in progress" from the Parser.
+    // Otherwise, it's a memory merge
+    boolean inProgress() { return _inProgress; }
+
+    @Override public String label() { return "ALLMEM"; }
+    @Override public boolean isMem() { return true; }
+
+    @Override
+    public StringBuilder _print1(StringBuilder sb, BitSet visited) {
+        sb.append(_inProgress ? "Merge[" : "MEM[ ");
+        for( int j=2; j<nIns(); j++ ) {
+            sb.append(j);
+            sb.append(":");
+            Node n = in(j);
+            while( n instanceof ScopeNode loop ) {
+                sb.append("Lazy_");
+                n = loop.mem(j);
+            }
+            if( n==null ) sb.append("___ ");
+            else n._print0(sb, visited).append(" ");
+        }
+        sb.setLength(sb.length()-1);
+        return sb.append("]");
+    }
+
+    // Make a memory merge: no longer a Scope really, tracking memory state but
+    // not related to the parser in any way.
+    public Node merge() {
+        // Force default memory to not be lazy
+        MemMergeNode merge = new MemMergeNode(false);
+        for( Node n : _inputs )
+            merge.addDef(n);
+        merge._mem(1,null);
+        return merge.peephole();
+    }
+
+
+    @Override public SONType compute() { return SONTypeMem.BOT; }
+
+    @Override public Node idealize() {
+        if( inProgress() ) return null;
+
+        // If not merging any memory (all memory is just the default)
+        if( nIns()==2 )
+            return in(1);       // Become default memory
+
+        return null;
+    }
+
+    public Node in( Var v ) { return in(v._idx); }
+
+    public Node alias( int alias ) {
+        return in(alias<nIns() && in(alias)!=null ? alias : 1);
+    }
+
+    Node alias( int alias, Node st ) {
+        while( alias >= nIns() ) addDef(null);
+        return setDef(alias,st);
+    }
+
+    // Read or update from memory.
+    // A shared implementation allows us to create lazy phis both during
+    // lookups and updates; the lazy phi creation is part of chapter 8.
+    Node _mem( int alias, Node st ) {
+        // Memory projections are made lazily; if one does not exist
+        // then it must be START.proj(1)
+        Node old = alias(alias);
+        if( old instanceof ScopeNode loop ) {
+            MemMergeNode loopmem = loop.mem();
+            Node memdef = loopmem.alias(alias);
+            // Lazy phi!
+            old = memdef instanceof PhiNode phi && loop.ctrl()==phi.region()
+                // Loop already has a real Phi, use it
+                ? memdef
+                // Set real Phi in the loop head
+                // The phi takes its one input (no backedge yet) from a recursive
+                // lookup, which might have insert a Phi in every loop nest.
+                : loopmem.alias(alias, new PhiNode(Compiler.memName(alias), SONTypeMem.BOT,loop.ctrl(),loopmem._mem(alias,null),null).peephole() );
+            alias(alias,old);
+        }
+        // Memory projections are made lazily; expand as needed
+        return st==null ? old : alias(alias,st); // Not lazy, so this is the answer
+    }
+
+
+    void _merge( MemMergeNode that, RegionNode r) {
+        int len = Math.max(nIns(),that.nIns());
+        for( int i = 2; i < len; i++)
+            if( alias(i) != that.alias(i) ) { // No need for redundant Phis
+                // If we are in lazy phi mode we need to a lookup
+                // by alias as it will trigger a phi creation
+                Node lhs = this._mem(i,null);
+                Node rhs = that._mem(i,null);
+                alias(i, new PhiNode(Compiler.memName(i), lhs._type.glb().meet(rhs._type.glb()), r, lhs, rhs).peephole());
+            }
+    }
+
+    // Fill in the backedge of any inserted Phis
+    void _endLoopMem( ScopeNode scope, MemMergeNode back, MemMergeNode exit ) {
+        Node exit_def = exit.alias(1);
+        for( int i=1; i<nIns(); i++ ) {
+            if( in(i) instanceof PhiNode phi && phi.region()==scope.ctrl() ) {
+                assert phi.in(2)==null;
+                phi.setDef(2,back.in(i)==scope ? phi : back.in(i)); // Fill backedge
+            }
+            if( exit_def == scope ) // Replace a lazy-phi on the exit path also
+                exit.alias(i,in(i));
+        }
+    }
+
+    // Now one-time do a useless-phi removal
+    void _useless( ) {
+        for( int i=2; i<nIns(); i++ ) {
+            if( in(i) instanceof PhiNode phi ) {
+                // Do an eager useless-phi removal
+                Node in = phi.peephole();
+                CodeGen.CODE.addAll(phi._outputs);
+                phi.moveDepsToWorklist();
+                if( in != phi ) {
+                    if( !phi.iskeep() ) // Keeping phi around for parser elsewhere
+                        phi.subsume(in);
+                    setDef(i,in); // Set the update back into Scope
+                }
+            }
+        }
+    }
+
+    @Override boolean eq( Node n ) {
+        return this==n || !_inProgress;
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MemOpNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MemOpNode.java
new file mode 100644
index 0000000..ae96bed
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MemOpNode.java
@@ -0,0 +1,78 @@
+package com.compilerprogramming.ezlang.compiler.nodes;
+
+import com.compilerprogramming.ezlang.compiler.Compiler;
+import com.compilerprogramming.ezlang.compiler.Utils;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeMemPtr;
+import com.compilerprogramming.ezlang.exceptions.CompilerException;
+
+import java.lang.StringBuilder;
+import java.util.BitSet;
+
+/**
+ * Convenience common base for Load and Store.
+ *
+ * 0 - Control
+ * 1 - Memory
+ * 2 - Base of object, OOP
+ * 3 - Offset, integer types
+ * 4 - Value, for Stores only
+ */
+public abstract class MemOpNode extends Node {
+
+    public final String _name;
+    public final int _alias;
+    // Declared type; not final because it might be a forward-reference
+    // which will be lazily improved when the reference is declared.
+    public SONType _declaredType;
+
+    public MemOpNode(String name, int alias, SONType glb, Node mem, Node ptr, Node off) {
+        super(null, mem, ptr, off);
+        _name  = name;
+        _alias = alias;
+        _declaredType = glb;
+    }
+    public MemOpNode(String name, int alias, SONType glb, Node mem, Node ptr, Node off, Node value) {
+        this(name, alias, glb, mem, ptr, off);
+        addDef(value);
+    }
+    public MemOpNode( Node mach, MemOpNode mop ) {
+        super(mach);
+        _name  = mop==null ? null : mop._name;
+        _alias = mop==null ? 0    : mop._alias;
+        _declaredType = mop==null ? SONType.BOTTOM : mop._declaredType;
+    }
+
+    //
+    static String mlabel(String name) { return name.equals("[]") ? "ary" : (name.equals("#") ? "len" : name); }
+    String mlabel() { return mlabel(_name); }
+
+    public Node mem() { return in(1); }
+    public Node ptr() { return in(2); }
+    public Node off() { return in(3); }
+
+    @Override public StringBuilder _print1( StringBuilder sb, BitSet visited ) { return _printMach(sb,visited);  }
+    public StringBuilder _printMach( StringBuilder sb, BitSet visited ) { throw Utils.TODO(); }
+
+
+    @Override
+    boolean eq(Node n) {
+        MemOpNode mem = (MemOpNode)n; // Invariant
+        return _alias==mem._alias;    // When comparing types error to use "equals"; always use "=="
+    }
+
+    @Override
+    int hash() { return _alias; }
+
+    @Override
+    public CompilerException err() {
+        SONType ptr = ptr()._type;
+        // Already an error, but better error messages come from elsewhere
+        if( ptr == SONType.BOTTOM ) return null;
+        if( ptr.isHigh() ) return null; // Assume it will fall to not-null
+        // Better be a not-nil TMP
+        if( ptr instanceof SONTypeMemPtr tmp && tmp.notNull() )
+            return null;
+        return Compiler.error( "Might be null accessing '" + _name + "'");
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MinusFNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MinusFNode.java
new file mode 100644
index 0000000..aa93f64
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MinusFNode.java
@@ -0,0 +1,36 @@
+package com.compilerprogramming.ezlang.compiler.nodes;
+
+import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFloat;
+
+import java.util.BitSet;
+
+public class MinusFNode extends Node {
+    public MinusFNode(Node in) { super(null, in); }
+
+    @Override public String label() { return "MinusF"; }
+
+    @Override public String glabel() { return "-"; }
+
+    @Override
+    public StringBuilder _print1(StringBuilder sb, BitSet visited) {
+        in(1)._print0(sb.append("(-"), visited);
+        return sb.append(")");
+    }
+
+    @Override
+    public SONType compute() {
+        if (in(1)._type instanceof SONTypeFloat i0)
+            return i0.isConstant() ? SONTypeFloat.constant(-i0.value()) : i0;
+        return SONTypeFloat.F64;
+    }
+
+    @Override
+    public Node idealize() {
+        // -(-x) is x
+        if( in(1) instanceof MinusFNode minus )
+            return minus.in(1);
+
+        return null;
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MinusNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MinusNode.java
new file mode 100644
index 0000000..6bb92e1
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MinusNode.java
@@ -0,0 +1,39 @@
+package com.compilerprogramming.ezlang.compiler.nodes;
+
+import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeInteger;
+
+import java.util.BitSet;
+
+public class MinusNode extends Node {
+    public MinusNode(Node in) { super(null, in); }
+
+    @Override public String label() { return "Minus"; }
+
+    @Override public String glabel() { return "-"; }
+
+    @Override
+    public StringBuilder _print1(StringBuilder sb, BitSet visited) {
+        in(1)._print0(sb.append("(-"), visited);
+        return sb.append(")");
+    }
+
+    @Override
+    public SONType compute() {
+        if( in(1)._type.isHigh() ) return SONTypeInteger.TOP;
+        if( in(1)._type instanceof SONTypeInteger i0 &&
+            !(i0== SONTypeInteger.BOT || i0._min == Long.MIN_VALUE || i0._max == Long.MIN_VALUE) )
+            return SONTypeInteger.make(-i0._max,-i0._min);
+        return SONTypeInteger.BOT;
+    }
+
+    @Override
+    public Node idealize() {
+        // -(-x) is x
+        if( in(1) instanceof MinusNode minus )
+            return minus.in(1);
+
+        return null;
+    }
+    @Override Node copyF() { return new MinusFNode(null); }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MulFNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MulFNode.java
new file mode 100644
index 0000000..d0f0f37
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MulFNode.java
@@ -0,0 +1,51 @@
+package com.compilerprogramming.ezlang.compiler.nodes;
+
+import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFloat;
+
+import java.util.BitSet;
+
+public class MulFNode extends Node {
+    public MulFNode(Node lhs, Node rhs) { super(null, lhs, rhs); }
+
+    @Override public String label() { return "MulF"; }
+
+    @Override public String glabel() { return "*"; }
+
+    @Override
+    public StringBuilder _print1(StringBuilder sb, BitSet visited) {
+        in(1)._print0(sb.append("("), visited);
+        in(2)._print0(sb.append("*"), visited);
+        return sb.append(")");
+    }
+
+    @Override
+    public SONType compute() {
+        if (in(1)._type instanceof SONTypeFloat i0 &&
+            in(2)._type instanceof SONTypeFloat i1) {
+            if (i0.isConstant() && i1.isConstant())
+                return SONTypeFloat.constant(i0.value()*i1.value());
+        }
+        return in(1)._type.meet(in(2)._type);
+    }
+
+    @Override
+    public Node idealize() {
+        Node lhs = in(1);
+        Node rhs = in(2);
+        SONType t1 = lhs._type;
+        SONType 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 SONTypeFloat i && i.value()==1 )
+            return lhs;
+
+        // Move constants to RHS: con*arg becomes arg*con
+        if ( t1.isConstant() && !t2.isConstant() )
+            return swap12();
+
+        return null;
+    }
+    @Override Node copy(Node lhs, Node rhs) { return new MulFNode(lhs,rhs); }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MulNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MulNode.java
new file mode 100644
index 0000000..e35b172
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MulNode.java
@@ -0,0 +1,89 @@
+package com.compilerprogramming.ezlang.compiler.nodes;
+
+import com.compilerprogramming.ezlang.compiler.Compiler;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeInteger;
+import com.compilerprogramming.ezlang.exceptions.CompilerException;
+
+import java.util.BitSet;
+
+public class MulNode extends Node {
+    public MulNode(Node lhs, Node rhs) { super(null, lhs, rhs); }
+
+    @Override public String label() { return "Mul"; }
+
+    @Override public String glabel() { return "*"; }
+
+    @Override
+    public StringBuilder _print1(StringBuilder sb, BitSet visited) {
+        in(1)._print0(sb.append("("), visited);
+        in(2)._print0(sb.append("*"), visited);
+        return sb.append(")");
+    }
+
+    @Override
+    public SONType compute() {
+        SONType t1 = in(1)._type, t2 = in(2)._type;
+        if( t1.isHigh() || t2.isHigh() )
+            return SONTypeInteger.TOP;
+        if( t1 instanceof SONTypeInteger i1 &&
+            t2 instanceof SONTypeInteger i2 ) {
+            if( i1== SONTypeInteger.ZERO || i2== SONTypeInteger.ZERO)
+                return SONTypeInteger.ZERO;
+            if (i1.isConstant() && i2.isConstant())
+                return SONTypeInteger.constant(i1.value()*i2.value());
+        }
+        return SONTypeInteger.BOT;
+    }
+
+    @Override
+    public Node idealize() {
+        Node lhs = in(1);
+        Node rhs = in(2);
+        SONType t1 = lhs._type;
+        SONType t2 = rhs._type;
+
+        // Move constants to RHS: con*arg becomes arg*con
+        if ( t1.isConstant() && !t2.isConstant() )
+            return swap12();
+
+        // Multiply by constant
+        if ( t2.isConstant() && t2 instanceof SONTypeInteger 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;
+            if( c==0 )  return Compiler.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(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(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(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...)
+        Node phicon = AddNode.phiCon(this,true);
+        if( phicon!=null ) return phicon;
+
+        return null;
+    }
+    @Override Node copy(Node lhs, Node rhs) { return new MulNode(lhs,rhs); }
+    @Override Node copyF() { return new MulFNode(null,null); }
+    @Override public CompilerException err() {
+        if( in(1)._type.isHigh() || in(2)._type.isHigh() ) return null;
+        if( !(in(1)._type instanceof SONTypeInteger) ) return Compiler.error("Cannot '"+label()+"' " + in(1)._type.glb());
+        if( !(in(2)._type instanceof SONTypeInteger) ) return Compiler.error("Cannot '"+label()+"' " + in(2)._type.glb());
+        return null;
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MultiNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MultiNode.java
new file mode 100644
index 0000000..4ef4ede
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MultiNode.java
@@ -0,0 +1,29 @@
+package com.compilerprogramming.ezlang.compiler.nodes;
+
+import com.compilerprogramming.ezlang.compiler.Ary;
+
+
+public interface MultiNode {
+
+    abstract Ary<Node> outs();
+
+    // Find a projection by index
+    default ProjNode proj( int idx ) {
+        for( Node out : outs() )
+            if( out instanceof ProjNode prj && prj._idx==idx )
+                return prj;
+        return null;
+    }
+
+    // Find a projection by index
+    default CProjNode cproj( int idx ) {
+        for( Node out : outs() )
+            if( out instanceof CProjNode prj && prj._idx==idx )
+                return prj;
+        return null;
+    }
+
+    // Return not-null if this projection index is a ideal copy.
+    // Called by ProjNode ideal and used to collapse Multis.
+    default Node pcopy(int idx) { return null; }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/NeverNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/NeverNode.java
new file mode 100644
index 0000000..b60f7a1
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/NeverNode.java
@@ -0,0 +1,20 @@
+package com.compilerprogramming.ezlang.compiler.nodes;
+
+import com.compilerprogramming.ezlang.compiler.Compiler;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeTuple;
+
+import java.util.BitSet;
+
+// "Never true" for infinite loop exits
+public class NeverNode extends IfNode {
+    public NeverNode(Node ctrl) { super(ctrl,Compiler.ZERO); }
+
+    @Override public String label() { return "Never"; }
+
+    @Override public StringBuilder _print1(StringBuilder sb, BitSet visited) { return sb.append("Never"); }
+
+    @Override public SONType compute() { return SONTypeTuple.IF_BOTH; }
+
+    @Override public Node idealize() { return null; }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/NewNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/NewNode.java
new file mode 100644
index 0000000..364cea0
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/NewNode.java
@@ -0,0 +1,83 @@
+package com.compilerprogramming.ezlang.compiler.nodes;
+
+import com.compilerprogramming.ezlang.compiler.sontypes.*;
+
+import java.util.BitSet;
+
+/**
+ *  Allocation!  Allocate a chunk of memory, and pre-zero it.
+ *  The inputs include control and size, and ALL aliases being set.
+ *  The output is large tuple, one for every alias plus the created pointer.
+ *  New is expected to be followed by projections for every alias.
+ */
+public class NewNode extends Node implements MultiNode {
+
+    public final SONTypeMemPtr _ptr;
+    public final int _len;
+
+    public NewNode(SONTypeMemPtr ptr, Node... nodes) {
+        super(nodes);
+        assert !ptr.nullable();
+        _ptr = ptr;
+        _len = ptr._obj._fields.length;
+        // Control in slot 0
+        assert nodes[0]._type== SONType.CONTROL || nodes[0]._type == SONType.XCONTROL;
+        // Malloc-length in slot 1
+        assert nodes[1]._type instanceof SONTypeInteger || nodes[1]._type== SONType.NIL;
+        for( int i=0; i<_len; i++ )
+            // Memory slices for all fields.
+            assert nodes[2+i]._type.isa(SONTypeMem.BOT);
+    }
+
+    public NewNode(NewNode nnn) { super(nnn); _ptr = nnn._ptr; _len = nnn._len; }
+
+    public Node mem (int idx) { return in(idx+2); }
+
+    @Override public String label() { return "new_"+glabel(); }
+    @Override public String glabel() {
+        return _ptr._obj.isAry() ? "ary_"+_ptr._obj._fields[1]._type.str() : _ptr._obj.str();
+    }
+
+    @Override
+    public StringBuilder _print1(StringBuilder sb, BitSet visited) {
+        sb.append("new ");
+        return sb.append(_ptr._obj.str());
+    }
+
+    int findAlias(int alias) {
+        int idx = _ptr._obj.findAlias(alias);
+        assert idx!= -1;        // Error, caller should be calling
+        return idx+2;           // Skip ctrl, size
+    }
+
+    // 0 - ctrl
+    // 1 - byte size
+    // 2-len+2 - aliases, one per field
+    // len+2 - 2*len+2 - initial values, one per field
+    public Node size() { return in(1); }
+
+    @Override
+    public SONTypeTuple compute() {
+        Field[] fs = _ptr._obj._fields;
+        SONType[] ts = new SONType[fs.length+2];
+        ts[0] = SONType.CONTROL;
+        ts[1] = _ptr;
+        for( int i=0; i<fs.length; i++ ) {
+            SONType mt = in(i+2)._type;
+            SONTypeMem mem = mt== SONType.TOP ? SONTypeMem.TOP : (SONTypeMem)mt;
+            SONType tfld = mem._t.meet(mem._t.makeZero());
+            SONType tfld2 = tfld.join(fs[i]._type);
+            ts[i+2] = SONTypeMem.make(fs[i]._alias,tfld2);
+        }
+        return SONTypeTuple.make(ts);
+    }
+
+    @Override
+    public Node idealize() { return null; }
+
+    @Override
+    boolean eq(Node n) { return this == n; }
+
+    @Override
+    int hash() { return _ptr.hashCode(); }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/Node.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/Node.java
new file mode 100644
index 0000000..7d4c936
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/Node.java
@@ -0,0 +1,721 @@
+package com.compilerprogramming.ezlang.compiler.nodes;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.CodeGen;
+import com.compilerprogramming.ezlang.compiler.print.IRPrinter;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFloat;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeInteger;
+import com.compilerprogramming.ezlang.exceptions.CompilerException;
+
+import java.util.*;
+import java.util.function.Function;
+import static com.compilerprogramming.ezlang.compiler.codegen.CodeGen.CODE;
+
+/**
+ * All Nodes in the Sea of Nodes IR inherit from the Node class.
+ * The Node class provides common functionality used by all subtypes.
+ * Subtypes of Node specialize by overriding methods.
+ */
+public abstract class Node {
+
+    /**
+     * Each node has a unique dense Node ID within a compilation context
+     * The ID is useful for debugging, for using as an offset in a bitvector,
+     * as well as for computing equality of nodes (to be implemented later).
+     */
+    public final int _nid;
+
+    /**
+     * Inputs to the node. These are use-def references to Nodes.
+     * <p>
+     * Generally fixed length, ordered, nulls allowed, no unused trailing space.
+     * Ordering is required because e.g. "a/b" is different from "b/a".
+     * The first input (offset 0) is often a {@link CFGNode} node.
+     */
+    public final Ary<Node> _inputs;
+
+    /**
+     * Outputs reference Nodes that are not null and have this Node as an
+     * input.  These nodes are users of this node, thus these are def-use
+     * references to Nodes.
+     * <p>
+     * Outputs directly match inputs, making a directed graph that can be
+     * walked in either direction.  These outputs are typically used for
+     * efficient optimizations but otherwise have no semantics meaning.
+     */
+    public final Ary<Node> _outputs;
+
+
+    /**
+     * Current computed type for this Node.  This value changes as the graph
+     * changes and more knowledge is gained about the program.
+     */
+    public SONType _type;
+
+
+    Node(Node... inputs) {
+        _nid = CODE.getUID(); // allocate unique dense ID
+        _inputs = new Ary<>(Node.class);
+        Collections.addAll(_inputs,inputs);
+        _outputs = new Ary<>(Node.class);
+        for( Node n : _inputs )
+            if( n != null )
+                n.addUse( this );
+    }
+
+    // Make a Node using the existing arrays of nodes.
+    // Used by any pass rewriting all Node classes but not the edges.
+    Node( Node n ) {
+        assert CodeGen.CODE._phase.ordinal() >= CodeGen.Phase.InstSelect.ordinal();
+        _nid = CODE.getUID(); // allocate unique dense ID
+        _inputs  = new Ary<>(n==null ? new Node[0] : n._inputs.asAry());
+        _outputs = new Ary<>(Node.class);
+        _type = n==null ? SONType.BOTTOM : n._type;
+        _deps = null;
+        _hash = 0;
+    }
+
+    // Easy reading label for debugger, e.g. "Add" or "Region" or "EQ"
+    public abstract String label();
+
+    // Unique label for graph visualization, e.g. "Add12" or "Region30" or "EQ99"
+    public String uniqueName() {
+        // Get rid of $ as graphviz doesn't like it
+        String label = label().replaceAll("\\$", "");
+        return label + _nid;
+    }
+
+    // Graphical label, e.g. "+" or "Region" or "=="
+    public String glabel() { return label(); }
+
+    // Extra fun stuff, for assembly printing.  Jump labels, parser locations,
+    // variable types, etc.
+    public String comment() { return null; }
+
+    // ------------------------------------------------------------------------
+
+    // Debugger Printing.
+
+    // {@code toString} is what you get in the debugger.  It has to print 1
+    // line (because this is what a debugger typically displays by default) and
+    // has to be robust with broken graph/nodes.
+    @Override
+    public final String toString() {  return print(); }
+
+    // This is a *deep* print.  We print with a mutually recursive tik-tok
+    // style; the common _print0 calls the per-Node _print1, which calls back
+    // to _print0;
+    public final String print() {
+        return _print0(new StringBuilder(), new BitSet()).toString();
+    }
+
+    // This is the common print: check for repeats, check for DEAD and print
+    // "DEAD" else call the per-Node print1.
+    public final StringBuilder _print0(StringBuilder sb, BitSet visited) {
+        if (visited.get(_nid) && !(this instanceof ConstantNode) )
+            return sb.append(label());
+        visited.set(_nid);
+        return isDead()
+            ? sb.append(uniqueName()).append(":DEAD")
+            : _print1(sb, visited);
+    }
+    // Every Node implements this; a partial-line recursive print
+    abstract public StringBuilder _print1(StringBuilder sb, BitSet visited);
+
+    public String p(int depth) { return IRPrinter.prettyPrint(this,depth); }
+
+    public boolean isConst() { return false; }
+
+    // ------------------------------------------------------------------------
+    // Graph Node & Edge manipulation
+
+    /**
+     * Gets the ith input node
+     * @param i Offset of the input node
+     * @return Input node or null
+     */
+    public Node in(int i) { return _inputs.get(i); }
+    public Node out(int i) { return _outputs.get(i); }
+    public final Ary<Node> outs() { return _outputs; }
+
+    public int nIns() { return _inputs.size(); }
+
+    public int nOuts() { return _outputs.size(); }
+
+    public boolean isUnused() { return nOuts() == 0; }
+
+    public CFGNode cfg0() { return (CFGNode)in(0); }
+
+    /**
+     * Change a <em>def</em> into a Node.  Keeps the edges correct, by removing
+     * the corresponding <em>use->def</em> edge.  This may make the original
+     * <em>def</em> go dead.  This function is co-recursive with {@link #kill}.
+     * <p>
+
+     * This method is the normal path for altering a Node, because it does the
+     * proper default edge maintenance.  It also <em>immediately</em> kills
+     * Nodes that lose their last use; at times care must be taken to avoid
+     * killing Nodes that are being used without having an output Node.  This
+     * definitely happens in the middle of recursive {@link #peephole} calls.
+     *
+     * @param idx which def to set
+     * @param new_def the new definition
+     * @return new_def for flow coding
+     */
+    public <N extends Node> N setDef(int idx, N new_def ) {
+        unlock();
+        Node old_def = in(idx);
+        if( old_def == new_def ) return new_def; // No change
+        // If new def is not null, add the corresponding def->use edge
+        // This needs to happen before removing the old node's def->use edge as
+        // the new_def might get killed if the old node kills it recursively.
+        if( new_def != null )
+            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 CODE.add(old_def);    // Else old lost a use, so onto worklist
+        }
+        moveDepsToWorklist();
+        // Return new_def for easy flow-coding
+        return new_def;
+    }
+    public <N extends Node> N setDefX(int idx, N new_def ) {
+        while( nIns() <= idx ) addDef(null);
+        return setDef(idx,new_def);
+    }
+
+    // Remove the numbered input, compressing the inputs in-place.  This
+    // shuffles the order deterministically - which is suitable for Region and
+    // Phi, but not for every Node.  If the def goes dead, it is recursively
+    // killed, which may include 'this' Node.
+    public Node delDef(int idx) {
+        unlock();
+        Node old_def = in(idx);
+        _inputs.del(idx);
+        if( old_def.delUse(this) ) // If we removed the last use, the old def is now dead
+            old_def.kill();     // Kill old def
+        old_def.moveDepsToWorklist();
+        return this;
+    }
+
+    // Insert the numbered input, sliding other inputs to the right
+    Node insertDef(int idx, Node new_def) {
+        _inputs.add(idx,null);
+        return setDef(idx,new_def);
+    }
+
+
+    /**
+     * Add a new def to an existing Node.  Keep the edges correct by
+     * adding the corresponding <em>def->use</em> edge.
+     *
+     * @param new_def the new definition, appended to the end of existing definitions
+     * @return new_def for flow coding
+     */
+    public Node addDef(Node new_def) {
+        unlock();
+        // Add use->def edge
+        _inputs.add(new_def);
+        // If new def is not null, add the corresponding def->use edge
+        if( new_def != null )
+            new_def.addUse(this);
+        return new_def;
+    }
+
+    // Breaks the edge invariants, used temporarily
+    protected <N extends Node> N addUse(Node n) { _outputs.add(n); return (N)this; }
+
+    // Remove node 'use' from 'def's (i.e. our) output list, by compressing the list in-place.
+    // Return true if the output list is empty afterward.
+    // Error is 'use' does not exist; ok for 'use' to be null.
+    protected boolean delUse( Node use ) {
+        _outputs.del(_outputs.find(use));
+        return _outputs.isEmpty();
+    }
+
+    // Shortcut for "popping" until n nodes.  A "pop" is basically a
+    // setDef(last,null) followed by lowering the nIns() count.
+    void popUntil(int n) {
+        unlock();
+        while( nIns() > n ) {
+            Node old_def = _inputs.pop();
+            if( old_def != null &&     // If it exists and
+                old_def.delUse(this) ) // If we removed the last use, the old def is now dead
+                old_def.kill();        // Kill old def
+        }
+    }
+
+    /**
+     * Kill a Node with no <em>uses</em>, by setting all of its <em>defs</em>
+     * to null.  This may recursively kill more Nodes, and is basically dead
+     * code elimination.
+     */
+    public void kill( ) {
+        unlock();
+        moveDepsToWorklist();
+        assert isUnused();      // Has no uses, so it is dead
+        _type=null;             // Flag as dead
+        while( nIns()>0 ) { // Set all inputs to null, recursively killing unused Nodes
+            Node old_def = _inputs.removeLast();
+            // 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
+    }
+
+    // Mostly used for asserts and printing.
+    public boolean isDead() { return isUnused() && nIns()==0 && _type==null; }
+
+    // Shortcuts to stop DCE mid-parse
+    // Add bogus null use to keep node alive
+    public <N extends Node> N keep() { return addUse(null); }
+    // Remove bogus null.
+    public <N extends Node> N unkeep() {
+        delUse(null);
+        return (N)this;
+    }
+    // Test "keep" status
+    public boolean iskeep() { return _outputs.find(null) != -1; }
+    public void unkill() {
+        if( unkeep().isUnused() )
+            kill();
+    }
+
+
+    // Replace self with nnn in the graph, making 'this' go dead
+    public void subsume( Node nnn ) {
+        assert nnn!=this;
+        while( nOuts() > 0 ) {
+            Node n = _outputs.removeLast();
+            n.unlock();
+            int idx = n._inputs.find(this);
+            n._inputs.set(idx,nnn);
+            nnn.addUse(n);
+            CODE.addAll(n._outputs);
+        }
+        kill();
+    }
+
+    // Replace uses of `def` with `this`, and insert `this` immediately after
+    // `def` in the basic block.
+    public void insertAfter( Node def, boolean must ) {
+        CFGNode cfg = def.cfg0();
+        int i = cfg._outputs.find(def)+1;
+        if( cfg instanceof CallEndNode ) {
+            cfg = cfg.uctrl();  i=0;
+        } else if( def.in(0) instanceof MultiNode ) {
+            assert i==0;
+            i = cfg._outputs.find(def.in(0))+1;
+        }
+
+        while( cfg.out(i) instanceof PhiNode || cfg.out(i) instanceof CalleeSaveNode )  i++;
+        cfg._outputs.insert(this,i);
+        _inputs.set(0,cfg);
+        for( int j=def.nOuts()-1; j>=0; j-- ) {
+            // Can we avoid a split of a split?  'this' split is used by
+            // another split in the same block.
+            if( !must && def.out(j) instanceof SplitNode split && def.out(j).cfg0()==cfg &&
+                !split._kind.contains("self") )
+                continue;
+            Node use = def._outputs.del(j);
+            use.unlock();
+            int idx = use._inputs.find(def);
+            use._inputs.set(idx,this);
+            addUse(use);
+        }
+        if( nIns()>1 ) setDef(1,def);
+    }
+
+    // Insert this in front of use.in(uidx) with this, and insert this
+    // immediately before use in the basic block.
+    public void insertBefore( Node use, int uidx ) {
+        CFGNode cfg = use.cfg0();
+        int i;
+        if( use instanceof PhiNode phi ) {
+            cfg = phi.region().cfg(uidx);
+            i = cfg.nOuts()-1;
+        } else {
+            i = cfg._outputs.find(use);
+        }
+        cfg._outputs.insert(this,i);
+        _inputs.set(0,cfg);
+        if( _inputs._len > 1 && this instanceof SplitNode )
+            setDefOrdered(1,use.in(uidx));
+        use.setDefOrdered(uidx,this);
+    }
+
+    public void setDefOrdered(int idx, Node def) {
+        // If old is dying, remove from CFG ordered
+        Node old = in(idx);
+        if( old!=null && old.nOuts()==1 ) {
+            CFGNode cfg = old.cfg0();
+            if( cfg!=null ) {
+                cfg._outputs.remove(cfg._outputs.find(old));
+                old._inputs.set(0,null);
+            }
+        }
+        setDef(idx,def);
+    }
+
+    public void removeSplit() {
+        CFGNode cfg = cfg0();
+        cfg._outputs.remove(cfg._outputs.find(this));
+        _inputs.set(0,null);
+        if( _inputs._len > 1 ) subsume(in(1));
+    }
+
+    // ------------------------------------------------------------------------
+    // Graph-based optimizations
+
+    /**
+     * Try to peephole at this node and return a better replacement Node.
+     * Always returns some not-null Node (often this).
+     */
+    public final Node peephole( ) {
+//        if( _type==null )       // Brand-new node, never peeped before
+//            JSViewer.show();
+        Node n = peepholeOpt();
+//        if( n!=null )           // Made progress?
+//            JSViewer.show();    // Show again
+        return n==null ? this : deadCodeElim(n._nid >= _nid ? n.peephole() : n); // Cannot return null for no-progress
+    }
+
+    /**
+     * Try to peephole at this node and return a better replacement Node if
+     * possible.  We compute a {@link SONType} and then check and replace:
+     * <ul>
+     * <li>if the Type {@link SONType#isConstant}, we replace with a {@link ConstantNode}</li>
+     * <li>in a future chapter we will look for a
+     * <a href="https://en.wikipedia.org/wiki/Common_subexpression_elimination">Common Subexpression</a>
+     * to eliminate.</li>
+     * <li>we ask the Node for a better replacement.  The "better replacement"
+     * is things like {@code (1+2)} becomes {@code 3} and {@code (1+(x+2))} becomes
+     * {@code (x+(1+2))}.  By canonicalizing expressions we fold common addressing
+     * math constants, remove algebraic identities and generally simplify the
+     * code. </li>
+     *
+     * Unlike peephole above, this explicitly returns null for no-change, or not-null
+     * for a better replacement (which can be this).
+     * </ul>
+     */
+    public final Node peepholeOpt( ) {
+        CODE.iterCnt();
+        // Compute initial or improved Type
+        SONType old = setType(compute());
+
+        // 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).peephole();
+
+        // Global Value Numbering
+        if( _hash==0 ) {
+            Node n = CODE._gvn.get(this); // Will set _hash as a side effect
+            if( n==null )
+                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
+                // types are valid.  To preserve monotonicity, the resulting
+                // shared Node has to have the best of both types.
+                n.setType(n._type.join(_type));
+                _hash = 0; // Clear, since it never went in the table
+                return deadCodeElim(n);// Return previous; does Common Subexpression Elimination
+            }
+        }
+
+        // Ask each node for a better replacement
+        Node n = idealize();
+        if( n != null )         // Something changed
+            return n;           // Report progress
+
+        if( old!=_type ) return this; // Report progress;
+        CODE.iterNop();
+        return null;            // No progress
+    }
+
+    // m is the new Node, self is the old.
+    // Return 'm', which may have zero uses but is alive nonetheless.
+    // If self has zero uses (and is not 'm'), {@link #kill} self.
+    private Node deadCodeElim(Node m) {
+        // If self is going dead and not being returned here (Nodes returned
+        // from peephole commonly have no uses (yet)), then kill self.
+        if( m != this && isUnused() && !isDead() ) {
+            // Killing self - and since self recursively kills self's inputs we
+            // might end up killing 'm', which we are returning as a live Node.
+            // So we add a bogus extra null output edge to stop kill().
+            m.keep();      // Keep m alive
+            kill();        // Kill self because replacing with 'm'
+            m.unkeep();    // Okay to peephole m
+        }
+        return m;
+    }
+
+    /**
+     * This function needs to be
+     * <a href="https://en.wikipedia.org/wiki/Monotonic_function">Monotonic</a>
+     * as it is part of a Monotone Analysis Framework.
+     * <a href="https://www.cse.psu.edu/~gxt29/teaching/cse597s21/slides/08monotoneFramework.pdf">See for example this set of slides</a>.
+     * <p>
+     * For Chapter 2, all our Types are really integer constants, and so all
+     * the needed properties are trivially true, and we can ignore the high
+     * theory.  Much later on, this will become important and allow us to do
+     * many fancy complex optimizations trivially... because theory.
+     * <p>
+     * {@link #compute()} needs to be stand-alone, and cannot recursively call compute
+     * on its inputs, because programs are cyclic (have loops!) and this will just
+     * infinitely recurse until stack overflow.  Instead, compute typically
+     * computes a new type from the {@link #_type} field of its inputs.
+     */
+    public abstract SONType compute();
+
+    // Set the type.  Assert monotonic progress.
+    // If changing, add users to worklist.
+    public SONType setType(SONType type) {
+        SONType old = _type;
+        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
+        CODE.addAll(_outputs);
+        moveDepsToWorklist();
+        return old;
+    }
+
+    public <N extends Node> N init() { _type = compute(); return (N)this; }
+
+    /**
+     * This function rewrites the current Node into a more "idealized" form.
+     * This is the bulk of our peephole rewrite rules, and we use this to
+     * e.g. turn arbitrary collections of adds and multiplies with mixed
+     * constants into a normal form that's easy for hardware to implement.
+     * Example: An array addressing expression:
+     * <pre>   ary[idx+1]</pre>
+     * might turn into Sea-of-Nodes IR:
+     * <pre>   (ary+12)+((idx+1) * 4)</pre>
+     * This expression can then be idealized into:
+     * <pre>   ary + ((idx*4) + (12 + (1*4)))</pre>
+     * And more folding:
+     * <pre>   ary + ((idx<<2) + 16)</pre>
+     * And during code-gen:
+     * <pre>   MOV4 Rary,[Ridx<<2 +16] // or some such hardware-specific notation </pre>
+     * <p>
+     * {@link #idealize} has a very specific calling convention:
+     * <ul>
+     * <li>If NO change is made, return {@code null}
+     * <li>If ANY change is made, return not-null; this can be {@code this}
+     * <li>The returned Node does NOT call {@link #peephole} on itself; the {@link #peephole} call will recursively peephole it.
+     * <li>Any NEW nodes that are not directly returned DO call {@link #peephole}.
+     * </ul>
+     * <p>
+     * Examples:
+     * <table border="3">
+     * <tr><th>    before       </th><th>       after     </th><th>return </th><th>comment  </th></tr>
+     * <tr><td>{@code (x+5)    }</td><td>{@code   (x+5)  }</td><td>{@code null  }</td><td>No change</td></tr>
+     * <tr><td>{@code (5+x)    }</td><td>{@code   (x+5)  }</td><td>{@code this  }</td><td>Swapped arguments</td></tr>
+     * <tr><td>{@code ((x+1)+2)}</td><td>{@code (x+(1+2))}</td><td>{@code (x+_) }</td><td>Returns 2 new Nodes</td></tr>
+     * </table>
+     *
+     * The last entry deserves more discussion.  The new Node {@code (1+2)}
+     * created in {@link #idealize} calls {@link #peephole} (which then folds
+     * into a constant).  The other new Node {@code (x+3)} does not call
+     * peephole, because it is returned and peephole itself will recursively
+     * call peephole.
+     * <p>
+     * Since idealize calls peephole and peephole calls idealize, you must be
+     * careful that all idealizations are <em>monotonic</em>: all transforms
+     * remove some feature, so that the set of available transforms always
+     * shrinks.  If you don't, you risk an infinite peephole loop!
+     *
+     * @return Either a new or changed node, or null for no changes.
+     */
+    public abstract Node idealize();
+
+
+    // Some of the peephole rules get complex, and search further afield than
+    // just the nearest neighbor.  These peepholes can fail the pattern match
+    // on a node some distance away, and if that node ever changes we should
+    // retry the peephole.  Track a set of Nodes dependent on `this`, and
+    // revisit them if `this` changes.
+    Ary<Node> _deps;
+
+    /**
+     * Add a node to the list of dependencies.  Only add it if its not an input
+     * or output of this node, that is, it is at least one step away.  The node
+     * being added must benefit from this node being peepholed.
+     */
+    Node addDep( Node dep ) {
+        // Running peepholes during the big assert cannot have side effects
+        // like adding dependencies.
+        if( CODE._midAssert ) return this;
+        if( dep == null ) return this;
+        if( _deps==null ) _deps = new Ary<>(Node.class);
+        if( _deps   .find(dep) != -1 ) return this; // Already on list
+        if( _inputs .find(dep) != -1 ) return this; // No need for deps on immediate neighbors
+        if( _outputs.find(dep) != -1 ) return this;
+        _deps.add(dep);
+        return this;
+    }
+
+    // Move the dependents onto a worklist, and clear for future dependents.
+    public void moveDepsToWorklist( ) {
+        if( _deps==null ) return;
+        CODE.addAll(_deps);
+        _deps.clear();
+    }
+
+
+    // 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 ) {
+        if( o==this ) return true;
+        if( o.getClass() != getClass() ) return false;
+        Node n = (Node)o;
+        int len = _inputs.size();
+        if( len != n._inputs.size() ) return false;
+        for( int i=0; i<len; i++ )
+            if( in(i) != n.in(i) )
+                return false;
+        return eq(n);
+    }
+    // Subclasses add extra checks (such as ConstantNodes have same constant),
+    // and can assume "this!=n" and has the same Java class.
+    boolean eq( Node n ) { return true; }
+
+
+    // Cached hash.  If zero, then not computed AND this Node is NOT in the GVN
+    // table - and can have its edges hacked (which will change his hash
+    // anyway).  If Non-Zero then this Node is IN the GVN table, or is being
+    // probed to see if it can be inserted.  His edges are "locked", because
+    // hacking his edges will change his hash.
+    int _hash;
+
+    // If the _hash is set, then the Node is in the GVN table; remove it.
+    void unlock() {
+        if( _hash==0 ) return;
+        Node old = CODE._gvn.remove(this); // Pull from table
+        assert old==this;
+        _hash=0;                // Out of table now
+    }
+
+
+    // Hash of opcode and inputs
+    @Override public final int hashCode() {
+        if( _hash != 0 ) return _hash;
+        int hash = hash();
+        for( Node n : _inputs )
+            if( n != null )
+                hash = hash ^ (hash<<17) ^ (hash>>13) ^ n._nid;
+        if( hash==0 ) hash = 0xDEADBEEF; // Bad hash, so use some junky thing
+        return (_hash=hash);
+    }
+    // Subclasses add extra hash info (such as ConstantNodes constant)
+    int hash() { return 0; }
+
+    // ------------------------------------------------------------------------
+    //
+
+    /** Is this Node Memory related */
+    public boolean isMem() { return false; }
+
+    /** Pinned in the schedule; these are data nodes whose input#0 is not allowed to change */
+    public boolean isPinned() { return false; }
+
+    // Semantic change to the graph (so NOT a peephole), used by the Parser.
+    // If any input is a float, flip to a float-flavored opcode and widen any
+    // non-float input.
+    public final Node widen() {
+        if( !hasFloatInput() ) return this;
+        Node flt = copyF();
+        if( flt==null ) return this;
+        for( int i=1; i<nIns(); i++ )
+            flt.setDef(i, in(i)._type instanceof SONTypeFloat ? in(i) : new ToFloatNode(in(i)).peephole());
+        kill();
+        return flt;
+    }
+    private boolean hasFloatInput() {
+        for( int i=1; i<nIns(); i++ )
+            if( in(i)._type instanceof SONTypeFloat)
+                return true;
+        return false;
+    }
+    Node copyF() { return null; }
+
+
+    // ------------------------------------------------------------------------
+    // Peephole utilities
+
+    // Swap inputs without letting either input go dead during the swap.
+    public Node swap12() {
+        unlock();               // Hash is order dependent
+        _inputs.swap(1,2);
+        return this;
+    }
+
+    /**
+     * Does this node contain all constants?  Ignores in(0), as is usually
+     * control.  In an input is not a constant, we add dep as a dependency to
+     * it because dep can make progress if the input becomes a constant later.
+     * It is sufficient for one of the non-const inputs to have the dependency,
+     * so we don't bother checking the rest.
+     */
+    boolean allCons(Node dep) {
+        for( int i=1; i<nIns(); i++ )
+            if( !(in(i)._type.isConstant()) ) {
+                in(i).addDep(dep); // If in(i) becomes a constant later, will trigger some peephole
+                return false;
+            }
+        return true;
+    }
+
+
+    // Make a shallow copy (same class) of this Node, with given inputs and
+    // empty outputs and a new Node ID.  The original inputs are ignored.
+    // Does not need to be implemented in isCFG() nodes.
+    Node copy(Node lhs, Node rhs) { throw Utils.TODO("Binary ops need to implement copy"); }
+
+    // Report any post-optimize errors
+    public CompilerException err() { return null; }
+
+    // Common integer constants
+    public static ConstantNode con(long x) { return (ConstantNode)(new ConstantNode(SONTypeInteger.constant(x)).peephole()); }
+
+    // Utility to walk the entire graph applying a function; return the first
+    // not-null result.
+    final public <E> E walk( Function<Node,E> pred ) {
+        assert CODE._visit.isEmpty();
+        E rez = _walk(pred);
+        CODE._visit.clear();
+        return rez;
+    }
+
+    private <E> E _walk( Function<Node,E> pred ) {
+        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;
+        for( Node use : _outputs )  if( use != null && (x = use._walk(pred)) != null ) return x;
+        return null;
+    }
+
+    /**
+     * Debugging utility to find a Node by index
+     */
+    public Node find(int nid) { return _find(nid, new BitSet()); }
+    private Node _find(int nid, BitSet bs) {
+        if( bs.get(_nid) ) return null; // Been there, done that
+        bs.set(_nid);
+        if( _nid==nid ) return this;
+        Node x;
+        for( Node def : _inputs  )  if( def != null && (x = def._find(nid,bs)) != null ) return x;
+        for( Node use : _outputs )  if( use != null && (x = use._find(nid,bs)) != null ) return x;
+        return null;
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/NotNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/NotNode.java
new file mode 100644
index 0000000..5649814
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/NotNode.java
@@ -0,0 +1,31 @@
+package com.compilerprogramming.ezlang.compiler.nodes;
+
+import com.compilerprogramming.ezlang.compiler.sontypes.*;
+import java.util.BitSet;
+
+public class NotNode extends Node {
+    public NotNode(Node in) { super(null, in); }
+
+    @Override public String label() { return "Not"; }
+
+    @Override public String glabel() { return "!"; }
+
+    @Override
+    public StringBuilder _print1(StringBuilder sb, BitSet visited) {
+        in(1)._print0(sb.append("(!"), visited);
+        return sb.append(")");
+    }
+
+    @Override
+    public SONTypeInteger compute() {
+        SONType t0 = in(1)._type;
+        if( t0.isHigh() )  return SONTypeInteger.BOOL.dual();
+        if( t0 == SONType.NIL || t0 == SONTypeInteger.ZERO ) return SONTypeInteger.TRUE;
+        if( t0 instanceof SONTypeNil tn && tn.notNull() ) return SONTypeInteger.FALSE;
+        if( t0 instanceof SONTypeInteger i && (i._min > 0 || i._max < 0) ) return SONTypeInteger.FALSE;
+        return SONTypeInteger.BOOL;
+    }
+
+    @Override
+    public Node idealize() { return null; }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/OrNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/OrNode.java
new file mode 100644
index 0000000..fc76f23
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/OrNode.java
@@ -0,0 +1,53 @@
+package com.compilerprogramming.ezlang.compiler.nodes;
+
+import com.compilerprogramming.ezlang.compiler.Compiler;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeInteger;
+
+public class OrNode extends LogicalNode {
+    public OrNode(Node lhs, Node rhs) { super(lhs, rhs); }
+
+    @Override public String label() { return "Or"; }
+    @Override public String op() { return "|"; }
+
+    @Override public String glabel() { return "|"; }
+
+    @Override
+    public SONType compute() {
+        SONType t1 = in(1)._type, t2 = in(2)._type;
+        if( t1.isHigh() || t2.isHigh() )
+            return SONTypeInteger.TOP;
+        if( t1 instanceof SONTypeInteger i0 &&
+            t2 instanceof SONTypeInteger i1 ) {
+            if( i0.isConstant() && i1.isConstant() )
+                return SONTypeInteger.constant(i0.value()|i1.value());
+        }
+        return SONTypeInteger.BOT;
+    }
+
+    @Override
+    public Node idealize() {
+        Node lhs = in(1);
+        Node rhs = in(2);
+        SONType t1 = lhs._type;
+        SONType t2 = rhs._type;
+
+        // Or of 0.  We do not check for (0|x) because this will already
+        // canonicalize to (x|0)
+        if( t2.isConstant() && t2 instanceof SONTypeInteger i && i.value()==0 )
+            return lhs;
+
+        // Move constants to RHS: con*arg becomes arg*con
+        if ( t1.isConstant() && !t2.isConstant() )
+            return swap12();
+
+        // 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...)
+        Node phicon = AddNode.phiCon(this,true);
+        if( phicon!=null ) return phicon;
+
+        return null;
+    }
+    @Override Node copy(Node lhs, Node rhs) { return new OrNode(lhs,rhs); }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ParmNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ParmNode.java
new file mode 100644
index 0000000..b755949
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ParmNode.java
@@ -0,0 +1,47 @@
+package com.compilerprogramming.ezlang.compiler.nodes;
+
+import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
+import java.util.BitSet;
+
+public class ParmNode extends PhiNode {
+
+    // Argument indices are mapped one-to-one on CallNode inputs
+    public final int _idx;             // Argument index
+
+    public ParmNode(String label, int idx, SONType declaredType, Node... inputs) {
+        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); }
+
+    @Override public String glabel() { return _label; }
+
+    public FunNode fun() { return (FunNode)in(0); }
+
+    @Override
+    public StringBuilder _print1(StringBuilder sb, BitSet visited) {
+        if( "main".equals(fun()._name) && _label.equals("arg") )
+            return sb.append("arg");
+        sb.append("Parm_").append(_label).append("(");
+        for( Node in : _inputs ) {
+            if (in == null) sb.append("____");
+            else in._print0(sb, visited);
+            sb.append(",");
+        }
+        sb.setLength(sb.length()-1);
+        sb.append(")");
+        return sb;
+    }
+
+
+    @Override
+    public Node idealize() {
+        if( inProgress() ) return null;
+        return super.idealize();
+    }
+
+    // Always in-progress until we run out of unknown callers
+    @Override public boolean inProgress() { return fun().inProgress(); }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/PhiNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/PhiNode.java
new file mode 100644
index 0000000..5523b17
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/PhiNode.java
@@ -0,0 +1,205 @@
+package com.compilerprogramming.ezlang.compiler.nodes;
+
+import com.compilerprogramming.ezlang.compiler.sontypes.*;
+import com.compilerprogramming.ezlang.exceptions.CompilerException;
+
+import java.util.BitSet;
+
+public class PhiNode extends Node {
+
+    public final String _label;
+
+    // The Phi type we compute must stay within the domain of the Phi.
+    // Example Int stays Int, Ptr stays Ptr, Control stays Control, Mem stays Mem.
+    final SONType _declaredType;
+
+    public PhiNode(String label, SONType declaredType, Node... inputs) { super(inputs); _label = label;  assert declaredType!=null; _declaredType = declaredType; }
+    public PhiNode(PhiNode phi, String label, SONType 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(new Node[]{r});
+        _label = "";
+        _declaredType = sample._type;
+        while( nIns() < r.nIns() )
+            addDef(sample);
+    }
+
+    @Override public String label() { return "Phi_"+MemOpNode.mlabel(_label); }
+
+    @Override public String glabel() { return "&phi;_"+_label; }
+
+    @Override
+    public StringBuilder _print1(StringBuilder sb, BitSet visited) {
+        if( !(region() instanceof RegionNode r) || r.inProgress() )
+            sb.append("Z");
+        sb.append("Phi(");
+        for( Node in : _inputs ) {
+            if (in == null) sb.append("____");
+            else in._print0(sb, visited);
+            sb.append(",");
+        }
+        sb.setLength(sb.length()-1);
+        sb.append(")");
+        return sb;
+    }
+
+    public CFGNode region() { return (CFGNode)in(0); }
+    @Override public boolean isMem() { return _declaredType instanceof SONTypeMem; }
+    @Override public boolean isPinned() { return true; }
+
+    @Override
+    public SONType compute() {
+        if( !(region() instanceof RegionNode r) )
+            return region()._type== SONType.XCONTROL ? (_type instanceof SONTypeMem ? SONTypeMem.TOP : SONType.TOP) : _type;
+        // During parsing Phis have to be computed type pessimistically.
+        if( r.inProgress() ) return _declaredType;
+        // Set type to local top of the starting type
+        SONType t = _declaredType.glb().dual();//Type.TOP;
+        for (int i = 1; i < nIns(); i++)
+            // If the region's control input is live, add this as a dependency
+            // to the control because we can be peeped should it become dead.
+            if( r.in(i).addDep(this)._type != SONType.XCONTROL )
+                t = t.meet(in(i)._type);
+        return t;
+    }
+
+    @Override
+    public Node idealize() {
+        if( !(region() instanceof RegionNode r ) )
+            return in(1);       // Input has collapse to e.g. starting control.
+        if( r.inProgress() || r.nIns()<=1 )
+            return null;        // Input is in-progress
+
+        // If we have only a single unique input, become it.
+        Node live = singleUniqueInput();
+        if (live != null)
+            return live;
+
+        // No bother if region is going to fold dead paths soon
+        for( int i=1; i<nIns(); i++ )
+            if( r.in(i)._type == SONType.XCONTROL )
+                return null;
+
+        // Pull "down" a common data op.  One less op in the world.  One more
+        // Phi, but Phis do not make code.
+        //   Phi(op(A,B),op(Q,R),op(X,Y)) becomes
+        //     op(Phi(A,Q,X), Phi(B,R,Y)).
+        Node op = in(1);
+        if( !isMem() && op.nIns()==3 && op.in(0)==null && same_op() ) {
+            assert !(op instanceof CFGNode);
+            Node[] lhss = new Node[nIns()];
+            Node[] rhss = new Node[nIns()];
+            lhss[0] = rhss[0] = in(0); // Set Region
+            for( int i=1; i<nIns(); i++ ) {
+                lhss[i] = in(i).in(1);
+                rhss[i] = in(i).in(2);
+            }
+            Node phi_lhs = new PhiNode(_label, _declaredType,lhss).peephole();
+            Node phi_rhs = new PhiNode(_label, _declaredType,rhss).peephole();
+            Node down = op.copy(phi_lhs,phi_rhs);
+            // Test not running backwards, which can happen for e.g. And's
+            if( down.compute().isa(compute()) )
+                return down;
+            in(1).in(1).addDep(this);
+            in(1).in(2).addDep(this);
+            in(2).in(1).addDep(this);
+            in(2).in(2).addDep(this);
+            down.kill();
+        }
+
+        // If merging Phi(N, cast(N)) - we are losing the cast JOIN effects, so just remove.
+        if( nIns()==3 ) {
+            if( in(1) instanceof CastNode cast && cast.in(1).addDep(this)==in(2) ) return in(2);
+            if( in(2) instanceof CastNode cast && cast.in(1).addDep(this)==in(1) ) return in(1);
+        }
+        // If merging a null-checked null and the checked value, just use the value.
+        // if( val ) ..; phi(Region,False=0/null,True=val);
+        // then replace with plain val.
+        if( nIns()==3 ) {
+            int nullx = -1;
+            if( in(1)._type == in(1)._type.makeZero() ) nullx = 1;
+            if( in(2)._type == in(2)._type.makeZero() ) nullx = 2;
+            if( nullx != -1 ) {
+                Node val = in(3-nullx);
+                if( val instanceof CastNode cast )
+                    val = cast.in(1);
+                if( r.idom(this).addDep(this) instanceof IfNode iff && iff.pred().addDep(this)==val ) {
+                    // Must walk the idom on the null side to make sure we hit False.
+                    CFGNode idom = (CFGNode)r.in(nullx);
+                    while( idom != null && idom.nIns() > 0 && idom.in(0) != iff ) idom = idom.idom();
+                    if( idom instanceof CProjNode proj && proj._idx==1 )
+                        return val;
+                }
+            }
+        }
+
+        return null;
+    }
+
+    private boolean same_op() {
+        for( int i=2; i<nIns(); i++ )
+            if( in(1).getClass() != in(i).getClass() )
+                return false;
+        return true;
+    }
+
+    /**
+     * If only single unique input, return it
+     */
+    private Node singleUniqueInput() {
+        if( region() instanceof LoopNode loop && loop.entry()._type == SONType.XCONTROL )
+            return null;    // Dead entry loops just ignore and let the loop collapse
+        Node live = null;
+        for( int i=1; i<nIns(); i++ ) {
+            // If the region's control input is live, add this as a dependency
+            // to the control because we can be peeped should it become dead.
+            if( region().in(i).addDep(this)._type != SONType.XCONTROL && in(i) != this )
+                if( live == null || live == in(i) ) live = in(i);
+                else return null;
+        }
+        return live;
+    }
+
+    @Override
+    boolean allCons(Node dep) {
+        if( !(region() instanceof RegionNode r) ) return false;
+        // When the region completes (is no longer in progress) the Phi can
+        // become a "all constants" Phi, and the "dep" might make progress.
+        addDep(dep);
+        if( r.inProgress() ) return false;
+        return super.allCons(dep);
+    }
+
+    // True if last input is null
+    public boolean inProgress() {
+        return in(nIns()-1) == null;
+    }
+
+    // Never equal if inProgress
+    @Override boolean eq( Node n ) {
+        return !inProgress();
+    }
+
+    @Override
+    public CompilerException err() {
+        if( _type != SONType.BOTTOM ) return null;
+
+        // BOTTOM means we mixed e.g. int and ptr
+        for( int i=1; i<nIns(); i++ )
+            // Already an error, but better error messages come from elsewhere
+            if( in(i)._type == SONType.BOTTOM )
+                return null;
+
+        // Gather a minimal set of types that "cover" all the rest
+        boolean ti=false, tf=false, tp=false, tn=false;
+        for( int i=1; i<nIns(); i++ ) {
+            SONType t = in(i)._type;
+            ti |= t instanceof SONTypeInteger x;
+            tf |= t instanceof SONTypeFloat x;
+            tp |= t instanceof SONTypeMemPtr x;
+            tn |= t== SONType.NIL;
+        }
+        return ReturnNode.mixerr(ti,tf,tp,tn);
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ProjNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ProjNode.java
new file mode 100644
index 0000000..dcac4b4
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ProjNode.java
@@ -0,0 +1,54 @@
+package com.compilerprogramming.ezlang.compiler.nodes;
+
+import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeMem;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeTuple;
+
+import java.util.BitSet;
+
+public class ProjNode extends Node {
+
+    // Which slice of the incoming multipart value
+    public final int _idx;
+
+    // Debugging label
+    public final String _label;
+
+    public ProjNode(Node ctrl, int idx, String label) {
+        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 StringBuilder _print1(StringBuilder sb, BitSet visited) {
+        if( _label != null )  return sb.append(_label);
+        if( in(0) instanceof CallEndNode cend && cend.call()!=null )
+            return cend.call()._print0(sb,visited);
+        return sb.append("LONELY PROJ");
+    }
+
+    @Override public CFGNode cfg0() {
+        return in(0) instanceof CFGNode cfg ? cfg : in(0).cfg0();
+    }
+
+    @Override public boolean isMem() { return _type instanceof SONTypeMem; }
+    @Override public boolean isPinned() { return true; }
+
+    @Override
+    public SONType compute() {
+        SONType t = in(0)._type;
+        return t instanceof SONTypeTuple tt ? tt._types[_idx] : SONType.BOTTOM;
+    }
+
+    @Override public Node idealize() { return ((MultiNode)in(0)).pcopy(_idx); }
+
+    @Override
+    boolean eq( Node n ) { return _idx == ((ProjNode)n)._idx; }
+
+    @Override
+    int hash() { return _idx; }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ReadOnlyNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ReadOnlyNode.java
new file mode 100644
index 0000000..90b6a96
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ReadOnlyNode.java
@@ -0,0 +1,30 @@
+package com.compilerprogramming.ezlang.compiler.nodes;
+
+import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeMemPtr;
+
+import java.util.BitSet;
+
+/**
+ * Cast a pointer to read-only
+ */
+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
+    public StringBuilder _print1(StringBuilder sb, BitSet visited) {
+        return in(1)._print0(sb.append("(const)"),visited);
+    }
+    @Override public SONType compute() {
+        SONType t = in(1)._type;
+        return t instanceof SONTypeMemPtr tmp ? tmp.makeRO() : t;
+    }
+
+    @Override public Node idealize() {
+        if( in(1)._type instanceof SONTypeMemPtr tmp && tmp.isFinal() )
+            return in(1);
+        return null;
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/RegionNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/RegionNode.java
new file mode 100644
index 0000000..3bccfab
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/RegionNode.java
@@ -0,0 +1,123 @@
+package com.compilerprogramming.ezlang.compiler.nodes;
+
+import com.compilerprogramming.ezlang.compiler.Compiler;
+import com.compilerprogramming.ezlang.compiler.codegen.CodeGen;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
+import java.util.*;
+
+public class RegionNode extends CFGNode {
+
+    public RegionNode(Node...   nodes) { super(nodes); }
+    public RegionNode(RegionNode r) { super(r); }
+
+    @Override
+    public String label() { return "Region"; }
+
+    @Override
+    public StringBuilder _print1(StringBuilder sb, BitSet visited) {
+        return sb.append(label());
+    }
+
+    @Override public boolean blockHead() { return true; }
+
+    @Override
+    public SONType compute() {
+        if( inProgress() ) return SONType.CONTROL;
+        SONType t = SONType.XCONTROL;
+        for (int i = 1; i < nIns(); i++)
+            t = t.meet(in(i)._type);
+        return t;
+    }
+
+    @Override
+    public Node idealize() {
+        if( inProgress() ) return null;
+
+        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
+
+        // If a CFG diamond with no merging, delete: "if( pred ) {} else {};"
+        if( !hasPhi() &&         // No Phi users, just a control user
+            in(1) instanceof CProjNode p1 &&
+            in(2) instanceof CProjNode p2 &&
+            p1.in(0).addDep(this)==p2.in(0).addDep(this) &&
+            p1.in(0) instanceof IfNode iff ) {
+            // Replace with the iff.ctrl directly
+            if( nIns()==3 ) return iff.ctrl();
+            // Just delete the path for fat Regions
+            setDef(1,iff.ctrl());
+            return delDef(2);
+        }
+
+        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() ? Compiler.XCTRL : delDef(path);
+    }
+
+
+    private int findDeadInput() {
+        for( int i=1; i<nIns(); i++ )
+            if( in(i)._type== SONType.XCONTROL )
+                return i;
+        return 0;               // All inputs alive
+    }
+
+    boolean hasPhi() {
+        for( Node phi : _outputs )
+            if( phi instanceof PhiNode )
+                return true;
+        return false;
+    }
+
+    // Immediate dominator of Region is a little more complicated.
+    @Override public int idepth() {
+        if( _idepth!=0 ) return _idepth;
+        int d=0;
+        for( Node n : _inputs )
+            if( n!=null )
+                d = Math.max(d,((CFGNode)n).idepth()+1);
+        return _idepth=d;
+    }
+
+    @Override public CFGNode idom(Node dep) {
+        CFGNode lca = null;
+        // Walk the LHS & RHS idom trees in parallel until they match, or either fails.
+        // Because this does not cache, it can be linear in the size of the program.
+        for( int i=1; i<nIns(); i++ )
+            lca = cfg(i)._idom(lca,dep);
+        return lca;
+    }
+
+    // True if last input is null
+    public boolean inProgress() { return nIns()>1 && in(nIns()-1) == null; }
+
+    // Never equal if inProgress
+    @Override boolean eq( Node n ) { return !inProgress(); }
+
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ReturnNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ReturnNode.java
new file mode 100644
index 0000000..b44328e
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ReturnNode.java
@@ -0,0 +1,131 @@
+package com.compilerprogramming.ezlang.compiler.nodes;
+
+import com.compilerprogramming.ezlang.compiler.Compiler;
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.sontypes.*;
+import com.compilerprogramming.ezlang.exceptions.CompilerException;
+
+import java.util.BitSet;
+
+/**
+ * The Return node has two inputs.  The first input is a control node and the
+ * second is the data node that supplies the return value.
+ * <p>
+ * In this presentation, Return functions as a Stop node, since multiple <code>return</code> statements are not possible.
+ * The Stop node will be introduced in Chapter 6 when we implement <code>if</code> statements.
+ * <p>
+ * The Return's output is the value from the data node.
+ */
+public class ReturnNode extends CFGNode {
+
+    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); }
+    public Node expr() { return in(2); }
+    public Node rpc () { return in(3); }
+    public FunNode fun() { return _fun; }
+
+    @Override
+    public String label() { return "Return"; }
+
+    @Override
+    public StringBuilder _print1(StringBuilder sb, BitSet visited) {
+        sb.append("return ");
+        if( expr()==null ) sb.append("----");
+        else expr()._print0(sb, visited);
+        return sb.append(";");
+    }
+
+    // No one unique control follows; can be many call end sites
+    @Override public CFGNode uctrl() { return null; }
+
+    @Override
+    public SONType compute() {
+        if( inProgress () ) return SONTypeTuple.RET; // In progress
+        if( _fun.isDead() ) return SONTypeTuple.RET.dual(); // Dead another way
+        return SONTypeTuple.make(ctrl()._type,mem()._type,expr()._type);
+    }
+
+    @Override public Node idealize() {
+        if( inProgress () ) return null;
+        if( _fun.isDead() ) return null;
+
+//        // Upgrade signature based on return type
+//        SONType ret = expr()._type;
+//        SONTypeFunPtr fcn = _fun.sig();
+//        assert ret.isa(fcn.ret());
+//        if( ret != fcn.ret() )
+//            _fun.setSig(fcn.makeFrom(ret));
+
+        // If dead (cant be reached; infinite loop), kill the exit values
+        if( ctrl()._type== SONType.XCONTROL &&
+            !(mem() instanceof ConstantNode && expr() instanceof ConstantNode) ) {
+            Node top = new ConstantNode(SONType.TOP).peephole();
+            setDef(1,top);
+            setDef(2,top);
+            return this;
+        }
+
+        return null;
+    }
+
+    public boolean inProgress() {
+        return ctrl().getClass() == RegionNode.class && ((RegionNode)ctrl()).inProgress();
+    }
+
+    // Gather parse-time return types for error reporting
+    private SONType mt = SONType.TOP;
+    private boolean ti=false, tf=false, tp=false, tn=false;
+
+    // Add a return exit to the current parsing function
+    void addReturn( Node ctrl, Node rmem, Node expr ) {
+        assert inProgress();
+
+        // Gather parse-time return types for error reporting
+        SONType t = expr._type;
+        mt = mt.meet(t);
+        ti |= t instanceof SONTypeInteger x;
+        tf |= t instanceof SONTypeFloat x;
+        tp |= t instanceof SONTypeMemPtr x;
+        tn |= t== SONType.NIL;
+
+        // Merge path into the One True Return
+        RegionNode r = (RegionNode)ctrl();
+        // Assert that the Phis are in particular outputs; not reordered or shuffled
+        PhiNode mem = (PhiNode)r.out(0); assert mem._declaredType == SONTypeMem.BOT;
+        PhiNode rez = (PhiNode)r.out(1); assert rez._declaredType == SONType.BOTTOM;
+        // Pop "inProgress" null off
+        r  ._inputs.pop();
+        mem._inputs.pop();
+        rez._inputs.pop();
+        // Add new return point
+        r  .addDef(ctrl);
+        mem.addDef(rmem);
+        rez.addDef(expr);
+        // Back to being inProgress
+        r  .addDef(null);
+        mem.addDef(null);
+        rez.addDef(null);
+    }
+
+    @Override public CompilerException err() {
+        return expr()._type/*mt*/== SONType.BOTTOM ? mixerr(ti,tf,tp,tn) : null;
+    }
+
+    static CompilerException mixerr( boolean ti, boolean tf, boolean tp, boolean tn) {
+        if( !ti && !tf && !tp && !tn )
+            return Compiler.error("No defined return type");
+        SB sb = new SB().p("No common type amongst ");
+        if( ti ) sb.p("int and ");
+        if( tf ) sb.p("f64 and ");
+        if( tp || tn ) sb.p("reference and ");
+        return Compiler.error(sb.unchar(5).toString());
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/RoundF32Node.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/RoundF32Node.java
new file mode 100644
index 0000000..aa9a698
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/RoundF32Node.java
@@ -0,0 +1,38 @@
+package com.compilerprogramming.ezlang.compiler.nodes;
+
+import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFloat;
+
+import java.util.BitSet;
+
+public class RoundF32Node extends Node {
+    public RoundF32Node(Node lhs) { super(null, lhs); }
+
+    @Override public String label() { return "RoundF32"; }
+
+    @Override public String glabel() { return "(f32)"; }
+
+    @Override
+    public StringBuilder _print1(StringBuilder sb, BitSet visited) {
+        return in(1)._print0(sb.append("((f32)"), visited).append(")");
+    }
+
+    @Override
+    public SONType compute() {
+        if (in(1)._type instanceof SONTypeFloat i0 && i0.isConstant() )
+            return SONTypeFloat.constant((float)i0.value());
+        return in(1)._type;
+    }
+
+    @Override
+    public Node idealize() {
+        Node lhs = in(1);
+        SONType t1 = lhs._type;
+
+        // RoundF32 of float
+        if( t1 instanceof SONTypeFloat tf && tf._sz==32 )
+            return lhs;
+
+        return null;
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/SarNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/SarNode.java
new file mode 100644
index 0000000..1a823dd
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/SarNode.java
@@ -0,0 +1,48 @@
+package com.compilerprogramming.ezlang.compiler.nodes;
+
+import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeInteger;
+
+public class SarNode extends LogicalNode {
+    public SarNode(Node lhs, Node rhs) { super(lhs, rhs); }
+
+    @Override public String label() { return "Sar"; }
+    @Override public String op() { return ">>"; }
+
+    @Override public String glabel() { return "&gt;&gt;"; }
+
+    @Override
+    public SONType compute() {
+        SONType t1 = in(1)._type, t2 = in(2)._type;
+        if( t1.isHigh() || t2.isHigh() )
+            return SONTypeInteger.TOP;
+        if (t1 instanceof SONTypeInteger i0 &&
+            t2 instanceof SONTypeInteger i1) {
+            if( i0 == SONTypeInteger.ZERO )
+                return SONTypeInteger.ZERO;
+            if( i0.isConstant() && i1.isConstant() )
+                return SONTypeInteger.constant(i0.value()>>i1.value());
+            if( i1.isConstant() ) {
+                int log = (int)i1.value();
+                return SONTypeInteger.make(-1L<<(63-log),(1L<<(63-log))-1);
+            }
+        }
+        return SONTypeInteger.BOT;
+    }
+
+    @Override
+    public Node idealize() {
+        Node lhs = in(1);
+        Node rhs = in(2);
+        SONType t2 = rhs._type;
+
+        // Sar of 0.
+        if( t2.isConstant() && t2 instanceof SONTypeInteger i && (i.value()&63)==0 )
+            return lhs;
+
+        // TODO: x >> 3 >> (y ? 1 : 2) ==> x >> (y ? 4 : 5)
+
+        return null;
+    }
+    @Override Node copy(Node lhs, Node rhs) { return new SarNode(lhs,rhs); }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ScopeNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ScopeNode.java
new file mode 100644
index 0000000..c3109cc
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ScopeNode.java
@@ -0,0 +1,429 @@
+package com.compilerprogramming.ezlang.compiler.nodes;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.CodeGen;
+import com.compilerprogramming.ezlang.compiler.sontypes.*;
+import java.util.*;
+
+/**
+ * The Scope node is purely a parser helper - it tracks names to nodes with a
+ * stack of variables.
+ */
+public class ScopeNode extends MemMergeNode {
+
+    /**
+     * The control is a name that binds to the currently active control
+     * node in the graph
+     */
+    public static final String CTRL = "$ctrl";
+    public static final String ARG0 = "arg";
+    public static final String MEM0 = "$mem";
+
+    // All active/live variables in all nested scopes, all run together
+    public final Ary<Var> _vars;
+
+    // Size of each nested lexical scope
+    public final Ary<Integer> _lexSize;
+
+    // Lexical scope is one of normal Block, constructor or function
+    public enum Kind { Block, Constructor, Function };
+    public final Ary<Kind> _kinds;
+
+    // Extra guards; tested predicates and casted results
+    private final Ary<Node> _guards;
+
+    // A new ScopeNode
+    public ScopeNode() {
+        super(true);
+        _vars   = new Ary<>(Var    .class);
+        _lexSize= new Ary<>(Integer.class);
+        _kinds  = new Ary<>(Kind   .class);
+        _guards = new Ary<>(Node   .class);
+    }
+
+    @Override public String label() { return "Scope"; }
+
+    @Override
+    public StringBuilder _print1(StringBuilder sb, BitSet visited) {
+        sb.append("Scope[ ");
+        int j=1;
+        for( int i=0; i<nIns(); i++ ) {
+            if( j < _lexSize._len && i == _lexSize.at(j) ) { sb.append("| "); j++; }
+            Var v = _vars.get(i);
+            sb.append(v.type().print(new SB()));
+            sb.append(" ");
+            if( v._final ) sb.append("!");
+            sb.append(v._name);
+            sb.append("=");
+            Node n = in(i);
+            while( n instanceof ScopeNode loop ) {
+                sb.append("Lazy_");
+                n = loop.in(i);
+            }
+            if( n==null ) sb.append("___");
+            else n._print0(sb, visited);
+            sb.append(", ");
+        }
+        sb.setLength(sb.length()-2);
+        return sb.append("]");
+    }
+
+
+    public Node ctrl() { return in(0); }
+    public MemMergeNode mem() { return (MemMergeNode)in(1); }
+
+    /**
+     * The ctrl of a ScopeNode is always bound to the currently active
+     * control node in the graph, via a special name '$ctrl' that is not
+     * a valid identifier in the language grammar and hence cannot be
+     * referenced in Simple code.
+     *
+     * @param n The node to be bound to '$ctrl'
+     *
+     * @return Node that was bound
+     */
+    public <N extends Node> N ctrl(N n) { return setDef(0,n); }
+    public Node mem(Node n) { return setDef(1,n); }
+
+    public void push(Kind kind) {
+        assert _lexSize._len==_kinds._len;
+        _lexSize.push(_vars.size());
+        _kinds  .push(kind);
+    }
+
+    // Pop a lexical scope
+    public void pop() {
+        assert _lexSize._len==_kinds._len;
+        promote();
+        int n = _lexSize.pop();
+        _kinds.pop();
+        popUntil(n);
+        _vars.setLen(n);
+    }
+
+
+    // Look for forward references in the last lexical scope and promote to the
+    // next outer lexical scope.  At the last scope declare them an error.
+    public void promote() {
+        int n = _lexSize.last();
+        for( int i=n; i<nIns(); i++ ) {
+            Var v = _vars.at(i);
+            if( !v.isFRef() ) continue;
+            if( _lexSize._len==1 )
+                throw Compiler.error("Undefined name '" + v._name + "'");
+            _vars.swap(n,i);
+            _inputs.swap(n,i);
+            v._idx = n;
+            n++;
+            _lexSize.set(_lexSize._len-1,n);
+        }
+    }
+
+
+    public boolean inCon() { return _kinds.last() == Kind.Constructor; }
+
+    // Is v outside any current function scope?
+    public boolean outOfFunction( Var v ) {
+        for( int i=_lexSize._len-1; i>=0 && v._idx<_lexSize.at(i); i-- )
+            if( _kinds.at(i)==Kind.Function )
+                return true;
+        return false;
+    }
+
+
+    // Find name in reverse, return an index into _vars or -1.  Linear scan
+    // instead of hashtable, but probably doesn't matter until the scan
+    // typically hits many dozens of variables.
+    int find( String name ) {
+        for( int i=_vars.size()-1; i>=0; i-- )
+            if( _vars.at(i)._name.equals(name) )
+                return i;
+        return -1;
+    }
+
+    /**
+     * Create a new variable name in the current scope
+     */
+    public boolean define(String name, SONType declaredType, boolean xfinal, Node init) {
+        assert _lexSize.isEmpty() || name.charAt(0)!='$' ; // Later scopes do not define memory
+        if( _lexSize._len > 0 )
+            for( int i=_vars.size()-1; i>=_lexSize.last(); i-- ) {
+                Var n = _vars.at(i);
+                if( n._name.equals(name) ) {
+                    if( !n.isFRef() ) return false;       // Double define
+                    FRefNode fref = (FRefNode)in(n._idx); // Get forward ref
+                    if( !xfinal || !declaredType.isConstant() ) throw fref.err();  // Must be a final constant
+                    n.defFRef(declaredType,xfinal);   // Declare full correct type, final, source location
+                    setDef(n._idx,fref.addDef(init));     // Set FRef to defined; tell parser also
+                }
+            }
+        Var v = new Var(nIns(),name,declaredType,xfinal,init==Compiler.XCTRL);
+        _vars.add(v);
+        // Creating a forward reference
+        if( init==Compiler.XCTRL )
+            init = new FRefNode(v).init();
+        addDef(init);
+        return true;
+    }
+
+    // Read from memory
+    public Node mem( int alias ) { return mem()._mem(alias,null); }
+    // Write to memory
+    public void mem( int alias, Node st ) { mem()._mem(alias,st); }
+
+
+    /**
+     * Lookup a name in all scopes starting from most deeply nested.
+     *
+     * @param name Name to be looked up
+     * @return null if not found, or the implementing Node
+     */
+    public Var lookup( String name ) {
+        int idx = find(name);
+        // -1 is missed in all scopes, not found
+        return idx == -1 ? null : update(_vars.at(idx),null);
+    }
+
+    /**
+     * If the name is present in any scope, then redefine else null
+     *
+     * @param name Name being redefined
+     * @param n    The node to bind to the name
+     */
+    public void update( String name, Node n ) {
+        int idx = find(name);
+        assert idx>=0;
+        update(_vars.at(idx),n);
+    }
+
+    public Var update( Var v, Node st ) {
+        Node old = in(v._idx);
+        if( old instanceof ScopeNode loop ) {
+            // Lazy Phi!
+            Node def = loop.in(v._idx);
+            old = def instanceof PhiNode phi && loop.ctrl()==phi.region()
+                // Loop already has a real Phi, use it
+                ? def
+                // Set real Phi in the loop head
+                // The phi takes its one input (no backedge yet) from a recursive
+                // lookup, which might have insert a Phi in every loop nest.
+                : loop.setDef(v._idx,new PhiNode(v._name, v.lazyGLB(), loop.ctrl(), loop.in(loop.update(v,null)._idx),null).peephole());
+            setDef(v._idx,old);
+        }
+        assert !v._final || st==null;
+        if( st!=null ) setDef(v._idx,st); // Set new value
+        return v;
+    }
+
+    /**
+     * Duplicate a ScopeNode; including all levels, up to Nodes.  So this is
+     * neither shallow (would dup the Scope but not the internal HashMap
+     * tables), nor deep (would dup the Scope, the HashMap tables, but then
+     * also the program Nodes).
+     * <p>
+     * If the {@code loop} flag is set, the edges are filled in as the original
+     * Scope, as an indication of Lazy Phis at loop heads.  The goal here is to
+     * not make Phis at loop heads for variables which are never touched in the
+     * loop body.
+     * <p>
+     * The new Scope is a full-fledged Node with proper use<->def edges.
+     */
+    public ScopeNode dup() { return dup(false); }
+    public ScopeNode dup(boolean loop) {
+        ScopeNode dup = new ScopeNode();
+        // Our goals are:
+        // 1) duplicate the name bindings of the ScopeNode across all stack levels
+        // 2) Make the new ScopeNode a user of all the nodes bound
+        // 3) Ensure that the order of defs is the same to allow easy merging
+        dup._vars   .addAll(_vars   );
+        dup._lexSize.addAll(_lexSize);
+        dup._kinds  .addAll(_kinds  );
+        dup._guards .addAll(_guards );
+        // The dup'd guards all need dup'd keepers, to keep proper accounting
+        // when later removing all guards
+        for( Node n : _guards )
+            if( !(n instanceof CFGNode) )
+                n.keep();
+        dup.addDef(ctrl());     // Control input is just copied
+
+        // Memory input is a shallow copy
+        MemMergeNode memdup = new MemMergeNode(true), mem = mem();
+        memdup.addDef(null);
+        memdup.addDef(loop ? this : mem.in(1));
+        for( int i=2; i<mem.nIns(); i++ )
+            // For lazy phis on loops we use a sentinel
+            // that will trigger phi creation on update
+            memdup.addDef(loop ? this : mem.in(i));
+        dup.addDef(memdup);
+
+        // Copy of other inputs
+        for( int i=2; i<nIns(); i++ )
+            // For lazy phis on loops we use a sentinel
+            // that will trigger phi creation on update
+            dup.addDef(loop && !_vars.at(i)._final ? this : in(i));
+        return dup;
+    }
+
+    /**
+     * Merges the names whose node bindings differ, by creating Phi node for such names
+     * The names could occur at all stack levels, but a given name can only differ in the
+     * innermost stack level where the name is bound.
+     *
+     * @param that The ScopeNode to be merged into this
+     * @return A new node representing the merge point
+     */
+    public RegionNode mergeScopes(ScopeNode that) {
+        RegionNode r = ctrl(new RegionNode(null,ctrl(), that.ctrl()).keep());
+        mem()._merge(that.mem(),r);
+        this ._merge(that      ,r);
+        that.kill();            // Kill merged scope
+        CodeGen.CODE.add(r);
+        return r.unkeep();
+    }
+
+    private void _merge(ScopeNode that, RegionNode r) {
+        for( int i = 2; i < nIns(); i++)
+            if( in(i) != that.in(i) ) { // No need for redundant Phis
+                // If we are in lazy phi mode we need to a lookup
+                // by name as it will trigger a phi creation
+                Var v = _vars.at(i);
+                Node lhs = this.in(this.update(v,null));
+                Node rhs = that.in(that.update(v,null));
+                setDef(i, new PhiNode(v._name, v.type(), r, lhs, rhs).peephole());
+            }
+    }
+
+    // Balance arms of an IF.  Extra lonely defs are thrown: "if(pred) int x;".
+    // Forward refs are copied to the other side, "as if" they were there all along.
+    public void balanceIf( ScopeNode scope ) {
+        for( int i = nIns(); i < scope.nIns(); i++ ) {
+            Var n = scope._vars.at(i);
+            if( n.isFRef() ) {  // RHS has forward refs
+                _vars.add(n);   // Copy to LHS
+                addDef(scope.in(i));
+            } else
+                throw Compiler.error("Cannot define a '"+n._name+"' on one arm of an if");
+        }
+    }
+
+
+    // peephole the backedge scope into this loop head scope
+    // We set the second input to the phi from the back edge (i.e. loop body)
+    public void endLoop(ScopeNode back, ScopeNode exit ) {
+        Node ctrl = ctrl();
+        assert ctrl instanceof LoopNode loop && loop.inProgress();
+        ctrl.setDef(2,back.ctrl());
+
+        mem()._endLoopMem( this, back.mem(), exit.mem() );
+        this ._endLoop   ( this, back      , exit       );
+        back.kill();            // Loop backedge is dead
+        // Now one-time do a useless-phi removal
+        mem()._useless();
+        this ._useless();
+
+        // The exit mem's lazy default value had been the loop top,
+        // now it goes back to predating the loop.
+        exit.mem().setDef(1,mem().in(1));
+    }
+
+    // Fill in the backedge of any inserted Phis
+    void _endLoop( ScopeNode scope, Node back, Node exit ) {
+        for( int i=2; i<nIns(); i++ ) {
+            if( _vars.at(i)._final ) continue; // Final vars did not get modified in the loop
+            if( _vars.at(i).type().isHighOrConst() ) continue; // Cannot lift higher than a constant, so no Phi
+            if( back.in(i) != scope ) {
+                PhiNode phi = (PhiNode)in(i);
+                assert phi.region()==scope.ctrl() && phi.in(2)==null;
+                phi.setDef(2,back.in(i)); // Fill backedge
+            }
+            if( exit.in(i) == scope ) // Replace a lazy-phi on the exit path also
+                exit.setDef(i,in(i));
+        }
+    }
+
+
+    // Up-casting: using the results of an If to improve a value.
+    // E.g. "if( ptr ) ptr.field;" is legal because ptr is known not-null.
+    public void addGuards( Node ctrl, Node pred, boolean invert ) {
+        assert ctrl instanceof CFGNode;
+        _guards.add(ctrl);      // Marker to distinguish 0,1,2 guards
+        // add pred & its cast to the normal input list, with special Vars
+        if( pred==null || pred.isDead() )
+            return;           // Dead, do not add any guards
+        // Invert the If conditional
+        if( invert )
+            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.
+        SONType tnz = pred._type.nonZero();
+        if( tnz!=null )
+            _addGuard(tnz,ctrl,pred);
+
+        // Compute the negative test type.
+        if( pred instanceof NotNode not ) {
+            Node npred = not.in(1);
+            SONType tzero = npred._type.makeZero();
+            _addGuard(tzero,ctrl,npred);
+        }
+    }
+
+    private void _addGuard(SONType guard, Node ctrl, Node pred) {
+        SONType tcast = guard.join(pred._type);
+        if( tcast != pred._type && !tcast.isHigh() ) {
+            Node cast = new CastNode(tcast,ctrl,pred.keep()).peephole().keep();
+            _guards.add(pred);
+            _guards.add(cast);
+            replace(pred,cast);
+        }
+    }
+
+
+    // Remove matching pred/cast pairs from this guarded region.
+    public ScopeNode removeGuards( Node ctrl ) {
+        assert ctrl instanceof CFGNode;
+        // 0,1 or 2 guards
+        while( true ) {
+            Node g = _guards.pop();
+            if( g == ctrl ) break;
+            if( g instanceof CFGNode ) continue;
+            g            .unkill(); // Pop/kill cast
+            _guards.pop().unkill(); // Pop/kill pred
+        }
+        return this;
+    }
+
+    // If we find a guarded instance of pred, replace with the upcasted version
+    public Node upcastGuard( Node pred ) {
+        // If finding an instanceof pred, replace with cast.
+        // Otherwise, just pred itself.
+        int i = _guards._len;
+        while( i > 0 ) {
+            Node  cast = _guards.at(--i);
+            if( cast instanceof CFGNode ) continue; // Marker between guard sets
+            Node xpred = _guards.at(--i);
+            if( xpred == pred )
+                return cast;
+        }
+        return pred;
+    }
+
+    // Kill guards also
+    @Override public void kill() {
+        for( Node n : _guards )
+            if( !(n instanceof CFGNode) )
+                n.unkill();
+        _guards.clear();
+        // Can have lazy uses remaining
+        if( isUnused() )
+            super.kill();
+    }
+
+
+    private void replace( Node old, Node cast ) {
+        assert old!=null && old!=cast;
+        for( int i=0; i<nIns(); i++ )
+            if( in(i)==old )
+                setDef(i,cast);
+    }
+
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ShlNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ShlNode.java
new file mode 100644
index 0000000..072f468
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ShlNode.java
@@ -0,0 +1,52 @@
+package com.compilerprogramming.ezlang.compiler.nodes;
+
+import com.compilerprogramming.ezlang.compiler.Compiler;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeInteger;
+
+public class ShlNode extends LogicalNode {
+    public ShlNode(Node lhs, Node rhs) { super(lhs, rhs); }
+
+    @Override public String label() { return "Shl"; }
+    @Override public String op() { return "<<"; }
+
+    @Override public String glabel() { return "&lt;&lt;"; }
+
+    @Override
+    public SONType compute() {
+        SONType t1 = in(1)._type, t2 = in(2)._type;
+        if( t1.isHigh() || t2.isHigh() )
+            return SONTypeInteger.TOP;
+        if (t1 instanceof SONTypeInteger i0 &&
+            t2 instanceof SONTypeInteger i1 ) {
+            if( i0 == SONTypeInteger.ZERO )
+                return SONTypeInteger.ZERO;
+            if( i0.isConstant() && i1.isConstant() )
+                return SONTypeInteger.constant(i0.value()<<i1.value());
+        }
+        return SONTypeInteger.BOT;
+    }
+
+    @Override
+    public Node idealize() {
+        Node lhs = in(1);
+        Node rhs = in(2);
+
+        if( rhs._type instanceof SONTypeInteger shl && shl.isConstant() ) {
+            // Shl of 0.
+            if( (shl.value()&63)==0 )
+                return lhs;
+            // (x + c) << i  =>  (x << i) + (c << i)
+            if( lhs instanceof AddNode add && add.addDep(this).in(2)._type instanceof SONTypeInteger c && c.isConstant() ) {
+                long sum = c.value() << shl.value();
+                if( Integer.MIN_VALUE <= sum  && sum <= Integer.MAX_VALUE )
+                    return new AddNode(new ShlNode(add.in(1),rhs).peephole(), Compiler.con(sum) );
+            }
+        }
+
+        // TODO: x << 3 << (y ? 1 : 2) ==> x << (y ? 4 : 5)
+
+        return null;
+    }
+    @Override Node copy(Node lhs, Node rhs) { return new ShlNode(lhs,rhs); }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ShrNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ShrNode.java
new file mode 100644
index 0000000..c4b2a9b
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ShrNode.java
@@ -0,0 +1,45 @@
+package com.compilerprogramming.ezlang.compiler.nodes;
+
+import com.compilerprogramming.ezlang.compiler.Compiler;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeInteger;
+
+public class ShrNode extends LogicalNode {
+    public ShrNode(Node lhs, Node rhs) { super(lhs, rhs); }
+
+    @Override public String label() { return "Shr"; }
+    @Override public String op() { return ">>>"; }
+
+    @Override public String glabel() { return "&gt;&gt;&gt;"; }
+
+    @Override
+    public SONType compute() {
+        SONType t1 = in(1)._type, t2 = in(2)._type;
+        if( t1.isHigh() || t2.isHigh() )
+            return SONTypeInteger.TOP;
+        if (t1 instanceof SONTypeInteger i0 &&
+            t2 instanceof SONTypeInteger i1 ) {
+            if( i0 == SONTypeInteger.ZERO )
+                return SONTypeInteger.ZERO;
+            if( i0.isConstant() && i1.isConstant() )
+                return SONTypeInteger.constant(i0.value()>>>i1.value());
+        }
+        return SONTypeInteger.BOT;
+    }
+
+    @Override
+    public Node idealize() {
+        Node lhs = in(1);
+        Node rhs = in(2);
+        SONType t2 = rhs._type;
+
+        // Shr of 0.
+        if( t2.isConstant() && t2 instanceof SONTypeInteger i && (i.value()&63)==0 )
+            return lhs;
+
+        // TODO: x >>> 3 >>> (y ? 1 : 2) ==> x >>> (y ? 4 : 5)
+
+        return null;
+    }
+    @Override Node copy(Node lhs, Node rhs) { return new ShrNode(lhs,rhs); }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/SplitNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/SplitNode.java
new file mode 100644
index 0000000..9a980ec
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/SplitNode.java
@@ -0,0 +1,22 @@
+package com.compilerprogramming.ezlang.compiler.nodes;
+
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.codegen.CodeGen;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
+import java.util.BitSet;
+
+public abstract class SplitNode extends MachConcreteNode {
+    public final String _kind;  // Kind of split
+    public final byte _round;
+    public SplitNode(String kind, byte round, Node[] nodes) { super(nodes); _kind = kind; _round = round; }
+    @Override public String op() { return "mov"; }
+    @Override public StringBuilder _print1(StringBuilder sb, BitSet visited) {
+        return in(1)._print0(sb.append("mov("),visited).append(")");
+    }
+    @Override public SONType compute() { return in(0)._type; }
+    @Override public Node idealize() { return null; }
+    @Override public void asm(CodeGen code, SB sb) {
+        sb.p(code.reg(this)).p(" = ").p(code.reg(in(1)));
+    }
+    @Override public String comment() { return _kind + " #"+ _round; }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StartNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StartNode.java
new file mode 100644
index 0000000..ac9053f
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StartNode.java
@@ -0,0 +1,57 @@
+package com.compilerprogramming.ezlang.compiler.nodes;
+
+import com.compilerprogramming.ezlang.compiler.codegen.CodeGen;
+import com.compilerprogramming.ezlang.compiler.Compiler;
+import com.compilerprogramming.ezlang.compiler.sontypes.*;
+
+import java.util.BitSet;
+
+import static com.compilerprogramming.ezlang.compiler.Utils.TODO;
+
+/**
+ * The Start node represents the start of the function.
+ *
+ * Start initially has 1 input (arg) from outside and the initial control.
+ * In ch10 we also add mem aliases as structs get defined; each field in struct
+ * adds a distinct alias to Start's tuple.
+ */
+public class StartNode extends LoopNode implements MultiNode {
+
+    final SONType _arg;
+
+    public StartNode(SONType arg) { super((Node)null); _arg = arg; _type = compute(); }
+    public StartNode(StartNode start) { super(start); _arg = start==null ? null : start._arg; }
+
+    @Override public String label() { return "Start"; }
+
+    @Override
+    public StringBuilder _print1(StringBuilder sb, BitSet visited) {
+      return sb.append(label());
+    }
+
+    @Override public boolean blockHead() { return true; }
+    @Override public CFGNode cfg0() { return null; }
+
+    // Get the one control following; error to call with more than one e.g. an
+    // IfNode or other multi-way branch.  For Start, its "main"
+    @Override public CFGNode uctrl() {
+        // Find "main", its the start.
+        CFGNode C = null;
+        for( Node use : _outputs )
+            if( use instanceof FunNode fun && fun.sig().isa(CodeGen.CODE._main) )
+                { assert C==null; C = fun; }
+        return C;
+    }
+
+
+    @Override public SONTypeTuple compute() {
+        return SONTypeTuple.make(SONType.CONTROL, SONTypeMem.TOP,_arg);
+    }
+
+    @Override public Node idealize() { return null; }
+
+    // No immediate dominator, and idepth==0
+    @Override public int idepth() { return 0; }
+    @Override public CFGNode idom(Node dep) { return null; }
+
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StopNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StopNode.java
new file mode 100644
index 0000000..1d13a7f
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StopNode.java
@@ -0,0 +1,63 @@
+package com.compilerprogramming.ezlang.compiler.nodes;
+
+import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
+import java.util.BitSet;
+
+public class StopNode extends CFGNode {
+
+    public final String _src;
+
+    public StopNode(String src) {
+        super();
+        _src = src;
+        _type = compute();
+    }
+    public StopNode(StopNode stop) { super(stop);  _src = stop==null ? null : stop._src; }
+
+    @Override
+    public String label() {
+        return "Stop";
+    }
+
+    @Override
+    public StringBuilder _print1(StringBuilder sb, BitSet visited) {
+        // For the sake of many old tests, and single value prints as "return val"
+        if( ret()!=null ) return ret()._print0(sb,visited);
+        sb.append("Stop[ ");
+        for( Node ret : _inputs )
+            ret._print0(sb, visited).append(" ");
+        return sb.append("]");
+    }
+
+    @Override public boolean blockHead() { return true; }
+
+
+    // If a single Return, return it.
+    // Otherwise, null because ambiguous.
+    public ReturnNode ret() {
+        return nIns()==1 && in(0) instanceof ReturnNode ret ? ret : null;
+    }
+
+    @Override
+    public SONType compute() {
+        return SONType.BOTTOM;
+    }
+
+    @Override
+    public Node idealize() {
+        int len = nIns();
+        for( int i=0; i<nIns(); i++ )
+            if( ((ReturnNode)in(i)).fun().isDead() )
+                delDef(i--);
+        if( len != nIns() ) return this;
+        return null;
+    }
+
+    @Override public int idepth() {
+        if( _idepth!=0 ) return _idepth;
+        int d=0;
+        for( Node ret : _inputs )
+            d = Math.max(d,((ReturnNode)ret).idepth()+1);
+        return _idepth=d;
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StoreNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StoreNode.java
new file mode 100644
index 0000000..ca4a712
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StoreNode.java
@@ -0,0 +1,115 @@
+package com.compilerprogramming.ezlang.compiler.nodes;
+
+import com.compilerprogramming.ezlang.compiler.Compiler;
+import com.compilerprogramming.ezlang.compiler.sontypes.*;
+import com.compilerprogramming.ezlang.exceptions.CompilerException;
+
+import java.util.BitSet;
+
+/**
+ * Store represents setting a value to a memory based object, in chapter 10
+ * this means a field inside a struct.
+ */
+public class StoreNode extends MemOpNode {
+
+    private final boolean _init; // Initializing writes are allowed to write null
+
+    /**
+     * @param name  The struct field we are assigning to
+     * @param mem   The memory alias node - this is updated after a Store
+     * @param ptr   The ptr to the struct base where we will store a value
+     * @param off   The offset inside the struct base
+     * @param value Value to be stored
+     */
+    public StoreNode(String name, int alias, SONType glb, Node mem, Node ptr, Node off, Node value, boolean init) {
+        super(name, alias, glb, mem, ptr, off, value);
+        _init = init;
+    }
+
+    // GraphVis DOT code and debugger labels
+    @Override public String  label() { return "st_"+mlabel(); }
+    // GraphVis node-internal labels
+    @Override public String glabel() { return "." +_name+"="; }
+    @Override public boolean isMem() { return true; }
+
+    public Node val() { return in(4); }
+
+    @Override
+    public StringBuilder _print1(StringBuilder sb, BitSet visited) {
+        return sb.append(".").append(_name).append("=").append( val()).append(";");
+    }
+
+    @Override
+    public SONType compute() {
+        SONType val = val()._type;
+        SONTypeMem mem = (SONTypeMem)mem()._type; // Invariant
+        if( mem == SONTypeMem.TOP ) return SONTypeMem.TOP;
+        SONType t = SONType.BOTTOM;               // No idea on field contents
+        // Same alias, lift val to the declared type and then meet into other fields
+        if( mem._alias == _alias ) {
+            // Update declared forward ref to the actual
+            if( _declaredType.isFRef() && val instanceof SONTypeMemPtr tmp && !tmp.isFRef() )
+                _declaredType = tmp;
+            val = val.join(_declaredType);
+            t = val.meet(mem._t);
+        }
+        return SONTypeMem.make(_alias,t);
+    }
+
+    @Override
+    public Node idealize() {
+
+        // Simple store-after-store on same address.  Should pick up the
+        // required init-store being stomped by a first user store.
+        if( mem() instanceof StoreNode st &&
+            ptr()==st.ptr() &&  // Must check same object
+            off()==st.off() &&  // And same offset (could be "same alias" but this handles arrays to same index)
+            ptr()._type instanceof SONTypeMemPtr && // No bother if weird dead pointers
+            // Must have exactly one use of "this" or you get weird
+            // non-serializable memory effects in the worse case.
+            checkOnlyUse(st) ) {
+            assert _name.equals(st._name); // Equiv class aliasing is perfect
+            setDef(1,st.mem());
+            return this;
+        }
+
+        // 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 = ((SONTypeInteger)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;
+    }
+
+    // Check that "mem" has no uses except "this"
+    private boolean checkOnlyUse(Node mem) {
+        if( mem.nOuts()==1 ) return true;
+        // Add deps on the other uses (can be e.g. ScopeNode mid-parse) so that
+        // when the other uses go away we can retry.
+        for( Node use : mem._outputs )
+            if( use != this )
+                use.addDep(this);
+        return false;
+    }
+
+    @Override
+    public CompilerException err() {
+        CompilerException err = super.err();
+        if( err != null ) return err;
+        SONTypeMemPtr tmp = (SONTypeMemPtr)ptr()._type;
+        if( tmp._obj.field(_name)._final && !_init )
+            return Compiler.error("Cannot modify final field '"+_name+"'");
+        SONType t = val()._type;
+        //return _init || t.isa(_declaredType) ? null : Parser.error("Cannot store "+t+" into field "+_declaredType+" "+_name,_loc);
+        return null;
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StructNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StructNode.java
new file mode 100644
index 0000000..2d0779e
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StructNode.java
@@ -0,0 +1,46 @@
+package com.compilerprogramming.ezlang.compiler.nodes;
+
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.sontypes.*;
+import java.util.BitSet;
+
+/**
+ * Build a compound object
+ */
+public class StructNode extends Node {
+
+    public SONTypeStruct _ts;
+
+    @Override public String label() { return _ts==null ? "STRUCT?" : _ts.str(); }
+
+    @Override
+    public StringBuilder _print1(StringBuilder sb, BitSet visited) {
+        if( _ts==null ) return sb.append("STRUCT?");
+        sb.append(_ts._name).append(" {");
+        for( int i=0; i<nIns(); i++ ) {
+            sb.append(_ts._fields[i]._fname).append(":");
+            sb.append((in(i)==null ? SONType.BOTTOM : in(i)._type).print(new SB()));
+            sb.append("; ");
+        }
+        sb.setLength(sb.length()-2);
+        return sb.append("}");
+    }
+
+    @Override
+    public SONTypeStruct compute() {
+        if( _ts==null ) return SONTypeStruct.BOT;
+        Field[] fs = new Field[_ts._fields.length];
+        for( int i=0; i<fs.length; i++ )
+            fs[i] = _ts._fields[i].makeFrom(in(i)==null ? SONType.TOP : in(i)._type);
+        return SONTypeStruct.make(_ts._name,fs);
+    }
+
+    @Override
+    public Node idealize() { return null; }
+
+    @Override
+    boolean eq(Node n) { return _ts == ((StructNode)n)._ts; }
+
+    @Override
+    int hash() { return _ts==null ? 0 : _ts.hashCode(); }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/SubFNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/SubFNode.java
new file mode 100644
index 0000000..371fe5b
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/SubFNode.java
@@ -0,0 +1,44 @@
+package com.compilerprogramming.ezlang.compiler.nodes;
+
+import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFloat;
+
+import java.util.BitSet;
+
+public class SubFNode extends Node {
+    public SubFNode(Node lhs, Node rhs) { super(null, lhs, rhs); }
+
+    @Override public String label() { return "SubF"; }
+
+    @Override public String glabel() { return "-"; }
+
+    @Override
+    public StringBuilder _print1(StringBuilder sb, BitSet visited) {
+        in(1)._print0(sb.append("("), visited);
+        in(2)._print0(sb.append("-"), visited);
+        return sb.append(")");
+    }
+
+    @Override
+    public SONType compute() {
+        if (in(1)._type instanceof SONTypeFloat i0 &&
+            in(2)._type instanceof SONTypeFloat i1) {
+            if (i0.isConstant() && i1.isConstant())
+                return SONTypeFloat.constant(i0.value()-i1.value());
+        }
+        return in(1)._type.meet(in(2)._type);
+    }
+
+    @Override
+    public Node idealize() {
+        Node lhs = in(1);
+        SONType t2 = in(2)._type;
+
+        // Sub of 0.
+        if ( t2.isConstant() && t2 instanceof SONTypeFloat i && i.value()==0 )
+            return lhs;
+
+        return null;
+    }
+    @Override Node copy(Node lhs, Node rhs) { return new SubFNode(lhs,rhs); }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/SubNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/SubNode.java
new file mode 100644
index 0000000..e779260
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/SubNode.java
@@ -0,0 +1,58 @@
+package com.compilerprogramming.ezlang.compiler.nodes;
+
+import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeInteger;
+import java.util.BitSet;
+
+public class SubNode extends Node {
+    public SubNode(Node lhs, Node rhs) { super(null, lhs, rhs); }
+
+    @Override public String label() { return "Sub"; }
+
+    @Override public String glabel() { return "-"; }
+
+    @Override
+    public StringBuilder _print1(StringBuilder sb, BitSet visited) {
+        in(1)._print0(sb.append("("), visited);
+        in(2)._print0(sb.append("-"), visited);
+        return sb.append(")");
+    }
+
+    @Override
+    public SONType compute() {
+        SONType t1 = in(1)._type, t2 = in(2)._type;
+        if( t1.isHigh() || t2.isHigh() )
+            return SONTypeInteger.TOP;
+        // Sub of same is 0
+        if( in(1)==in(2) )
+            return SONTypeInteger.ZERO;
+        if( t1 instanceof SONTypeInteger i1 &&
+            t2 instanceof SONTypeInteger i2 ) {
+            if (i1.isConstant() && i2.isConstant())
+                return SONTypeInteger.constant(i1.value()-i2.value());
+            // Fold ranges like {2-3} - {0-1} into {1-3}.
+            if( !AddNode.overflow(i1._min,-i2._max) &&
+                !AddNode.overflow(i1._max,-i2._min) &&
+                i2._min != Long.MIN_VALUE  )
+                return SONTypeInteger.make(i1._min-i2._max,i1._max-i2._min);
+        }
+
+        return SONTypeInteger.BOT;
+    }
+
+    @Override
+    public Node idealize() {
+        // x - (-y) is x+y
+        if( in(2) instanceof MinusNode minus )
+            return new AddNode(in(1),minus.in(1));
+
+        // (-x) - y is -(x+y)
+        if( in(1) instanceof MinusNode minus )
+            return new MinusNode(new AddNode(minus.in(1),in(2)).peephole());
+
+        return null;
+    }
+
+    @Override Node copy(Node lhs, Node rhs) { return new SubNode(lhs,rhs); }
+    @Override Node copyF() { return new SubFNode(null,null); }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ToFloatNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ToFloatNode.java
new file mode 100644
index 0000000..439f25f
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ToFloatNode.java
@@ -0,0 +1,34 @@
+package com.compilerprogramming.ezlang.compiler.nodes;
+
+import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFloat;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeInteger;
+
+import java.util.BitSet;
+
+public class ToFloatNode extends Node {
+    public ToFloatNode(Node lhs) { super(null, lhs); }
+
+    @Override public String label() { return "ToFloat"; }
+
+    @Override public String glabel() { return "(flt)"; }
+
+    @Override
+    public StringBuilder _print1(StringBuilder sb, BitSet visited) {
+        return in(1)._print0(sb.append("(flt)"), visited);
+    }
+
+    @Override
+    public SONType compute() {
+        if( in(1)._type == SONType.NIL ) return SONTypeFloat.FZERO;
+        if (in(1)._type instanceof SONTypeInteger i0 ) {
+            if( i0.isHigh() ) return SONTypeFloat.F64.dual();
+            if( i0.isConstant() )
+                return SONTypeFloat.constant(i0.value());
+        }
+        return SONTypeFloat.F64;
+    }
+
+    @Override public Node idealize() { return null; }
+    @Override Node copy(Node lhs, Node rhs) { return new ToFloatNode(lhs); }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/XCtrlNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/XCtrlNode.java
new file mode 100644
index 0000000..94fd704
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/XCtrlNode.java
@@ -0,0 +1,14 @@
+package com.compilerprogramming.ezlang.compiler.nodes;
+
+import com.compilerprogramming.ezlang.compiler.codegen.CodeGen;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
+import java.util.BitSet;
+
+public class XCtrlNode extends CFGNode {
+    public XCtrlNode() { super(new Node[]{CodeGen.CODE._start}); }
+    @Override public String label() { return "Xctrl"; }
+    @Override public StringBuilder _print1(StringBuilder sb, BitSet visited) { return sb.append("Xctrl"); }
+    @Override public boolean isConst() { return true; }
+    @Override  public SONType compute() { return SONType.XCONTROL; }
+    @Override public Node idealize() { return null; }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/XorNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/XorNode.java
new file mode 100644
index 0000000..1840851
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/XorNode.java
@@ -0,0 +1,52 @@
+package com.compilerprogramming.ezlang.compiler.nodes;
+
+import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeInteger;
+
+public class XorNode extends LogicalNode {
+    public XorNode(Node lhs, Node rhs) { super(lhs, rhs); }
+
+    @Override public String label() { return "Xor"; }
+    @Override public String op() { return "^"; }
+
+    @Override public String glabel() { return "^"; }
+
+    @Override
+    public SONType compute() {
+        SONType t1 = in(1)._type, t2 = in(2)._type;
+        if( t1.isHigh() || t2.isHigh() )
+            return SONTypeInteger.TOP;
+        if( t1 instanceof SONTypeInteger i0 &&
+            t2 instanceof SONTypeInteger i1 ) {
+            if( i0.isConstant() && i1.isConstant() )
+                return SONTypeInteger.constant(i0.value()^i1.value());
+        }
+        return SONTypeInteger.BOT;
+    }
+
+    @Override
+    public Node idealize() {
+        Node lhs = in(1);
+        Node rhs = in(2);
+        SONType t1 = lhs._type;
+        SONType t2 = rhs._type;
+
+        // Xor of 0.  We do not check for (0^x) because this will already
+        // canonicalize to (x^0)
+        if( t2.isConstant() && t2 instanceof SONTypeInteger i && i.value()==0 )
+            return lhs;
+
+        // Move constants to RHS: con*arg becomes arg*con
+        if ( t1.isConstant() && !t2.isConstant() )
+            return swap12();
+
+        // 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...)
+        Node phicon = AddNode.phiCon(this,true);
+        if( phicon!=null ) return phicon;
+
+        return null;
+    }
+    @Override Node copy(Node lhs, Node rhs) { return new XorNode(lhs,rhs); }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AddARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AddARM.java
new file mode 100644
index 0000000..d3ad62d
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AddARM.java
@@ -0,0 +1,20 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.arm;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+public class AddARM extends MachConcreteNode implements MachNode {
+    AddARM( Node add) { super(add); }
+    @Override public String op() { return "add"; }
+    @Override public RegMask regmap(int i) { return arm.RMASK; }
+    @Override public RegMask outregmap() { return arm.RMASK; }
+
+    // ADD (shifted register)
+    @Override public void encoding( Encoding enc ) { arm.r_reg(enc,this,0b10001011); }
+    // 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)));
+    }
+
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AddFARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AddFARM.java
new file mode 100644
index 0000000..7517d8f
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AddFARM.java
@@ -0,0 +1,22 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.arm;
+
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+public class AddFARM extends MachConcreteNode implements MachNode {
+    AddFARM(Node addf) { super(addf); }
+    @Override public String op() { return "addf"; }
+    @Override public RegMask regmap(int i) { return arm.DMASK; }
+    @Override public RegMask outregmap() { return arm.DMASK; }
+
+    //FADD (scalar)
+    @Override public void encoding( Encoding enc ) { arm.f_scalar(enc,this,0b00011110); }
+
+    // Default on double precision for now(64 bits)
+    // General form: "addf  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)));
+    }
+
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AddIARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AddIARM.java
new file mode 100644
index 0000000..ef16d9f
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AddIARM.java
@@ -0,0 +1,27 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.arm;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+public class AddIARM extends MachConcreteNode implements MachNode {
+    final int _imm;
+    AddIARM(Node add, int imm) {
+        super(add);
+        _inputs.pop();
+        _imm = imm;
+    }
+    @Override public String op() {
+        return _imm == 1 ? "inc" : (_imm == -1 ? "dec" : "addi");
+    }
+    @Override public RegMask regmap(int i) { return arm.RMASK; }
+    @Override public RegMask outregmap() { return arm.RMASK; }
+    //ADD (immediate)
+    @Override public void encoding( Encoding enc ) {
+        arm.imm_inst(enc,this,0b1001000100,_imm);
+    }
+    // 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(" + #").p(_imm);
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AndARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AndARM.java
new file mode 100644
index 0000000..903a307
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AndARM.java
@@ -0,0 +1,21 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.arm;
+
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.MachConcreteNode;
+import com.compilerprogramming.ezlang.compiler.nodes.MachNode;
+import com.compilerprogramming.ezlang.compiler.nodes.Node;
+
+public class AndARM extends MachConcreteNode implements MachNode {
+    AndARM(Node and) { super(and); }
+    @Override public String op() { return "and"; }
+    @Override public String glabel() { return "&"; }
+    @Override public RegMask regmap(int i) { return arm.RMASK; }
+    @Override public RegMask outregmap() { return arm.RMASK; }
+    // AND (shifted register)
+    @Override public void encoding( Encoding enc ) { arm.r_reg(enc,this,0b10001010); }
+    // 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)));
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AndIARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AndIARM.java
new file mode 100644
index 0000000..2412999
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AndIARM.java
@@ -0,0 +1,24 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.arm;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+public class AndIARM extends MachConcreteNode implements MachNode {
+    final int _imm;
+    AndIARM(Node and, int imm) {
+        super(and);
+        _inputs.pop();
+        _imm = imm;
+    }
+    @Override public String op() { return "andi"; }
+    @Override public RegMask regmap(int i) { return arm.RMASK; }
+    @Override public RegMask outregmap() { return arm.RMASK; }
+    @Override public void encoding( Encoding enc ) {
+        arm.imm_inst_n(enc,this,0b100100100,_imm);
+    }
+    // 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(" & #").p(_imm);
+    }
+}
\ No newline at end of file
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AsrARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AsrARM.java
new file mode 100644
index 0000000..4711932
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AsrARM.java
@@ -0,0 +1,18 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.arm;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+// Arithmetic Shift Right (register)
+public class AsrARM extends MachConcreteNode implements MachNode {
+    AsrARM(Node asr) {super(asr);}
+    @Override public String op() { return "sar"; }
+    @Override public RegMask regmap(int i) { return arm.RMASK; }
+    @Override public RegMask outregmap() { return arm.RMASK; }
+    @Override public void encoding( Encoding enc ) { arm.shift_reg(enc,this,0b1010); }
+    @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)));
+    }
+
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AsrIARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AsrIARM.java
new file mode 100644
index 0000000..05a508c
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AsrIARM.java
@@ -0,0 +1,31 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.arm;
+
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.codegen.CodeGen;
+import com.compilerprogramming.ezlang.compiler.codegen.Encoding;
+import com.compilerprogramming.ezlang.compiler.codegen.RegMask;
+import com.compilerprogramming.ezlang.compiler.nodes.MachConcreteNode;
+import com.compilerprogramming.ezlang.compiler.nodes.MachNode;
+import com.compilerprogramming.ezlang.compiler.nodes.Node;
+
+public class AsrIARM extends MachConcreteNode implements MachNode {
+    final int _imm;
+    AsrIARM(Node asr, int imm) {
+        super(asr);
+        _inputs.pop();
+        _imm = imm;
+    }
+    @Override public String op() { return "asri"; }
+    @Override public RegMask regmap(int i) { return arm.RMASK; }
+    @Override public RegMask outregmap() { return arm.RMASK; }
+    @Override public void encoding( Encoding enc ) {
+        short rd = enc.reg(this);
+        short rn = enc.reg(in(1));
+        assert _imm > 0;
+        enc.add4(arm.imm_shift(0b1001001101,_imm, 0b111111, rn, rd));
+    }
+    // General form: "asri  rd = rs1 >> imm"
+    @Override public void asm(CodeGen code, SB sb) {
+        sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" >> #").p(_imm);
+    }
+}
\ No newline at end of file
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/BranchARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/BranchARM.java
new file mode 100644
index 0000000..12ce1c2
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/BranchARM.java
@@ -0,0 +1,65 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.arm;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+// Jump on flags, uses flags
+public class BranchARM extends IfNode implements MachNode, RIPRelSize {
+    String _bop;
+    BranchARM(IfNode iff, String bop ) {
+        super(iff);
+        _bop = bop;
+    }
+    @Override public String op() { return "j"+_bop; }
+    @Override public String label() { return op(); }
+
+    @Override public void postSelect(CodeGen code) {
+        Node set = in(1);
+        Node cmp = set.in(1);
+        // Bypass an expected Set and just reference the cmp directly
+        if( set instanceof SetARM)
+            _inputs.set(1,cmp);
+        else
+            throw Utils.TODO();
+    }
+
+    @Override public RegMask regmap(int i) { assert i==1; return arm.FLAGS_MASK; }
+    @Override public RegMask outregmap() { return null; }
+    @Override public void invert() { _bop = invert(_bop);  }
+
+    // Encoding is appended into the byte array; size is returned
+    @Override public void encoding( Encoding enc ) {
+        // Assuming that condition flags are already set.  These flags are set
+        // by comparison (or sub).  No need for regs because it uses flags
+        enc.jump(this,cproj(0));
+        // B.cond
+        enc.add4( arm.b_cond(0b01010100, 0, arm.make_condition(_bop)) );
+    }
+
+    // Delta is from opcode start
+    @Override public byte encSize(int delta) {
+        if( -(1<<19) <= delta && delta < (1<<19) ) return 4;
+        // 2 word encoding needs a tmp register, must teach RA
+        throw Utils.TODO();
+    }
+
+    // Delta is from opcode start
+    @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) {
+        if( opLen==4 ) {
+            enc.patch4(opStart,arm.b_cond(0b01010100, delta, arm.make_condition(_bop)));
+        } else {
+            throw Utils.TODO();
+        }
+    }
+
+    @Override public void asm(CodeGen code, SB sb) {
+        String src = code.reg(in(1));
+        if( src!="flags" ) sb.p(src).p(" ");
+        CFGNode prj = cproj(0);
+        while( prj.nOuts() == 1 )
+            prj = prj.uctrl();  // Skip empty blocks
+        sb.p(label(prj));
+    }
+    @Override public String comment() { return "L"+cproj(1)._nid; }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CallARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CallARM.java
new file mode 100644
index 0000000..d16a25f
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CallARM.java
@@ -0,0 +1,47 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.arm;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFunPtr;
+
+public class CallARM extends CallNode implements MachNode, RIPRelSize {
+    final SONTypeFunPtr _tfp;
+    final String _name;
+
+    CallARM(CallNode call, SONTypeFunPtr tfp) {
+        super(call);
+        _inputs.pop(); // Pop constant target
+        assert tfp.isConstant();
+        _tfp = tfp;
+        _name = CodeGen.CODE.link(tfp)._name;
+    }
+
+    @Override public String op() { return "call"; }
+    @Override public String label() { return op(); }
+    @Override public String name() { return _name; }
+    @Override public SONTypeFunPtr tfp() { return _tfp; }
+    @Override public RegMask regmap(int i) { return arm.callInMask(_tfp,i); }
+    @Override public RegMask outregmap() { return null; }
+
+    @Override public void encoding( Encoding enc ) {
+        enc.relo(this);
+        // BL
+        enc.add4(arm.b(0b100101,0)); // Target patched at link time
+    }
+
+    // Delta is from opcode start, but X86 measures from the end of the 5-byte encoding
+    @Override public byte encSize(int delta) { return 4; }
+
+    // Delta is from opcode start
+    @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) {
+        enc.patch4(opStart,arm.b(0b100101,delta));
+    }
+
+    @Override public void asm(CodeGen code, SB sb) {
+        sb.p(_name);
+        for( int i=0; i<nargs(); i++ )
+            sb.p(code.reg(arg(i+2))).p("  ");
+        sb.unchar(2);
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CallEndARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CallEndARM.java
new file mode 100644
index 0000000..af4d96c
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CallEndARM.java
@@ -0,0 +1,24 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.arm;
+
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.CallEndNode;
+import com.compilerprogramming.ezlang.compiler.nodes.MachNode;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFunPtr;
+
+public class CallEndARM extends CallEndNode implements MachNode {
+    final SONTypeFunPtr _tfp;
+    CallEndARM( CallEndNode cend ) {
+        super(cend);
+        _tfp = (SONTypeFunPtr)(cend.call().fptr()._type);
+    }
+
+    @Override public String op() { return "cend"; }
+    @Override public String label() { return op(); }
+    @Override public RegMask regmap(int i) { return null; }
+    @Override public RegMask outregmap() { return null; }
+    @Override public RegMask outregmap(int idx) { return idx == 2 ? arm.retMask(_tfp,2) : null; }
+    @Override public RegMask killmap() { return arm.armCallerSave(); }
+    @Override public void encoding( Encoding enc ) { }
+    @Override public void asm(CodeGen code, SB sb) {  }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CallRRARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CallRRARM.java
new file mode 100644
index 0000000..a8cb51b
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CallRRARM.java
@@ -0,0 +1,32 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.arm;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+public class CallRRARM extends CallNode implements MachNode {
+    CallRRARM(CallNode call) { super(call); }
+    @Override public String op() { return "callr"; }
+    @Override public String label() { return op(); }
+    @Override public RegMask regmap(int i) {
+        return i==_inputs._len
+            ? arm.RMASK                // Function call target
+            : arm.callInMask(tfp(),i); // Normal argument
+    }
+    @Override public RegMask outregmap() { return null; }
+
+    @Override public void encoding( Encoding enc ) {
+        // Needs a register, typically a jump-and-link-register opcode
+        // blr
+        short self = enc.reg(this);
+        enc.add4(arm.blr(0b1101011000111111000000, self));
+    }
+
+    @Override public void asm(CodeGen code, SB sb) {
+        sb.p(code.reg(fptr())).p("  ");
+        for( int i=0; i<nargs(); i++ )
+            sb.p(code.reg(arg(i))).p("  ");
+        sb.unchar(2);
+    }
+
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CmpARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CmpARM.java
new file mode 100644
index 0000000..5e2366b
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CmpARM.java
@@ -0,0 +1,22 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.arm;
+
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+public class CmpARM extends MachConcreteNode implements MachNode {
+    CmpARM( Node cmp ) { super(cmp); }
+    @Override public String op() { return "cmp"; }
+    @Override public RegMask regmap(int i) { return arm.RMASK; }
+    @Override public RegMask outregmap() { return arm.FLAGS_MASK; }
+
+    // SUBS (shifted register)
+    @Override public void encoding( Encoding enc ) { arm.r_reg(enc,this,0b11101011); }
+
+    // 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)));
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CmpFARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CmpFARM.java
new file mode 100644
index 0000000..ad01bfe
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CmpFARM.java
@@ -0,0 +1,22 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.arm;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+// compare instruction on float regs input(D-registers)
+public class CmpFARM extends MachConcreteNode implements MachNode{
+    CmpFARM(Node cmp) {super(cmp);}
+    @Override public String op() { return "cmpf"; }
+    @Override public RegMask regmap(int i) { return arm.DMASK; }
+    @Override public RegMask outregmap() { return arm.FLAGS_MASK; }
+
+    // FCMP
+    @Override public void encoding( Encoding enc ) { arm.f_cmp(enc,this); }
+    // General form: "cmp  d0, d1"
+    @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)));
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CmpIARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CmpIARM.java
new file mode 100644
index 0000000..af1fccc
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CmpIARM.java
@@ -0,0 +1,40 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.arm;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+// Compare with immediate.
+// Conditional compare (immediate)? e.g CCMP(immediate)
+public class CmpIARM extends MachConcreteNode implements MachNode {
+    final int _imm;
+    final String _bop;
+    CmpIARM(BoolNode bool, int imm) {
+        super(bool);
+        _inputs.pop();
+        _bop = bool.op();
+        _imm = imm;
+    }
+    CmpIARM( CmpIARM cmp ) {
+        super((Node)null);
+        addDef(null);
+        addDef(cmp.in(1));
+        _bop = cmp._bop;
+        _imm = cmp._imm;
+    }
+    @Override public String op() { return _imm==0 ? "test" : "cmp"; }
+    @Override public RegMask regmap(int i) { return arm.RMASK; }
+    @Override public RegMask outregmap() { return arm.FLAGS_MASK; }
+    @Override public boolean isClone() { return true; }
+    @Override public Node copy() { return new CmpIARM(this); }
+    // Encoding is appended into the byte array; size is returned
+    @Override public void encoding( Encoding enc ) { arm.imm_inst(enc,this,964,_imm); }
+
+    // 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);
+    }
+ }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/DivARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/DivARM.java
new file mode 100644
index 0000000..16deea1
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/DivARM.java
@@ -0,0 +1,19 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.arm;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+public class DivARM extends MachConcreteNode implements MachNode {
+    DivARM( Node div ) { super(div); }
+    @Override public String op() { return "div"; }
+    @Override public RegMask regmap(int i) { return arm.RMASK; }
+    @Override public RegMask outregmap() { return arm.RMASK; }
+    // SDIV
+    @Override public void encoding( Encoding enc ) { arm.madd(enc,this,0b10011010110,3); }
+    // 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)));
+    }
+
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/DivFARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/DivFARM.java
new file mode 100644
index 0000000..fa550cb
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/DivFARM.java
@@ -0,0 +1,19 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.arm;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+public class DivFARM extends MachConcreteNode implements MachNode {
+    DivFARM( Node divf) { super(divf); }
+    @Override public String op() { return "divf"; }
+    @Override public RegMask regmap(int i) { return arm.DMASK; }
+    @Override public RegMask outregmap() {  return arm.DMASK; }
+
+    // FDIV (scalar)
+    @Override public void encoding( Encoding enc ) { arm.f_scalar(enc,this,0b000110); }
+    // General form: "VDIF =  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)));
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/FloatARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/FloatARM.java
new file mode 100644
index 0000000..c56d88e
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/FloatARM.java
@@ -0,0 +1,45 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.arm;
+
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.Utils;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.ConstantNode;
+import com.compilerprogramming.ezlang.compiler.nodes.MachNode;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFloat;
+
+//FMOV (scalar, immediate)
+//Floating-point move immediate.
+public class FloatARM extends ConstantNode implements MachNode, RIPRelSize {
+    FloatARM(ConstantNode con) { super(con); }
+    @Override public String op() { return "ld8"; }
+    @Override public RegMask regmap(int i) { return null; }
+    @Override public RegMask outregmap() { return arm.DMASK; }
+    @Override public boolean isClone() { return true; }
+    @Override public FloatARM copy() { return new FloatARM(this); }
+
+    @Override public void encoding( Encoding enc ) {
+        enc.largeConstant(this,_con);
+        short dst = (short)(enc.reg(this) - arm.D_OFFSET);
+        double d = ((SONTypeFloat)_con).value();
+        long x = Double.doubleToRawLongBits(d);
+        enc.add4(arm.load_pc(0b01011100, 0, dst));
+    }
+
+    // Delta is from opcode start.
+    @Override public byte encSize(int delta) { return 4;  }
+
+    // Delta is from opcode start
+    @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) {
+        short dst = (short)(enc.reg(this) - arm.D_OFFSET);
+        enc.add4(arm.load_pc(0b01011100, delta, dst));
+    }
+
+    // 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(" = #"));
+    }
+
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/FunARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/FunARM.java
new file mode 100644
index 0000000..96fc6cc
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/FunARM.java
@@ -0,0 +1,36 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.arm;
+
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.nodes.FunNode;
+import com.compilerprogramming.ezlang.compiler.nodes.MachNode;
+
+public class FunARM  extends FunNode implements MachNode {
+    FunARM(FunNode fun) { super(fun); }
+    @Override public String op() { return "addi"; }
+    @Override public RegMask regmap(int i) { return null; }
+    @Override public RegMask outregmap() { return null; }
+    @Override public void postSelect(CodeGen code) { code.link(this);  }
+
+    // Actual stack layout is up to each CPU.
+    // X86, with too many args & spills:
+    // | CALLER |
+    // |  argN  | // slot 1, required by callER
+    // +--------+
+    // |  RPC   | // slot 0, required by callER
+    // | callee | // slot 3, callEE
+    // | callee | // slot 2, callEE
+    // |  PAD16 |
+    // +--------+
+    @Override public void encoding( Encoding enc ) {
+        // Stack frame size: _maxSlot - max(arg), padded to 16.
+        _maxArgSlot = arm.maxSlot(enc._fun.sig());
+        _frameAdjust = (short) (_maxSlot+1 - _maxArgSlot);
+        if( _frameAdjust == 0 ) return; // Skip if no frame adjust
+        enc.add4(arm.imm_inst(0b1001000100, (_frameAdjust*8)&0xFFF, arm.RSP, arm.RSP));
+    }
+
+    @Override public void asm(CodeGen code, SB sb) {
+        sb.p("rsp += #").p(_frameAdjust*8);
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/I2F8ARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/I2F8ARM.java
new file mode 100644
index 0000000..ef24c5a
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/I2F8ARM.java
@@ -0,0 +1,25 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.arm;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+public class I2F8ARM extends MachConcreteNode implements MachNode {
+    I2F8ARM(Node i2f8) { super(i2f8); }
+    @Override public String op() { return "cvt"; }
+    @Override public RegMask regmap(int i) { return arm.RMASK; }
+    @Override public RegMask outregmap() { return arm.DMASK; }
+    @Override public void encoding( Encoding enc ) {
+        // SCVTF
+        short self = (short)(enc.reg(this )-arm.D_OFFSET);
+        short reg1 = enc.reg(in(1));
+        int body = arm.float_cast(0b10011110, 1, reg1, self);
+        enc.add4(body);
+    }
+
+    // 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)));
+    }
+
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/IntARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/IntARM.java
new file mode 100644
index 0000000..2608b04
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/IntARM.java
@@ -0,0 +1,66 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.arm;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.ConstantNode;
+import com.compilerprogramming.ezlang.compiler.nodes.MachNode;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeInteger;
+
+// Integer constants
+public class IntARM extends ConstantNode implements MachNode {
+    IntARM( ConstantNode con ) { super(con); }
+
+    @Override public String op() { return "ldi"; }
+    @Override public RegMask regmap(int i) { return null; }
+    @Override public RegMask outregmap() { return arm.WMASK; }
+
+    @Override public boolean isClone() { return true; }
+    @Override public IntARM copy() { return new IntARM(this); }
+
+    @Override public void encoding( Encoding enc ) {
+        short self = enc.reg(this);
+        long x = ((SONTypeInteger)_con).value();
+        int nb0 = 0;
+        int nb1 = 0;
+        // Count number of 0000 and FFFF blocks
+        for (int i=0; i<64; i+=16) {
+            int block = (int)(x >> i) & 0xFFFF;
+            if (block == 0) nb0++;
+            if (block == 0xFFFF) nb1++;
+        }
+        int pattern;
+        int op;
+        if(nb0 >= nb1) {
+            // More 0 blocks then F blocks, use movz
+            pattern = 0;
+            op = 0b110100101;
+        } else {
+            // More F blocks then 0 blocks, use movn
+            pattern = 0xFFFF;
+            op = 0b100100101;
+        }
+        int invert = pattern;
+        for (int i=0; i<4; i++) {
+            int block = (int)x & 0xFFFF;
+            x >>= 16;
+            if (block != pattern) {
+                enc.add4(arm.mov(op, i, block ^ invert, self));
+                op = 0b111100101;
+                invert = 0;
+            }
+        }
+        if (op != 0b111100101) {
+            // All blocks are the same, special case
+            enc.add4(arm.mov(op, 0, 0, self));
+        }
+    }
+
+    // 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);
+        _con.print(sb.p(reg).p(" = #"));
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LoadARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LoadARM.java
new file mode 100644
index 0000000..6d11b2f
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LoadARM.java
@@ -0,0 +1,33 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.arm;
+
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.LoadNode;
+import com.compilerprogramming.ezlang.compiler.nodes.Node;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFloat;
+
+// Load memory addressing on ARM
+// Support imm, reg(direct), or reg+off(indirect) addressing
+// Base = base - base pointer, offset is added to base
+// idx  = null
+// off  = off - offset added to base
+
+public class LoadARM extends MemOpARM{
+    LoadARM(LoadNode ld,Node base, Node idx, int off) {
+        super(ld, base, idx, off, 0);
+    }
+    @Override public String op() { return "ld"+_sz; }
+    @Override public RegMask outregmap() { return arm.MEM_MASK; }
+    // ldr(immediate - unsigned offset) | ldr(register)
+    @Override public void encoding( Encoding enc ) {
+        if(_declaredType == SONTypeFloat.F32 || _declaredType == SONTypeFloat.F64) {
+            ldst_encode(enc, 0b1111110101, 0b11111100011, this, true);
+        } else {
+            ldst_encode(enc, 0b1111100101, 0b11111000011, this, false);
+        }
+    }
+    @Override public void asm(CodeGen code, SB sb) {
+        sb.p(code.reg(this)).p(",");
+        asm_address(code,sb);
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LslARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LslARM.java
new file mode 100644
index 0000000..0596fc2
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LslARM.java
@@ -0,0 +1,19 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.arm;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+// Logical Shift Left (register)
+public class LslARM extends MachConcreteNode implements MachNode {
+    LslARM(Node asr) {super(asr);}
+    @Override public String op() { return "shr"; }
+    @Override public RegMask regmap(int i) { return arm.RMASK; }
+    @Override public RegMask outregmap() { return arm.RMASK; }
+
+    @Override public void encoding( Encoding enc ) { arm.shift_reg(enc,this,0b1000); }
+    @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)));
+    }
+
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LslIARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LslIARM.java
new file mode 100644
index 0000000..9201580
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LslIARM.java
@@ -0,0 +1,33 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.arm;
+
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.codegen.CodeGen;
+import com.compilerprogramming.ezlang.compiler.codegen.Encoding;
+import com.compilerprogramming.ezlang.compiler.codegen.RegMask;
+import com.compilerprogramming.ezlang.compiler.nodes.MachConcreteNode;
+import com.compilerprogramming.ezlang.compiler.nodes.MachNode;
+import com.compilerprogramming.ezlang.compiler.nodes.Node;
+
+public class LslIARM extends MachConcreteNode implements MachNode {
+    final int _imm;
+    LslIARM(Node lsl, int imm) {
+        super(lsl);
+        _inputs.pop();
+        _imm = imm;
+    }
+    @Override public String op() { return "lsli"; }
+    @Override public RegMask regmap(int i) { return arm.RMASK; }
+    @Override public RegMask outregmap() { return arm.RMASK; }
+    @Override public void encoding( Encoding enc ) {
+        short rd = enc.reg(this);
+        short rn = enc.reg(in(1));
+        assert _imm > 0;
+        // UBFM <Xd>, <Xn>, #(-<shift> MOD 64), #(63-<shift>)
+        // immr must be (-<shift> MOD 64) = 64 - shift
+        enc.add4(arm.imm_shift(0b1101001101, 64 - _imm, (64 - _imm) - 1, rn, rd));
+    }
+    // General form: "lsli  rd = rs1 << imm"
+    @Override public void asm(CodeGen code, SB sb) {
+        sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" << #").p(_imm);
+    }
+}
\ No newline at end of file
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LsrARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LsrARM.java
new file mode 100644
index 0000000..afd84ba
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LsrARM.java
@@ -0,0 +1,19 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.arm;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+// Logical Shift Right (register)
+public class LsrARM extends MachConcreteNode implements MachNode {
+    LsrARM(Node asr) {super(asr);}
+    @Override public String op() { return "shr"; }
+    @Override public RegMask regmap(int i) { return arm.RMASK; }
+    @Override public RegMask outregmap() { return arm.RMASK; }
+
+    @Override public void encoding( Encoding enc ) { arm.shift_reg(enc,this,0b1001); }
+    @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)));
+    }
+
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LsrIARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LsrIARM.java
new file mode 100644
index 0000000..72629ff
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LsrIARM.java
@@ -0,0 +1,29 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.arm;
+
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+// logical right shift immediate
+public class LsrIARM extends MachConcreteNode implements MachNode {
+    final int _imm;
+    LsrIARM(Node lsr, int imm) {
+        super(lsr);
+        _inputs.pop();
+        _imm = imm;
+    }
+    @Override public String op() { return "lsri"; }
+    @Override public RegMask regmap(int i) { return arm.RMASK; }
+    @Override public RegMask outregmap() { return arm.RMASK; }
+    @Override public void encoding( Encoding enc ) {
+        short rd = enc.reg(this);
+        short rn = enc.reg(in(1));
+        assert _imm > 0;
+        enc.add4(arm.imm_shift(0b1101001101,_imm, 0b111111, rn,rd));
+    }
+    // General form: "lsri  rd = rs1 >>> imm"
+    @Override public void asm(CodeGen code, SB sb) {
+        sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" >>> #").p(_imm);
+    }
+}
\ No newline at end of file
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/MemOpARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/MemOpARM.java
new file mode 100644
index 0000000..70dfdee
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/MemOpARM.java
@@ -0,0 +1,67 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.arm;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+import com.compilerprogramming.ezlang.compiler.sontypes.*;
+
+import java.lang.StringBuilder;
+import java.util.BitSet;
+
+
+public abstract class MemOpARM extends MemOpNode implements MachNode {
+    final int _off;             // Limit 9 bits sized, or (13 bits<<logsize) unsigned
+    final int _imm;             // Limit ? bits
+    final char _sz = (char)('0'+(1<<_declaredType.log_size()));
+    MemOpARM(MemOpNode mop, Node ptr, Node idx, int off, int imm) {
+        super(mop,mop);
+        assert ptr._type instanceof SONTypeMemPtr && !(ptr instanceof AddNode);
+        assert ptr() == ptr;
+        _inputs.setX(2, ptr);
+        _inputs.setX(3, idx);
+        _off = off;
+        _imm = imm;
+    }
+
+    // Store-based flavors have a value edge
+    MemOpARM( MemOpNode mop, Node ptr, Node idx, int off, int imm, Node val ) {
+        this(mop,ptr,idx,off,imm);
+        _inputs.setX(4,val);
+    }
+
+    @Override public String label() { return op(); }
+    Node val() { return in(4); } // Only for stores
+
+    @Override public  StringBuilder _printMach(StringBuilder sb, BitSet visited) { return sb.append(".").append(_name); }
+
+    @Override public SONType compute() { throw Utils.TODO(); }
+    @Override public Node idealize() { throw Utils.TODO(); }
+
+    // Wider mask to store both GPRs and FPRs
+    @Override public RegMask regmap(int i) {
+        // 0 - ctrl
+        if( i==1 ) return null; // memory
+        if( i==2 ) return arm.RMASK; // ptr/base
+        if( i==3 ) return arm.RMASK; // off/index
+        if( i==4 ) return arm.RMASK; // value
+        throw Utils.TODO();
+    }
+
+    // Shared encoding for loads and stores(int and float)
+    public void ldst_encode( Encoding enc, int opcode_imm, int opcode_reg, Node xval, boolean float_type ) {
+        short ptr = enc.reg(ptr());
+        short off = enc.reg(off());
+        short val = enc.reg(xval);
+        int body = off() == null
+            ? arm.load_str_imm(opcode_imm, _off, ptr, val)
+            : arm.indr_adr(opcode_reg, off, arm.STORE_LOAD_OPTION.SXTX, 0, ptr, val);
+        enc.add4(body);
+    }
+
+    SB asm_address(CodeGen code, SB sb) {
+        sb.p("[").p(code.reg(ptr())).p("+");
+        if( off() != null ) sb.p(code.reg(off()));
+        else sb.p(_off);
+        return sb.p("]");
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/MulARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/MulARM.java
new file mode 100644
index 0000000..776f507
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/MulARM.java
@@ -0,0 +1,19 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.arm;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+// mulh signed multiply instruction(no-imm form)
+public class MulARM extends MachConcreteNode implements MachNode {
+    MulARM(Node mul) {super(mul);}
+    @Override public String op() { return "mul"; }
+    @Override public RegMask regmap(int i) { return arm.RMASK; }
+    @Override public RegMask outregmap() { return arm.RMASK; }
+
+    @Override public void encoding( Encoding enc ) { arm.madd(enc,this,0b10011011000,31); }
+    // 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)));
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/MulFARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/MulFARM.java
new file mode 100644
index 0000000..3143d6e
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/MulFARM.java
@@ -0,0 +1,18 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.arm;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+public class MulFARM extends MachConcreteNode implements MachNode{
+    MulFARM(Node mulf) {super(mulf);}
+    @Override public String op() { return "mulf"; }
+    @Override public RegMask regmap(int i) { return arm.DMASK; }
+    @Override public RegMask outregmap() { return arm.DMASK; }
+    @Override public void encoding( Encoding enc ) { arm.f_scalar(enc,this,0b10); }
+    // Default on double precision for now(64 bits)
+    // General form: "VMUL.f32  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)));
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/NewARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/NewARM.java
new file mode 100644
index 0000000..76a1424
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/NewARM.java
@@ -0,0 +1,24 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.arm;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+public class NewARM extends NewNode implements MachNode {
+    // A pre-zeroed chunk of memory.
+    NewARM(NewNode nnn) { super(nnn); }
+    @Override public String op() { return "alloc"; }
+    @Override public RegMask    regmap(int i) { return i == 1 ? arm. X0_MASK : null; }
+    @Override public RegMask outregmap(int i) { return i == 1 ? arm. X0_MASK : null; }
+    @Override public RegMask outregmap() { return null; }
+    @Override public RegMask killmap() { return arm.armCallerSave(); }
+
+    @Override public void encoding( Encoding enc ) {
+        // bl
+        enc.external(this,"calloc").add4(arm.b(0b100101, 0));
+    }
+
+    @Override public void asm(CodeGen code, SB sb) {
+        sb.p("#calloc, ").p(code.reg(size()));
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/NotARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/NotARM.java
new file mode 100644
index 0000000..97537eb
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/NotARM.java
@@ -0,0 +1,28 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.arm;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+import com.compilerprogramming.ezlang.compiler.nodes.MachConcreteNode;
+
+public class NotARM extends MachConcreteNode implements MachNode{
+    NotARM(NotNode not) {super(not);}
+    @Override public String op() { return "not"; }
+    @Override public RegMask regmap(int i) { return arm.RMASK; }
+    @Override public RegMask outregmap() { return arm.RMASK;  }
+    @Override public RegMask killmap() { return arm.FLAGS_MASK; }
+    @Override public void encoding( Encoding enc ) {
+        // subs xzr, rs, #0
+        // cset    rd, eq        // Set rd to 1 if rs == 0 (equal), else 0
+        // subtracting zero from rs will just yield rs, it sets the zero flag and then it's used in cset
+        short self = enc.reg(this );
+        short reg1 = enc.reg(in(1));
+        int subs = arm.imm_inst(0b1111000100, 0, reg1, self);
+        enc.add4(subs);
+        int cset = arm.cond_set(0b10011010100, 31, arm.COND.EQ, 63, reg1);
+        enc.add4(cset);
+    }
+
+    @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(this)); }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/OrARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/OrARM.java
new file mode 100644
index 0000000..5b07950
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/OrARM.java
@@ -0,0 +1,20 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.arm;
+
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+// Not using the ORRS variant.
+public class OrARM extends MachConcreteNode implements MachNode {
+    OrARM(Node and) { super(and); }
+    @Override public String op() { return "or"; }
+    @Override public String glabel() { return "|"; }
+    @Override public RegMask regmap(int i) { return arm.RMASK; }
+    @Override public RegMask outregmap() { return arm.RMASK; }
+    @Override public void encoding( Encoding enc ) { arm.r_reg(enc,this,0b10101010); }
+    // 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)));
+    }
+
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/OrIARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/OrIARM.java
new file mode 100644
index 0000000..7773166
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/OrIARM.java
@@ -0,0 +1,24 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.arm;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+public class OrIARM extends MachConcreteNode implements MachNode {
+    final int _imm;
+    OrIARM(Node or, int imm) {
+        super(or);
+        _inputs.pop();
+        _imm = imm;
+    }
+    @Override public String op() { return "ori"; }
+    @Override public RegMask regmap(int i) { return arm.RMASK; }
+    @Override public RegMask outregmap() { return arm.RMASK; }
+    @Override public void encoding( Encoding enc ) {
+        arm.imm_inst_n(enc, this, 0b101100100, _imm);
+    }
+    // General form: "ori  rd = rs1 | imm"
+    @Override public void asm(CodeGen code, SB sb) {
+        sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" | #").p(_imm);
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/ParmARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/ParmARM.java
new file mode 100644
index 0000000..2761799
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/ParmARM.java
@@ -0,0 +1,17 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.arm;
+
+import com.compilerprogramming.ezlang.compiler.codegen.Encoding;
+import com.compilerprogramming.ezlang.compiler.codegen.RegMask;
+import com.compilerprogramming.ezlang.compiler.nodes.MachNode;
+import com.compilerprogramming.ezlang.compiler.nodes.ParmNode;
+
+public class ParmARM extends ParmNode implements MachNode {
+    final RegMask _rmask;
+    ParmARM(ParmNode parm) {
+        super(parm);
+        _rmask = arm.callInMask(fun().sig(),_idx);
+    }
+    @Override public RegMask regmap(int i) { return null; }
+    @Override public RegMask outregmap() { return _rmask; }
+    @Override public void encoding( Encoding enc ) { }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/ProjARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/ProjARM.java
new file mode 100644
index 0000000..d69ac79
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/ProjARM.java
@@ -0,0 +1,15 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.arm;
+
+import com.compilerprogramming.ezlang.compiler.codegen.Encoding;
+import com.compilerprogramming.ezlang.compiler.codegen.RegMask;
+import com.compilerprogramming.ezlang.compiler.nodes.MachNode;
+import com.compilerprogramming.ezlang.compiler.nodes.ProjNode;
+
+public class ProjARM extends ProjNode implements MachNode {
+    ProjARM( ProjNode p ) { super(p); }
+    @Override public RegMask regmap(int i) { return null; }
+    @Override public RegMask outregmap() {
+        return ((MachNode)in(0)).outregmap(_idx);
+    }
+    @Override public void encoding( Encoding enc ) { }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/RetARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/RetARM.java
new file mode 100644
index 0000000..1250365
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/RetARM.java
@@ -0,0 +1,43 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.arm;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+public class RetARM extends ReturnNode implements MachNode {
+    RetARM(ReturnNode ret, FunNode fun) { super(ret, fun); fun.setRet(this); }
+    @Override public String op() {
+        return ((FunARM)fun())._frameAdjust > 0 ? "addi" : "ret";
+    }
+    // Correct Nodes outside the normal edges
+    @Override public void postSelect(CodeGen code) {
+        FunNode fun = (FunNode)rpc().in(0);
+        _fun = fun;
+        fun.setRet(this);
+    }
+    @Override public RegMask regmap(int i) { return arm.retMask(_fun.sig(),i); }
+    @Override public RegMask outregmap() { return null; }
+    // RET
+    @Override public void encoding( Encoding enc ) {
+        int frameAdjust = ((FunARM)fun())._frameAdjust;
+        if( frameAdjust > 0 )
+            enc.add4(arm.imm_inst(0b1001000100, (frameAdjust*-8)&0xFFF, arm.RSP, arm.RSP));
+        enc.add4(arm.ret(0b1101011001011111000000));
+    }
+
+    @Override public void asm(CodeGen code, SB sb) {
+        int frameAdjust = ((FunARM)fun())._frameAdjust;
+        if( frameAdjust>0 )
+            sb.p("rsp += #").p(frameAdjust*-8).p("\nret");
+        // Post code-gen, just print the "ret"
+        if( code._phase.ordinal() <= CodeGen.Phase.RegAlloc.ordinal() )
+            // Prints return reg (either X0 or D0), RPC (always R30) and
+            // then the callee-save registers.
+            for( int i=2; i<nIns(); i++ )
+                sb.p(code.reg(in(i))).p("  ");
+
+        // Post-allocation, if we did not get the expected return register, print what we got
+        else if( code._regAlloc.regnum(rpc()) != arm.X30 )
+            sb.p("[").p(code.reg(rpc())).p("]");
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SetARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SetARM.java
new file mode 100644
index 0000000..c8b149d
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SetARM.java
@@ -0,0 +1,33 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.arm;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+// conditional set?
+// e.g CSET, stores flag info into GPRS after compare
+public class SetARM extends MachConcreteNode implements MachNode {
+    final String _bop;          // One of <,<=,==
+    SetARM(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 String op() { return "set"+_bop; }
+    @Override public RegMask regmap(int i) { assert i==1; return arm.FLAGS_MASK; }
+    @Override public RegMask outregmap() { return arm.RMASK; }
+
+    // Encoding is appended into the byte array; size is returned
+    @Override public void encoding( Encoding enc ) {
+        int body = arm.cset(0b1001101010011111, arm.make_condition(_bop), 0b0111111, enc.reg(this));
+        enc.add4(body);
+    }
+
+    @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);
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SplitARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SplitARM.java
new file mode 100644
index 0000000..3498554
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SplitARM.java
@@ -0,0 +1,54 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.arm;
+
+import com.compilerprogramming.ezlang.compiler.Utils;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.Node;
+import com.compilerprogramming.ezlang.compiler.nodes.SplitNode;
+import com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv.riscv;
+
+public class SplitARM extends SplitNode {
+    SplitARM(String kind, byte round) { super(kind,round,new Node[2]);}
+    @Override public RegMask regmap(int i) { return arm.SPLIT_MASK; }
+    @Override public RegMask outregmap() { return arm.SPLIT_MASK; }
+
+    @Override public void encoding( Encoding enc ) {
+        short dst = enc.reg(this );
+        short src = enc.reg(in(1));
+
+        boolean dstX = dst >= arm.D_OFFSET;
+        boolean srcX = src >= arm.D_OFFSET;
+
+        if (dst >= arm.MAX_REG) {
+            // store to sp
+            if(src >= arm.MAX_REG) {
+                throw Utils.TODO();
+            }
+            int off = enc._fun.computeStackSlot(dst - arm.MAX_REG)*8;
+            enc.add4(arm.load_str_imm(0b1111100100, off, src, dst));
+        }
+
+        if(src >= arm.MAX_REG) {
+            // Load from SP
+            int off = enc._fun.computeStackSlot(src - arm.MAX_REG) * 8;
+            enc.add4(arm.load_str_imm(0b1111100101, off, src, dst));
+        }
+
+        // pick opcode based on regs
+        if(!dstX && !srcX) {
+            // GPR->GPR
+            enc.add4(arm.mov_reg(0b10101010000, src, dst));
+        } else if(dstX && srcX) {
+            // FPR->FPR
+            // fmov reg
+            enc.add4(arm.f_mov_reg(0b00011110, src,dst));
+        } else if(dstX && !srcX) {
+            // GPR->FPR
+            // FMOV(general) 64 bits to DOUBLE-PRECISION
+            enc.add4(arm.f_mov_general(0b10011110, 0b01, 0, 0b111, src, dst));
+        } else if(!dstX && srcX) {
+            //FPF->GPR
+            // FMOV(general) DOUBLE-PRECISION to 64 bits
+            enc.add4(arm.f_mov_general(0b10011110, 0b01, 0, 0b110, src, dst));
+        }
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/StoreARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/StoreARM.java
new file mode 100644
index 0000000..69fc9f1
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/StoreARM.java
@@ -0,0 +1,37 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.arm;
+
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.StoreNode;
+import com.compilerprogramming.ezlang.compiler.nodes.Node;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFloat;
+
+// Store memory addressing on ARM
+// Support imm, reg(direct), or reg+off(indirect) addressing
+// base - base pointer, offset is added to base
+// null - index never allowed (no [reg+reg] mode)
+// off  - offset added to base
+// imm  - immediate value to store, only if val is null
+// val  - value to store or null
+
+//e.g s.cs[0] =  67; // C
+// base = s.cs, off = 4, imm = 67, val = null
+public class StoreARM extends MemOpARM {
+    StoreARM(StoreNode st, Node base, Node idx, int off, Node val) {
+        super(st, base, idx, off, 0, val);
+    }
+    @Override public String op() { return "st"+_sz; }
+    @Override public RegMask outregmap() { return null; }
+    @Override public void encoding( Encoding enc ) {
+        if(_declaredType == SONTypeFloat.F32 || _declaredType == SONTypeFloat.F64) {
+            ldst_encode(enc, 0b1111110100,0b11111100001, val(), true);
+        } else {
+            ldst_encode(enc, 0b1111100100, 0b11111000001, val(), false);
+        }
+    }
+    @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/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SubARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SubARM.java
new file mode 100644
index 0000000..847a0b4
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SubARM.java
@@ -0,0 +1,19 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.arm;
+
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+public class SubARM extends MachConcreteNode implements MachNode {
+    SubARM(Node sub) { super(sub); }
+    @Override public String op() { return "sub"; }
+    @Override public RegMask regmap(int i) { return arm.RMASK; }
+    @Override public RegMask outregmap() { return arm.RMASK; }
+
+    @Override public void encoding( Encoding enc ) { arm.r_reg(enc,this,0b11001011); }
+
+    // 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)));
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SubFARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SubFARM.java
new file mode 100644
index 0000000..a1326f2
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SubFARM.java
@@ -0,0 +1,18 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.arm;
+
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+public class SubFARM extends MachConcreteNode implements MachNode {
+    SubFARM(Node subf) {super(subf);}
+    @Override public String op() { return "subf"; }
+    @Override public RegMask regmap(int i) { return arm.DMASK; }
+    @Override public RegMask outregmap() { return arm.DMASK; }
+    @Override public void encoding( Encoding enc ) { arm.f_scalar(enc,this,0b1110); }
+    // Default on double precision for now(64 bits)
+    // General form: "vsub.f32  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)));
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SubIARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SubIARM.java
new file mode 100644
index 0000000..2ada6af
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SubIARM.java
@@ -0,0 +1,27 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.arm;
+
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.MachConcreteNode;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeInteger;
+
+public class SubIARM extends MachConcreteNode implements MachNode {
+    final int _imm;
+    SubIARM(Node sub, int imm) {
+        super(sub);
+        _inputs.pop();
+        _imm = imm;
+    }
+    @Override public String op() { return _imm == -1 ? "dec" : "subi"; }
+    @Override public RegMask regmap(int i) { return arm.RMASK; }
+    @Override public RegMask outregmap() { return arm.RMASK; }
+
+    @Override public void encoding( Encoding enc ) { arm.imm_inst(enc,this,0b1101000100,_imm); }
+
+    // General form: "subi  rd = rs1 - imm"
+    @Override public void asm(CodeGen code, SB sb) {
+        sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" - #").p(_imm);
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/TFPARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/TFPARM.java
new file mode 100644
index 0000000..1be5a4e
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/TFPARM.java
@@ -0,0 +1,52 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.arm;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.ConstantNode;
+import com.compilerprogramming.ezlang.compiler.nodes.MachNode;
+import com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv.riscv;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFunPtr;
+
+public class TFPARM extends ConstantNode implements MachNode, RIPRelSize {
+    TFPARM( ConstantNode con ) { super(con); }
+    @Override public String op() { return "ldx"; }
+    @Override public RegMask regmap(int i) { return null; }
+    @Override public RegMask outregmap() { return arm.WMASK; }
+    @Override public boolean isClone() { return true; }
+    @Override public TFPARM copy() { return new TFPARM(this); }
+    @Override public void encoding( Encoding enc ) {
+        enc.relo(this);
+        short self = enc.reg(this);
+        // adrp    x0, 0
+        int adrp = arm.adrp(1,0, 0b10000, 0,self);
+        // add     x0, x0, 0
+        arm.imm_inst(enc,this,0b1001000100,0);
+        enc.add4(adrp);
+    }
+
+    @Override public byte encSize(int delta) {
+        if( -(1L<<12) <= delta && delta < (1L<<12) ) return 4;
+        throw Utils.TODO();
+    }
+
+    // Delta is from opcode start
+    @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) {
+        short rpc = enc.reg(this);
+        if(opLen ==4 ) {
+            // opstart of add
+            int next = opStart + opLen;
+            int adrp_delta = delta >> 12;
+            // patch upper 20 bits via adrp
+            enc.patch4(opStart, arm.adrp(1, adrp_delta & 0b11, 0b10000, adrp_delta >> 2, rpc));
+            // low 12 bits via add
+            enc.patch4(next, arm.imm_inst_l(enc, this, 0b1001000100, delta & 0xfff));
+        } else {
+            throw Utils.TODO();
+        }
+    }
+
+    @Override public void asm(CodeGen code, SB sb) {
+        String reg = code.reg(this);
+        _con.print(sb.p(reg).p(" #"));
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/UJmpARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/UJmpARM.java
new file mode 100644
index 0000000..fa566b5
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/UJmpARM.java
@@ -0,0 +1,48 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.arm;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
+import java.util.BitSet;
+
+// unconditional jump
+public class UJmpARM extends CFGNode implements MachNode, RIPRelSize {
+    UJmpARM() { }
+    @Override public String op() { return "jmp"; }
+    @Override public String label() { return op(); }
+    @Override public StringBuilder _print1( StringBuilder sb, BitSet visited ) {
+        return sb.append("jmp ");
+    }
+    @Override public RegMask regmap(int i) {return null; }
+    @Override public RegMask outregmap() { return null; }
+    @Override public SONType compute() { throw Utils.TODO(); }
+    @Override public Node idealize() { throw Utils.TODO(); }
+    @Override public void encoding( Encoding enc ) {
+        enc.jump(this,uctrl());
+        int body = arm.b(0b01010100, 0);
+        enc.add4(body);
+    }
+
+    // Delta is from opcode start
+    @Override public byte encSize(int delta) {
+        if( -(1<<26) <= delta && delta < (1<<26) ) return 4;
+        // 2 word encoding needs a tmp register, must teach RA
+        throw Utils.TODO();
+    }
+
+    // Delta is from opcode start
+    @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) {
+        if( opLen==4 ) {
+            enc.patch4(opStart,arm.b(0b01010100, delta));
+        } else {
+            throw Utils.TODO();
+        }
+    }
+
+    @Override public void asm(CodeGen code, SB sb) {
+        CFGNode target = uctrl();
+        assert target.nOuts() > 1; // Should optimize jmp to empty targets
+        sb.p(label(target));
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/XorARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/XorARM.java
new file mode 100644
index 0000000..ac9e38b
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/XorARM.java
@@ -0,0 +1,19 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.arm;
+
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+public class XorARM extends MachConcreteNode implements MachNode{
+    XorARM(Node xor) {super(xor);}
+    @Override public String op() { return "xor"; }
+    @Override public String glabel() { return "^"; }
+    @Override public RegMask regmap(int i) { return arm.RMASK; }
+    @Override public RegMask outregmap() { return arm.RMASK; }
+    @Override public void encoding( Encoding enc ) { arm.r_reg(enc,this,0b11001010); }
+    // General form: "rd = x1 ^ x2"
+    @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)));
+    }
+
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/XorIARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/XorIARM.java
new file mode 100644
index 0000000..2dc314a
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/XorIARM.java
@@ -0,0 +1,27 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.arm;
+
+import com.compilerprogramming.ezlang.compiler.nodes.MachConcreteNode;
+import com.compilerprogramming.ezlang.compiler.nodes.MachNode;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+public class XorIARM extends MachConcreteNode implements MachNode {
+    final int _imm;
+    XorIARM(Node xor, int imm) {
+        super(xor);
+        _inputs.pop();
+        _imm = imm;
+    }
+
+    @Override public String op() { return "xori"; }
+    @Override public RegMask regmap(int i) { return arm.RMASK; }
+    @Override public RegMask outregmap() { return arm.RMASK; }
+
+    // General form: "xori  rd = rs1 ^ imm"
+    @Override public void encoding( Encoding enc ) { arm.imm_inst_n(enc,this,0b110100100,_imm); }
+    @Override public void asm(CodeGen code, SB sb) {
+        sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" ^ #").p(_imm);
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/arm.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/arm.java
new file mode 100644
index 0000000..4f01b8d
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/arm.java
@@ -0,0 +1,662 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.arm;
+
+import com.compilerprogramming.ezlang.compiler.Utils;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+import com.compilerprogramming.ezlang.compiler.sontypes.*;
+import java.io.ByteArrayOutputStream;
+
+public class arm extends Machine {
+    public arm( CodeGen code ) {
+        if( !"SystemV".equals(code._callingConv) )
+            throw new IllegalArgumentException("Unknown calling convention "+code._callingConv);
+    }
+
+    // ARM64
+    @Override public String name() { return "arm"; }
+
+    // GPR(S)
+    static final int X0  =  0,  X1  =  1,  X2  =  2,  X3  =  3,  X4  =  4,  X5  =  5,  X6  =  6,  X7  =  7;
+    static final int X8  =  8,  X9  =  9,  X10 = 10,  X11 = 11,  X12 = 12,  X13 = 13,  X14 = 14,  X15 = 15;
+    static final int X16 = 16,  X17 = 17,  X18 = 18,  X19 = 19,  X20 = 20,  X21 = 21,  X22 = 22,  X23 = 23;
+    static final int X24 = 24,  X25 = 25,  X26 = 26,  X27 = 27,  X28 = 28,  X29 = 29,  X30 = 30,  RSP = 31;
+
+    // Floating point registers
+    static final int D0  = 32,  D1  = 33,  D2  = 34,  D3  = 35,  D4  = 36,  D5  = 37,  D6  = 38,  D7  = 39;
+    static final int D8  = 40,  D9  = 41,  D10 = 42,  D11 = 43,  D12 = 44,  D13 = 45,  D14 = 46,  D15 = 47;
+    static final int D16 = 48,  D17 = 49,  D18 = 50,  D19 = 51,  D20 = 52,  D21 = 53,  D22 = 54,  D23 = 55;
+    static final int D24 = 56,  D25 = 57,  D26 = 58,  D27 = 59,  D28 = 60,  D29 = 61,  D30 = 62,  D31 = 63;
+
+    static final int MAX_REG = 64;
+    static final int D_OFFSET = 31;
+    static final int FLAGS = 64;
+
+    static final String[] REGS = new String[] {
+            "X0",  "X1",  "X2",  "X3",  "X4",  "X5",  "X6",  "X7",
+            "X8",  "X9",  "X10", "X11", "X12", "X13", "X14", "X15",
+            "X16", "X17", "X18", "X19", "X20", "X21", "X22", "X23",
+            "X24", "X25", "X26", "X27", "X28", "X29", "RPC", "RSP",
+            "D0",  "D1",  "D2",  "D3",  "D4",  "D5",  "D6",  "D7",
+            "D8",  "D9",  "D10", "D11", "D12", "D13", "D14", "D15",
+            "D16", "D17", "D18", "D19", "D20", "D21", "D22", "D23",
+            "D24", "D25", "D26", "D27", "D28", "D29", "D30", "D31",
+            "flags"
+    };
+    @Override public String reg( int reg ) {
+        return reg < REGS.length ? REGS[reg] : "[rsp+"+(reg-REGS.length)*8+"]";
+    }
+    // Stack slots, in units of 8 bytes.
+    @Override public int stackSlot( int reg ) {
+        return reg < REGS.length ? -1 : reg-REGS.length;
+    }
+
+    // from (x0-x30)
+    // General purpose register mask: pointers and ints, not floats
+    static final long RD_BITS = 0xFFFFFFFFL;
+    static final RegMask RMASK = new RegMask(RD_BITS);
+    static final long WR_BITS = 0x7FFFFFFFL; // All the GPRs, not RSP
+    static final RegMask WMASK = new RegMask(WR_BITS);
+
+    // Float mask from(d0–d31)
+    static final long FP_BITS = 0xFFFFFFFFL<<D0;
+    static final RegMask DMASK = new RegMask(FP_BITS);
+
+    // Load/store mask; both GPR and FPR
+    static final RegMask MEM_MASK = new RegMask(WR_BITS | FP_BITS);
+
+    static final RegMask SPLIT_MASK = new RegMask(WR_BITS | FP_BITS, -1L);
+
+    static final RegMask FLAGS_MASK = new RegMask(FLAGS);
+    //  x30 (LR): Procedure link register, used to return from subroutines.
+    static final RegMask RPC_MASK = new RegMask(1L << X30);
+
+    // Arguments masks
+    static final RegMask X0_MASK = new RegMask(X0);
+    static final RegMask X1_MASK = new RegMask(X1);
+    static final RegMask X2_MASK = new RegMask(X2);
+    static final RegMask X3_MASK = new RegMask(X3);
+    static final RegMask X4_MASK = new RegMask(X4);
+    static final RegMask X5_MASK = new RegMask(X5);
+    static final RegMask X6_MASK = new RegMask(X6);
+    static final RegMask X7_MASK = new RegMask(X7);
+
+    // Arguments(float) masks
+    static final RegMask D0_MASK = new RegMask(D0);
+    static final RegMask D1_MASK = new RegMask(D1);
+    static final RegMask D2_MASK = new RegMask(D2);
+    static final RegMask D3_MASK = new RegMask(D3);
+    static final RegMask D4_MASK = new RegMask(D4);
+    static final RegMask D5_MASK = new RegMask(D5);
+    static final RegMask D6_MASK = new RegMask(D6);
+    static final RegMask D7_MASK = new RegMask(D7);
+
+    // 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 final RegMask[] CALLINMASK = new RegMask[] {
+            X0_MASK,
+            X1_MASK,
+            X2_MASK,
+            X3_MASK,
+            X4_MASK,
+            X5_MASK,
+            X6_MASK,
+            X7_MASK,
+    };
+    static final RegMask[] XMMS = new RegMask[] {
+            D0_MASK,
+            D1_MASK,
+            D2_MASK,
+            D3_MASK,
+            D4_MASK,
+            D5_MASK,
+            D6_MASK,
+            D7_MASK,
+    };
+
+    // ARM ENCODING
+    public enum OPTION {
+        UXTB,
+        UXTH,
+        UXTX,
+        SXTB,
+        SXTH,
+        SXTW,
+        SXTX,
+    }
+
+    static public int cset(int opcode, COND cond, int rn, int rd) {
+        return (opcode << 16) | (cond.ordinal() << 12) | (rn << 5) | rd;
+    }
+
+    public enum STORE_LOAD_OPTION {
+        UXTW,  // base+ u32 index [<< logsize]
+        UXTX,  // base+ u64 index [<< logsize]
+        SXTW,  // base+ s32 index [<< logsize]
+        SXTX,  // base+ s64 index [<< logsize]
+    }
+
+    static public String invert(String op) {
+        return switch (op) {
+            case "==" -> "!=";
+            case "!=" -> "==";
+            case ">" -> "<=";
+            case "<" -> ">=";
+            case ">=" -> "<";
+            case "<=" -> ">";
+            default -> throw new IllegalStateException("Unexpected value: " + op);
+        };
+    }
+    public enum COND {
+        EQ,
+        NE,
+        CS,
+        CC,
+        MI,
+        PL,
+        VS,
+        VC,
+        HI,
+        LS,
+        GE,
+        LT,
+        GT,
+        LE,
+        AL,
+        NV
+    }
+
+    // True if signed 9-bit immediate
+    private static boolean imm9(SONTypeInteger ti) {
+        // 55 = 64-9
+        return ti.isConstant() && ((ti.value()<<55)>>55) == ti.value();
+    }
+    // True if signed 12-bit immediate
+    private static boolean imm12(SONTypeInteger ti) {
+        // 52 = 64-12
+        return ti.isConstant() && ((ti.value()<<52)>>52) == ti.value();
+    }
+
+
+    // Can we encode this in ARM's 12-bit LOGICAL immediate form?
+    // Some combination of shifted bit-masks.
+    private static int imm12Logical(SONTypeInteger ti) {
+        if( !ti.isConstant() ) return -1;
+        if( !ti.isConstant() ) return -1;
+        long val = ti.value();
+        if (val == 0 || val == -1) return -1; // Special cases are not allowed
+        int immr = 0;
+        // Rotate until we have 0[...]1
+        while (val < 0 || (val & 1)==0) {
+            val = (val >>> 63) | (val << 1);
+            immr++;
+        }
+        int size = 32;
+        long pattern = val;
+        // Is upper half of pattern the same as the lower?
+        while ((pattern & ((1L<<size)-1)) == (pattern >> size)) {
+            // Then only take one half
+            pattern >>= size;
+            size >>= 1;
+        }
+        size <<= 1;
+        int imms = Long.bitCount(pattern);
+        // Pattern should now be zeros followed by ones 0000011111
+        if (pattern != (1L<<imms)-1) return -1;
+        imms--;
+        if (size == 64) return 0x1000 | immr << 6 | imms;
+        return (32-size)<<1 | immr << 6 | imms;
+    }
+
+    // sh is encoded in opcdoe
+    public static int imm_inst(int opcode, int imm12, int rn, int rd) {
+        assert opcode >=0 && imm12 >= 0 && rn >=0 && rd>=0; // Caller zeros high order bits
+        return (opcode << 22) | (imm12 << 10) | (rn << 5) | rd;
+    }
+
+    public static int imm_shift(int opcode, int imm, int imms, int rn,  int rd)  {
+            return (opcode << 22) | (1 << 22) | (imm << 16) | (imms << 10) | (rn << 5) | rd;
+    }
+
+    public static void imm_inst(Encoding enc, Node n, int opcode, int imm12) {
+        short self = enc.reg(n);
+        short reg1 = enc.reg(n.in(1));
+        int body = imm_inst(opcode, imm12&0xFFF, reg1, self);
+        enc.add4(body);
+    }
+
+    public static void imm_inst_n(Encoding enc, Node n, int opcode, int imm13) {
+        short self = enc.reg(n);
+        short reg1 = enc.reg(n.in(1));
+
+        int body = imm_inst_n(opcode, imm13, reg1, self);
+        enc.add4(body);
+    }
+
+    // nth bit comes from immediate and not opcode
+    public static int imm_inst_n(int opcode, int imm13, int rn, int rd) {
+        assert 0 <= imm13 && imm13 <= 0x1FFF;
+        return (opcode << 23) | (imm13 << 10) | (rn << 5) | rd;
+    }
+
+    public static int imm_inst_l(Encoding enc, Node n, int opcode, int imm12) {
+        short self = enc.reg(n);
+        short reg1 = enc.reg(n.in(1));
+        int body = imm_inst(opcode, imm12&0xFFF, reg1, self);
+        return body;
+    }
+
+    // for normal add, reg1, reg2 cases (reg-to-reg)
+    // using shifted-reg form
+    public static int r_reg(int opcode, int shift, int rm, int imm6, int rn, int rd) {
+        return (opcode << 24) | (shift << 21) | (rm << 16) | (imm6 << 10) << (rn << 5) | rd;
+    }
+    public static void r_reg(Encoding enc, Node n, int opcode) {
+        short self = enc.reg(n);
+        short reg1 = enc.reg(n.in(1));
+        short reg2 = enc.reg(n.in(2));
+        int body = r_reg(opcode, 0, reg2, 0,  reg1, self);
+        enc.add4(body);
+    }
+
+    public static int shift_reg(int opcode, int rm, int op2, int rn, int rd) {
+        return (opcode << 21) | (rm << 16) | (op2 << 10) | (rn << 5) | rd;
+    }
+    public static void shift_reg(Encoding enc, Node n, int op2) {
+        short self = enc.reg(n);
+        short reg1 = enc.reg(n.in(1));
+        short reg2 = enc.reg(n.in(2));
+        int body = shift_reg(0b10011010110, reg2, op2, reg1, self);
+        enc.add4(body);
+    }
+
+    //  MUL can be considered an alias for MADD with the third operand Ra being set to 0
+    public static int madd(int opcode, int rm, int ra, int rn, int rd) {
+        return (opcode << 21) | (rm << 16) | (ra << 10) | (rn << 5) | rd;
+    }
+    public static void madd(Encoding enc, Node n, int opcode, int ra) {
+        short self = enc.reg(n);
+        short reg1 = enc.reg(n.in(1));
+        short reg2 = enc.reg(n.in(2));
+        int body = madd(opcode, reg2, ra,  reg1, self);
+        enc.add4(body);
+    }
+
+    // encodes movk, movn, and movz
+    public static int mov(int opcode, int shift, int imm16, int rd) {
+        return (opcode << 23) | (shift << 21) | (imm16 << 5) | rd;
+    }
+
+    public static int mov_reg(int opcode, int src, int dst) {
+        return (opcode  << 21) | (src << 16) | 0b11111 << 5 | dst;
+    }
+
+    public static int ret(int opcode) {
+        return (opcode << 10);
+    }
+
+    // FMOV (scalar, immediate)
+    public static int f_mov(int opcode, int ftype,  int imm8, int rd) {
+        return (opcode << 24) | (ftype << 21) |(imm8 << 13) | (128 << 5) | rd;
+    }
+
+    public static int f_scalar(int opcode, int ftype, int rm, int op, int rn, int rd) {
+        return (opcode << 24) | (ftype << 22) | (1 << 21) | (rm << 16) | (op << 10) | (rn << 5) | rd;
+    }
+
+    public static int f_mov_reg(int opcode, int rn, int rd) {
+        return (opcode << 24) | (0b01100000010000 << 10) | (rn << 5) | rd;
+    }
+    public static int f_mov_general(int opcode, int ftype, int rmode, int opcode1, int rn, int rd) {
+        return (opcode << 24) |(ftype << 22) | (1 << 21) | (rmode << 19) | (opcode1 << 16) | (rn << 5) | rd;
+    }
+    public static void f_scalar(Encoding enc, Node n, int op ) {
+        short self = (short)(enc.reg(n)      -D_OFFSET);
+        short reg1 = (short)(enc.reg(n.in(1))-D_OFFSET);
+        short reg2 = (short)(enc.reg(n.in(2))-D_OFFSET);
+        int body = f_scalar(0b00011110, 1, reg2, op,  reg1, self);
+        enc.add4(body);
+    }
+
+    // share same encoding with LDR (literal, SIMD&FP) flavour
+    public static int load_pc(int opcode, int offset, int rt) {
+        return (opcode << 24) | (offset << 5) | rt;
+    }
+    // int l
+    public static int adrp(int op, int imlo,int opcode, int imhi, int rd) {
+        return (op << 31) | (imlo << 29) |(opcode << 24) | (imhi << 5) | rd;
+    }
+
+    public static int load_adr(int opcode, int offset, int base, int rt) {
+        return (opcode << 22) | (offset << 10) | (base << 5) | rt;
+    }
+
+    // [Rptr+Roff]
+    public static int indr_adr(int opcode, int off, STORE_LOAD_OPTION option, int s, int ptr, int rt) {
+        return (opcode << 21) | (off << 16) | (option.ordinal() << 13) | (s << 12) | (2 << 10) | (ptr << 5) | rt;
+    }
+    // [Rptr+imm9]
+    public static int load_str_imm(int opcode, int imm12, int ptr, int rt) {
+        return (opcode << 22) | (imm12 << 10)  |(ptr << 5) | rt;
+    }
+
+    // encoding for vcvt, size is encoded in operand
+    // <Qd>, <Qm>
+    // F32.S32
+    //encoded as op = 0b00, size = 0b10.
+    // VCVT<c>.<Td>.<Tm> <Dd>, <Dm>
+    // opcode is broken down into 4 pieces
+    public static int f_convert(int opcode_1, int opcode_2, int opcode_3, int opcode_4,  int vd, int vm)  {
+        return (opcode_1 << 28) | (opcode_2 << 24) | (opcode_3 << 20) | (opcode_4 << 16) |
+                (vd << 12) | (0x01100010 << 4) | vm;
+    }
+    public static int float_cast(int opcode, int ftype, int rn, int rd) {
+        return (opcode << 24) | (ftype << 22) | (2176 << 10) | (rn << 5) | rd;
+    }
+
+
+    // ftype = 3
+    public static int f_cmp(int opcode, int ftype, int rm, int rn) {
+        return (opcode  << 24) | (ftype << 21) | (rm << 16) | (8 << 10) | (rn << 5) | 8;
+    }
+    public static void f_cmp(Encoding enc, Node n) {
+        short reg1 = (short)(enc.reg(n.in(1))-D_OFFSET);
+        short reg2 = (short)(enc.reg(n.in(2))-D_OFFSET);
+        int body = f_cmp(0b00011110, 3, reg1,  reg2);
+        enc.add4(body);
+    }
+
+    public static COND make_condition(String bop) {
+        return switch (bop) {
+        case "==" -> COND.EQ;
+        case "!=" -> COND.NE;
+        case "<"  -> COND.LE;
+        case "<=" -> COND.LT;
+        case ">=" -> COND.GT;
+        case ">"  -> COND.GE;
+        default   -> throw Utils.TODO();
+        };
+    }
+    // Todo: maybe missing zero here after delta << 5
+    public static int b_cond(int opcode, int delta, COND cond) {
+        // 24-5 == 19bits offset range
+        assert -(1<<19) <= delta && delta < (1<<19);
+        delta &= (1L<<19)-1;    // Zero extend
+        return (opcode << 24) | (delta << 5) | cond.ordinal();
+    }
+
+    public static int cond_set(int opcode, int rm, COND cond, int rn, int rd) {
+        return (opcode << 21) | (rm << 16) | (cond.ordinal() << 12) | (rn << 5) | rd;
+    }
+
+    // Branch with Link to Register calls a subroutine at an address in a register, setting register X30 to PC+4.
+    public static int blr(int opcode, int rd) {
+        return opcode << 10 | rd << 5;
+    }
+    public static int b(int opcode, int delta) {
+        assert -(1<<26) <= delta && delta < (1<<26);
+        delta &= (1L<<26)-1;    // Zero extend
+        return (opcode << 26) | delta;
+    }
+
+    static RegMask callInMask(SONTypeFunPtr tfp, int idx ) {
+        if( idx==0 ) return RPC_MASK;
+        if( idx==1 ) return null;
+        // Count floats in signature up to index
+        int fcnt=0;
+        for( int i=2; i<idx; i++ )
+            if( tfp.arg(i-2) instanceof SONTypeFloat)
+                fcnt++;
+        // Floats up to XMMS in XMM registers
+        if( tfp.arg(idx-2) instanceof SONTypeFloat ) {
+            if( fcnt < XMMS.length )
+                return XMMS[fcnt];
+        } else {
+            RegMask[] cargs = CALLINMASK;
+            if( idx-2-fcnt < cargs.length )
+                return cargs[idx-2-fcnt];
+        }
+        throw Utils.TODO(); // Pass on stack slot
+    }
+    // Return the max stack slot used by this signature, or 0
+    static short maxSlot( SONTypeFunPtr tfp ) {
+        // Count floats in signature up to index
+        int fcnt=0;
+        for( int i=0; i<tfp.nargs(); i++ )
+            if( tfp.arg(i) instanceof SONTypeFloat )
+                fcnt++;
+        if( fcnt >= XMMS.length )
+            throw Utils.TODO();
+        RegMask[] cargs = CALLINMASK;
+        if( tfp.nargs()-fcnt >= cargs.length )
+            throw Utils.TODO();
+        return 0;               // No stack args
+    }
+
+    static final long CALLEE_SAVE =
+            1L<<X19 |
+                    1L<<X20 | 1L<<X21 | 1L<<X22 | 1L<<X23 |
+                    1L<<X24 | 1L<<X25 | 1L<<X26 | 1L<<X27 |
+                    1L<<X28 |
+                    1L<<D9  | 1L<<D10 | 1L<<D11 |
+                    1L<<D12 | 1L<<D13 | 1L<<D14 | 1L<<D15;
+
+    static final RegMask CALLER_SAVE_MASK;
+    static {
+        long caller = ~CALLEE_SAVE;
+        caller &= ~(1L<<RSP);
+        CALLER_SAVE_MASK = new RegMask(caller,1L<<(FLAGS-64));
+    }
+    static RegMask armCallerSave() { return CALLER_SAVE_MASK; }
+    @Override public RegMask callerSave() { return armCallerSave(); }
+
+    static final RegMask CALLEE_SAVE_MASK = new RegMask(CALLEE_SAVE);
+    static RegMask armCalleeSave() { return CALLEE_SAVE_MASK; }
+    @Override public RegMask calleeSave() { return armCalleeSave(); }
+
+
+    // Return single int/ptr register.  Used by CallEnd output and Return input.
+    static final RegMask[] RET_MASKS;
+    static {
+        int nSaves = CALLEE_SAVE_MASK.size();
+        RET_MASKS = new RegMask[4 + nSaves];
+        RET_MASKS[0] = null;
+        RET_MASKS[1] = null;     // Memory
+        RET_MASKS[2] = null;     // Varies, either XMM0 or RAX
+        RET_MASKS[3] = RMASK;    // Expected R30 but could be any register
+        short reg = CALLEE_SAVE_MASK.firstReg();
+        for( int i=0; i<nSaves; i++ ) {
+            RET_MASKS[i+4] = new RegMask(reg);
+            reg = CALLEE_SAVE_MASK.nextReg(reg);
+        }
+    }
+    static RegMask retMask( SONTypeFunPtr tfp, int i ) {
+        return i==2
+                ? (tfp.ret() instanceof SONTypeFloat ? D0_MASK : X0_MASK)
+                : RET_MASKS[i];
+    }
+
+    // Create a split op; any register to any register, including stack slots
+    @Override public SplitNode split(String kind, byte round, LRG lrg) {  return new SplitARM(kind,round);  }
+
+    // Return a MachNode unconditional branch
+    @Override public CFGNode jump() {
+        return new UJmpARM();
+    }
+
+    // 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  -> new AddFARM(addf);
+        case AddNode add    -> add(add);
+        case AndNode and    -> and(and);
+        case BoolNode bool  -> cmp(bool);
+        case CallNode call  -> call(call);
+        case CastNode cast  -> new CastNode(cast);
+        case CallEndNode cend -> new CallEndARM(cend);
+        case CProjNode c    -> new CProjNode(c);
+        case ConstantNode con -> con(con);
+        case DivFNode divf  -> new DivFARM(divf);
+        case DivNode div    -> new DivARM(div);
+        case FunNode fun    -> new FunARM(fun);
+        case IfNode iff     -> jmp(iff);
+        case LoadNode ld    -> ld(ld);
+        case MemMergeNode mem -> new MemMergeNode(mem);
+        case MulFNode mulf  -> new MulFARM(mulf);
+        case MulNode mul    -> new MulARM(mul);
+        case NewNode nnn    -> new NewARM(nnn);
+        case NotNode not    -> new NotARM(not);
+        case OrNode or      -> or(or);
+        case ParmNode parm  -> new ParmARM(parm);
+        case PhiNode phi    -> new PhiNode(phi);
+        case ProjNode prj   -> new ProjARM(prj);
+        case ReadOnlyNode read  -> new ReadOnlyNode(read);
+        case ReturnNode ret -> new RetARM(ret,ret.fun());
+        case SarNode sar    -> asr(sar);
+        case ShlNode shl    -> lsl(shl);
+        case ShrNode shr    -> lsr(shr);
+        case StartNode start -> new StartNode(start);
+        case StopNode stop  -> new StopNode(stop);
+        case StoreNode st   -> st(st);
+        case SubFNode subf  -> new SubFARM(subf);
+        case SubNode sub    -> sub(sub);
+        case ToFloatNode tfn-> new I2F8ARM(tfn);
+        case XorNode xor    -> xor(xor);
+
+        case LoopNode loop  -> new LoopNode(loop);
+        case RegionNode region-> new RegionNode(region);
+        default -> throw Utils.TODO();
+        };
+    }
+
+    private Node cmp(BoolNode bool){
+        Node cmp = _cmp(bool);
+        return new SetARM(cmp, invert(bool.op()));
+    }
+    private Node _cmp(BoolNode bool) {
+        if( bool instanceof BoolNode.EQF ||
+            bool instanceof BoolNode.LTF ||
+            bool instanceof BoolNode.LEF )
+            return new CmpFARM(bool);
+        return bool.in(2) instanceof ConstantNode con && con._con instanceof SONTypeInteger ti
+            ? new CmpIARM(bool, (int)ti.value())
+            : new CmpARM(bool);
+    }
+
+    private Node ld(LoadNode ld) {
+        return new LoadARM(address(ld), ld.ptr(), idx, off);
+    }
+
+    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(SONTypeInteger.ZERO)));
+        return new BranchARM(iff, invert(((BoolNode)iff.in(1)).op()));
+    }
+
+    private Node add(AddNode add) {
+        return add.in(2) instanceof ConstantNode off && off._con instanceof SONTypeInteger ti && imm12(ti)
+            ? new AddIARM(add, (int)ti.value())
+            : new AddARM(add);
+    }
+
+    private Node sub(SubNode sub) {
+        return sub.in(2) instanceof ConstantNode con && con._con instanceof SONTypeInteger ti && imm12(ti)
+            ? new SubIARM(sub, (int)ti.value())
+            : new SubARM(sub);
+    }
+
+    private Node con(ConstantNode con) {
+        if( !con._con.isConstant() ) return new ConstantNode( con ); // Default unknown caller inputs
+        return switch( con._con ) {
+        case SONTypeInteger ti  -> new IntARM(con);
+        case SONTypeFloat   tf  -> new FloatARM(con);
+        case SONTypeFunPtr  tfp -> new TFPARM(con);
+        case SONTypeMemPtr tmp -> new ConstantNode(con);
+        case SONTypeNil tn  -> throw Utils.TODO();
+        // TOP, BOTTOM, XCtrl, Ctrl, etc.  Never any executable code.
+        case SONType t -> new ConstantNode(con);
+        };
+    }
+
+    private Node call(CallNode call){
+        return call.fptr() instanceof ConstantNode con && con._con instanceof SONTypeFunPtr tfp
+            ? new CallARM(call, tfp)
+            : new CallRRARM(call);
+    }
+
+    private Node or(OrNode or) {
+        int imm12;
+        return or.in(2) instanceof ConstantNode off && off._con instanceof SONTypeInteger ti && (imm12 = imm12Logical(ti)) != -1
+            ? new OrIARM(or, imm12)
+            : new OrARM(or);
+    }
+
+    private Node xor(XorNode xor) {
+        int imm12;
+        return xor.in(2) instanceof ConstantNode off && off._con instanceof SONTypeInteger ti && (imm12 = imm12Logical(ti)) != -1
+                ? new XorIARM(xor, imm12)
+                : new XorARM(xor);
+    }
+
+    private Node and(AndNode and) {
+        int imm12;
+        return and.in(2) instanceof ConstantNode off && off._con instanceof SONTypeInteger ti && (imm12 = imm12Logical(ti)) != -1
+                ? new AndIARM(and, imm12)
+                : new AndARM(and);
+    }
+
+    private Node asr(SarNode asr) {
+        return asr.in(2) instanceof ConstantNode off && off._con instanceof SONTypeInteger ti && ti.value() >= 0 && ti.value() < 63
+                ? new AsrIARM(asr, (int)ti.value())
+                : new AsrARM(asr);
+    }
+
+    private Node lsl(ShlNode lsl) {
+        return lsl.in(2)  instanceof ConstantNode off && off._con instanceof SONTypeInteger ti && ti.value() >= 0 && ti.value() < 63
+                ? new LslIARM(lsl, (int)ti.value())
+                : new LslARM(lsl);
+    }
+
+    private Node lsr(ShrNode lsr) {
+        return lsr.in(2)  instanceof ConstantNode off && off._con instanceof SONTypeInteger ti && ti.value() >= 0 && ti.value() < 63
+                ? new LsrIARM(lsr, (int)ti.value())
+                : new LsrARM(lsr);
+    }
+
+
+    private static int off;
+    private static Node idx;
+    private Node st(StoreNode st) {
+        Node xval = st.val() instanceof ConstantNode con && con._con == SONTypeInteger.ZERO ? null : st.val();
+        return new StoreARM(address(st),st.ptr(),idx,off,xval);
+    }
+
+    // Gather 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 <N extends MemOpNode> N address(N mop ) {
+        off = 0;  // Reset
+        idx = 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 SONTypeMemPtr; // Base ptr always, not some derived
+        if( mop.off() instanceof ConstantNode con && con._con instanceof SONTypeInteger ti && imm9(ti) ) {
+            off = (int)ti.value();
+            assert off == ti.value(); // In 32-bit range
+        } else {
+            idx = mop.off();
+        }
+        return mop;
+    }
+
+
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/AUIPC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/AUIPC.java
new file mode 100644
index 0000000..5a848a1
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/AUIPC.java
@@ -0,0 +1,41 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.ConstantNode;
+import com.compilerprogramming.ezlang.compiler.nodes.MachNode;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFunPtr;
+
+// Add upper 20bits to PC.  Immediate comes from the relocation info.
+public class AUIPC extends ConstantNode implements MachNode, RIPRelSize {
+    AUIPC( SONTypeFunPtr tfp ) { super(tfp); }
+    @Override public RegMask regmap(int i) { return null; }
+    @Override public RegMask outregmap() { return riscv.WMASK; }
+    @Override public boolean isClone() { return true; }
+    @Override public AUIPC copy() { return new AUIPC((SONTypeFunPtr)_con); }
+    @Override public String op() { return "auipc"; }
+    @Override public void encoding( Encoding enc ) {
+        enc.relo(this);
+        short dst = enc.reg(this);
+        enc.add4(riscv.u_type(0x17, dst, 0));
+    }
+
+    // Delta is from opcode start, but X86 measures from the end of the 5-byte encoding
+    @Override public byte encSize(int delta) { return 4; }
+
+    // Delta is from opcode start
+    @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) {
+        //short rpc = enc.reg(this);
+        //// High half is where the TFP constant used to be, the last input
+        //short auipc = enc.reg(in(_inputs._len-1));
+        //enc.patch4(opStart,riscv.i_type(0x67, rpc, 0, auipc, delta));
+        throw Utils.TODO();
+    }
+
+    @Override public void asm(CodeGen code, SB sb) {
+        String reg = code.reg(this);
+        sb.p(reg).p(" = PC+#");
+        if( _con == null ) sb.p("---");
+        else _con.print(sb);
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/AddFRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/AddFRISC.java
new file mode 100644
index 0000000..7e9058a
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/AddFRISC.java
@@ -0,0 +1,17 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv;
+
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+// fadd.d
+public class AddFRISC extends MachConcreteNode implements MachNode {
+    AddFRISC(Node addf) {super(addf);}
+    @Override public String op() { return "addf"; }
+    @Override public RegMask regmap(int i) { assert i==1 || i==2; return riscv.FMASK; }
+    @Override public RegMask outregmap() { return riscv.FMASK; }
+    @Override public void encoding( Encoding enc ) { riscv.rf_type(enc,this,riscv.RM.RNE,1); }
+    @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)));
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/AddIRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/AddIRISC.java
new file mode 100644
index 0000000..1de9018
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/AddIRISC.java
@@ -0,0 +1,21 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv;
+
+import com.compilerprogramming.ezlang.compiler.Utils;
+import com.compilerprogramming.ezlang.compiler.nodes.Node;
+
+public class AddIRISC extends ImmRISC {
+    // Used to inst-selection as a direct match against an ideal Add/Sub
+    public AddIRISC( Node add, int imm12, boolean pop ) { super(add,imm12,pop); }
+    @Override public String op() { return "addi"; }
+    @Override public String glabel() { return "+"; }
+    @Override int opcode() {  return riscv.I_TYPE; }
+    @Override int func3() {return 0;}
+    @Override public AddIRISC copy() {
+        // Clone the AddI, using the same inputs-only code used during inst select.
+        // Output edges are missing.
+        AddIRISC add = new AddIRISC(in(1),_imm12,false);
+        // Copys happen when output edges should be valid, so correct missing output edges.
+        in(1)._outputs.add(add);
+        return add;
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/AddRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/AddRISC.java
new file mode 100644
index 0000000..4b163a2
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/AddRISC.java
@@ -0,0 +1,21 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv;
+
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+public class AddRISC extends MachConcreteNode implements MachNode {
+    public AddRISC( Node add ) { super(add); }
+    AddRISC( Node base, Node off ) {
+        super(new Node[3]);
+        _inputs.set(1,base);
+        _inputs.set(2,off );
+    }
+    @Override public String op() { return "add"; }
+    @Override public RegMask regmap(int i) { return riscv.RMASK; }
+    @Override public RegMask outregmap() { return riscv.WMASK; }
+    @Override public void encoding( Encoding enc ) { riscv.r_type(enc,this,0,0);  }
+    @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)));
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/AndIRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/AndIRISC.java
new file mode 100644
index 0000000..b37f960
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/AndIRISC.java
@@ -0,0 +1,12 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv;
+
+import com.compilerprogramming.ezlang.compiler.Utils;
+import com.compilerprogramming.ezlang.compiler.nodes.Node;
+
+public class AndIRISC extends ImmRISC {
+    AndIRISC( Node and, int imm) { super(and,imm); }
+    @Override public String op() { return "andi"; }
+    @Override public String glabel() { return "&"; }
+    @Override int opcode() {  return riscv.I_TYPE; }
+    @Override int func3() {return 7;}
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/AndRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/AndRISC.java
new file mode 100644
index 0000000..01677f0
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/AndRISC.java
@@ -0,0 +1,16 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv;
+
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+public class AndRISC extends MachConcreteNode implements MachNode {
+    AndRISC(Node and) { super(and); }
+    @Override public String op() { return "and"; }
+    @Override public RegMask regmap(int i) { return riscv.RMASK; }
+    @Override public RegMask outregmap() { return riscv.WMASK; }
+    @Override public void encoding( Encoding enc ) { riscv.r_type(enc,this,7,0);  }
+    @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)));
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/BranchRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/BranchRISC.java
new file mode 100644
index 0000000..a6bfb29
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/BranchRISC.java
@@ -0,0 +1,76 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+import java.util.BitSet;
+
+// Conditional branch such as: BEQ
+public class BranchRISC extends IfNode implements MachNode, RIPRelSize {
+    String _bop;
+    // label is obtained implicitly
+    public BranchRISC( IfNode iff, String bop, Node n1, Node n2 ) {
+        super(iff);
+        _bop = bop;
+        _inputs.setX(1,n1);
+        _inputs.setX(2,n2);
+    }
+
+    @Override public String op() { return "b"+_bop; }
+    @Override public String label() { return op(); }
+    @Override public String comment() { return "L"+cproj(1)._nid; }
+    @Override public RegMask regmap(int i) { return riscv.RMASK; }
+    @Override public RegMask outregmap() { return null; }
+    @Override public void invert() {
+        if( _bop.equals("<") || _bop.equals("<=") )
+            swap12();           // Cannot invert the test, so swap the operands
+        else
+            _bop = invert(_bop);
+    }
+
+    @Override public StringBuilder _print1(StringBuilder sb, BitSet visited) {
+        sb.append("if( ");
+        if( in(1)==null ) sb.append("0");
+        else in(1)._print0(sb,visited);
+        sb.append(_bop);
+        if( in(2)==null ) sb.append("0");
+        else in(2)._print0(sb,visited);
+        return sb.append(" )");
+    }
+
+    @Override public void encoding( Encoding enc ) {
+        enc.jump(this,cproj(0));
+        // Todo: relocs (for offset - immf)
+        short src1 = enc.reg(in(1));
+        short src2 = in(2)==null ? (short)riscv.ZERO : enc.reg(in(2));
+        enc.add4(riscv.b_type(riscv.OP_BRANCH, riscv.jumpop(_bop), src1, src2, 0));
+    }
+
+    // Delta is from opcode start
+    @Override public byte encSize(int delta) {
+        if( -4*1024 <= delta && delta < 4*1024 ) return 4;
+        // 2 word encoding needs a tmp register, must teach RA
+        throw Utils.TODO();
+    }
+
+    // Delta is from opcode start
+    @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) {
+        short src1 = enc.reg(in(1));
+        short src2 = in(2)==null ? (short)riscv.ZERO : enc.reg(in(2));
+        if( opLen==4 ) {
+            enc.patch4(opStart,riscv.b_type(riscv.OP_BRANCH, riscv.jumpop(_bop), src1, src2, delta));
+        } else {
+            throw Utils.TODO();
+        }
+    }
+
+    @Override public void asm(CodeGen code, SB sb) {
+        String src1 = in(1)==null ? "#0" : code.reg(in(1));
+        String src2 = in(2)==null ? "#0" : code.reg(in(2));
+        sb.p(src1).p(" ").p(_bop).p(" ").p(src2).p(" ");
+        CFGNode prj = cproj(0);
+        while( prj.nOuts() == 1 )
+            prj = prj.uctrl();       // Skip empty blocks
+        sb.p(label(prj));
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CallEndRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CallEndRISC.java
new file mode 100644
index 0000000..32cbb5b
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CallEndRISC.java
@@ -0,0 +1,23 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv;
+
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.CallEndNode;
+import com.compilerprogramming.ezlang.compiler.nodes.MachNode;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFunPtr;
+
+public class CallEndRISC extends CallEndNode implements MachNode {
+    final SONTypeFunPtr _tfp;
+    CallEndRISC( CallEndNode cend ) {
+        super(cend);
+        _tfp = (SONTypeFunPtr)(cend.call().fptr()._type);
+    }
+    @Override public String op() { return "cend"; }
+    @Override public String label() { return op(); }
+    @Override public RegMask regmap(int i) { return null; }
+    @Override public RegMask outregmap() { return null; }
+    @Override public RegMask outregmap(int idx) { return idx == 2 ? riscv.retMask(_tfp,2) : null; }
+    @Override public RegMask killmap() { return riscv.riscCallerSave(); }
+    @Override public void encoding( Encoding enc ) { }
+    @Override public void asm(CodeGen code, SB sb) {  }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CallRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CallRISC.java
new file mode 100644
index 0000000..67ff05d
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CallRISC.java
@@ -0,0 +1,62 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFunPtr;
+
+public class CallRISC extends CallNode implements MachNode, RIPRelSize {
+    final SONTypeFunPtr _tfp;
+    final String _name;
+    CallRISC( CallNode call, SONTypeFunPtr tfp ) {
+        super(call);
+        assert tfp.isConstant();
+        _inputs.pop(); // Pop constant target
+        _tfp = tfp;
+        _name = CodeGen.CODE.link(tfp)._name;
+    }
+
+    @Override public String op() { return "call"; }
+    @Override public String label() { return op(); }
+    @Override public RegMask regmap(int i) {
+        // Last call input is AUIPC
+        if( i == nIns()-1 ) return riscv.RMASK;
+        return riscv.callInMask(_tfp,i);
+    }
+    @Override public RegMask outregmap() { return riscv.RPC_MASK; }
+    @Override public String name() { return _name; }
+    @Override public SONTypeFunPtr tfp() { return _tfp; }
+
+    @Override public void encoding( Encoding enc ) {
+        // Short form +/-4K:  beq r0,r0,imm12
+        // Long form:  auipc rX,imm20/32; jal r0,[rX+imm12/32]
+        enc.relo(this);
+        short rpc = enc.reg(this);
+        enc.add4(riscv.j_type(riscv.J_JAL, rpc, 0));
+    }
+
+    // Delta is from opcode start
+    @Override public byte encSize(int delta) {
+        if( -(1L<<20) <= delta && delta < (1L<<20) ) return 4;
+        // 2 word encoding needs a tmp register, must teach RA
+        throw Utils.TODO();
+    }
+
+    // Delta is from opcode start
+    @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) {
+        short rpc = enc.reg(this);
+        if( opLen==4 ) {
+            enc.patch4(opStart,riscv.j_type(riscv.J_JAL, rpc, delta));
+        } else {
+            throw Utils.TODO();
+        }
+    }
+
+    @Override public void asm(CodeGen code, SB sb) {
+        sb.p(_name).p("  ");
+        for( int i=0; i<nargs(); i++ )
+            sb.p(code.reg(arg(i+2))).p(", ");
+        sb.unchar(2).p("  ").p(code.reg(fptr()));
+
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CallRRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CallRRISC.java
new file mode 100644
index 0000000..c3a7757
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CallRRISC.java
@@ -0,0 +1,31 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+public class CallRRISC extends CallNode implements MachNode {
+    CallRRISC( CallNode call ) { super(call); }
+    @Override public String op() { return "callr"; }
+    @Override public String label() { return op(); }
+    @Override public RegMask regmap(int i) {
+        return i==_inputs._len-1
+            ? riscv.RMASK                // Function call target
+            : riscv.callInMask(tfp(),i); // Normal argument
+    }
+    @Override public RegMask outregmap() { return null; }
+
+    @Override public void encoding( Encoding enc ) {
+        short rpc = enc.reg(this);
+        short src = enc.reg(in(_inputs._len-1));
+        int body = riscv.i_type(0x67, rpc, 0, src, 0);
+        enc.add4(body);
+    }
+
+    @Override public void asm(CodeGen code, SB sb) {
+        sb.p(code.reg(fptr())).p("  ");
+        for( int i=0; i<nargs(); i++ )
+            sb.p(code.reg(arg(i))).p("  ");
+        sb.unchar(2);
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CastRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CastRISC.java
new file mode 100644
index 0000000..0bc06b3
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CastRISC.java
@@ -0,0 +1,18 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv;
+
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.codegen.CodeGen;
+import com.compilerprogramming.ezlang.compiler.codegen.Encoding;
+import com.compilerprogramming.ezlang.compiler.codegen.RegMask;
+import com.compilerprogramming.ezlang.compiler.nodes.CastNode;
+import com.compilerprogramming.ezlang.compiler.nodes.MachNode;
+
+public class CastRISC extends CastNode implements MachNode {
+    CastRISC( CastNode cast ) { super(cast); }
+    @Override public String op() { return label(); }
+    @Override public RegMask regmap(int i) { assert i==1; return RegMask.FULL; }
+    @Override public RegMask outregmap() { return RegMask.FULL; }
+    @Override public int twoAddress( ) { return 1; }
+    @Override public void encoding( Encoding enc ) { }
+    @Override public void asm(CodeGen code, SB sb) { _t.print(sb.p(code.reg(in(1))).p(" isa ")); }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/DivFRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/DivFRISC.java
new file mode 100644
index 0000000..89ae6e6
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/DivFRISC.java
@@ -0,0 +1,19 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv;
+
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+// fdiv.d
+public class DivFRISC extends MachConcreteNode implements MachNode{
+    DivFRISC(Node divf) {super(divf);}
+    @Override public String op() { return "divf"; }
+    @Override public RegMask regmap(int i) { assert i==1 || i==2; return riscv.FMASK; }
+    @Override public RegMask outregmap() { return riscv.FMASK; }
+    @Override public void encoding( Encoding enc ) { riscv.rf_type(enc,this,riscv.RM.RUP,13); }
+    // 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)));
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/DivRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/DivRISC.java
new file mode 100644
index 0000000..c7482d7
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/DivRISC.java
@@ -0,0 +1,17 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv;
+
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+public class DivRISC extends MachConcreteNode implements MachNode{
+    DivRISC(Node div) {super(div);}
+    @Override public String op() { return "div"; }
+    @Override public RegMask regmap(int i) { return riscv.RMASK; }
+    @Override public RegMask outregmap() { return riscv.WMASK; }
+    @Override public void encoding( Encoding enc ) { riscv.r_type(enc,this,4,1);  }
+    @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)));
+    }
+
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/FltRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/FltRISC.java
new file mode 100644
index 0000000..0a477dd
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/FltRISC.java
@@ -0,0 +1,44 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv;
+
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.Utils;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.ConstantNode;
+import com.compilerprogramming.ezlang.compiler.nodes.MachNode;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFloat;
+
+public class FltRISC extends ConstantNode implements MachNode, RIPRelSize {
+    FltRISC(ConstantNode con) { super(con); }
+    @Override public String op() { return "ld8"; }
+    @Override public RegMask regmap(int i) { return null; }
+    @Override public RegMask outregmap() { return riscv.FMASK; }
+    @Override public boolean isClone() { return true; }
+    @Override public FltRISC copy() { return new FltRISC(this); }
+
+    @Override public void encoding( Encoding enc ) {
+        enc.largeConstant(this,_con);
+        short dst = (short)(enc.reg(this) - riscv.F_OFFSET);
+        // AUIPC dst,#hi20_constant_pool
+        enc.add4(riscv.u_type(0b0010111, dst, 0));
+        // Load dst,[dst+#low12_constant_pool]
+        enc.add4(riscv.i_type(0x07, dst, 0x03, dst, 0));
+    }
+
+    // Delta is from opcode start.
+    // TODO: always size 8?
+    @Override public byte encSize(int delta) { return 8;  }
+
+    // Delta is from opcode start
+    @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) {
+        short dst = (short)(enc.reg(this) - riscv.F_OFFSET);
+        // AUIPC dst,#hi20_constant_pool
+        enc.patch4(opStart  , riscv.u_type(0b0010111, dst, delta));
+        // Load dst,[dst+#low12_constant_pool]
+        enc.patch4(opStart+4, riscv.i_type(0x07, dst, 0x03, dst, delta));
+    }
+
+    @Override public void asm(CodeGen code, SB sb) {
+        _con.print(sb.p(code.reg(this)).p(" = #"));
+    }
+
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/FunRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/FunRISC.java
new file mode 100644
index 0000000..6f7b98d
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/FunRISC.java
@@ -0,0 +1,36 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv;
+
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.nodes.FunNode;
+import com.compilerprogramming.ezlang.compiler.nodes.MachNode;
+
+public class FunRISC extends FunNode implements MachNode {
+    FunRISC(FunNode fun) {super(fun);}
+    @Override public String op() { return "addi"; }
+    @Override public RegMask regmap(int i) { return null; }
+    @Override public RegMask outregmap() { return null; }
+    @Override public void postSelect(CodeGen code) { code.link(this);  }
+
+    // Actual stack layout is up to each CPU.
+    // X86, with too many args & spills:
+    // | CALLER |
+    // |  argN  | // slot 1, required by callER
+    // +--------+
+    // |  RPC   | // slot 0, required by callER
+    // | callee | // slot 3, callEE
+    // | callee | // slot 2, callEE
+    // |  PAD16 |
+    // +--------+
+    @Override public void encoding( Encoding enc ) {
+        // Stack frame size: _maxSlot - max(arg), padded to 16.
+        _maxArgSlot = riscv.maxSlot(enc._fun.sig());
+        _frameAdjust = (short) (_maxSlot+1 - _maxArgSlot);
+        if( _frameAdjust == 0 ) return; // Skip if no frame adjust
+        enc.add4(riscv.i_type(riscv.I_TYPE, riscv.SP, 0, riscv.SP, (_frameAdjust*8) & 0xFFF));
+    }
+
+    @Override public void asm(CodeGen code, SB sb) {
+        sb.p("rsp += #").p(_frameAdjust*8);
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/I2F8RISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/I2F8RISC.java
new file mode 100644
index 0000000..00854e4
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/I2F8RISC.java
@@ -0,0 +1,23 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+// fcvt.d.w
+// Converts a 32-bit signed integer, in integer register rs1 into a double-precision floating-point number in floating-point register rd.
+public class I2F8RISC extends MachConcreteNode implements MachNode {
+    I2F8RISC(Node i2f8) {super(i2f8);}
+    @Override public String op() { return "cvtf"; }
+    @Override public RegMask regmap(int i) { assert i==1; return riscv.RMASK; }
+    @Override public RegMask outregmap() { return riscv.FMASK; }
+    @Override public void encoding( Encoding enc ) {
+        short dst  = (short)(enc.reg(this )-riscv.F_OFFSET);
+        short src1 =         enc.reg(in(1));
+        int body = riscv.r_type(0b1010011,dst,riscv.RM.RNE.ordinal(),src1,0,0x69);
+        enc.add4(body);
+    }
+    @Override public void asm(CodeGen code, SB sb) {
+        sb.p(code.reg(this)).p(" = ").p("(flt)").p(code.reg(in(1)));
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/ImmRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/ImmRISC.java
new file mode 100644
index 0000000..9257b4d
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/ImmRISC.java
@@ -0,0 +1,54 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeInteger;
+import java.util.BitSet;
+
+abstract public class ImmRISC extends MachConcreteNode implements MachNode {
+    public final int _imm12;
+    ImmRISC( Node add, int imm12, boolean pop ) {
+        super(add);
+        _imm12 = imm12;
+        // If pop, we are moving from a ideal Node to MachNode; the ideal Node
+        // inputs got copied here, but we are absorbing the immediate into the
+        // MachNode and so do not want the constant input.
+        if( pop ) _inputs.pop();
+        // if NOT pop, we are the 2nd-half of a 2-part expansion, reset the
+        // input edges to no-control
+        else { _inputs.set(0,null); _inputs.setX(1,add); }
+    }
+    ImmRISC( Node add, int imm12 ) { this(add,imm12,true); }
+    @Override public RegMask regmap(int i) { return riscv.RMASK; }
+    @Override public RegMask outregmap() { return riscv.WMASK; }
+    @Override public boolean isClone() {
+        // Allow cloning of the 2nd half of a 2-part constant; this might
+        // trigger cloning the first part also.
+        return in(1) instanceof LUI || in(1) instanceof AUIPC;
+    }
+
+    @Override public StringBuilder _print1(StringBuilder sb, BitSet visited) {
+        int imm12 = (_imm12<<20)>>20; // Sign extend 12 bits
+        in(1)._print0(sb.append("( "), visited);
+        return sb.append(String.format(" %s #%d )",glabel(),imm12));
+    }
+
+    abstract int opcode();
+    abstract int func3();
+
+    @Override public void encoding( Encoding enc ) {
+        short dst = enc.reg(this );
+        short src = enc.reg(in(1));
+        int body = riscv.i_type(opcode(), dst, func3(), src, _imm12 & 0xFFF);
+        enc.add4(body);
+    }
+
+    // General form: "addi  rd = rs1 + imm"
+    @Override public void asm(CodeGen code, SB sb) {
+        int imm12 = (_imm12<<20)>>20; // Sign extend 12 bits
+        sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" ").p(glabel()).p(" #").p(imm12);
+        if( in(1) instanceof LUI lui )
+            sb.p(" // #").hex4((int)(((SONTypeInteger)lui._con).value()) + imm12);
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/IntRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/IntRISC.java
new file mode 100644
index 0000000..635155e
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/IntRISC.java
@@ -0,0 +1,29 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.ConstantNode;
+import com.compilerprogramming.ezlang.compiler.nodes.MachNode;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeInteger;
+
+// 12-bit integer constant.  Larger constants are made up in the instruction
+// selection by adding with a LUI.
+public class IntRISC extends ConstantNode implements MachNode {
+    public IntRISC(ConstantNode con) { super(con); }
+    @Override public String op() { return "ldi"; }
+    @Override public RegMask regmap(int i) { return null; }
+    @Override public RegMask outregmap() { return riscv.WMASK; }
+    @Override public boolean isClone() { return true; }
+    @Override public IntRISC copy() { return new IntRISC(this); }
+    @Override public void encoding( Encoding enc ) {
+        short dst  = enc.reg(this);
+        SONTypeInteger ti = (SONTypeInteger)_con;
+        // Explicit truncation of larger immediates; this will sign-extend on
+        // load and this is handled during instruction selection.
+        enc.add4(riscv.i_type(riscv.I_TYPE, dst, 0, riscv.ZERO, (int)(ti.value() & 0xFFF)));
+    }
+    @Override public void asm(CodeGen code, SB sb) {
+        String reg = code.reg(this);
+        _con.print(sb.p(reg).p(" = #"));
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/LUI.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/LUI.java
new file mode 100644
index 0000000..a85eba9
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/LUI.java
@@ -0,0 +1,32 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.ConstantNode;
+import com.compilerprogramming.ezlang.compiler.nodes.MachNode;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeInteger;
+
+// Load upper 20bits.
+public class LUI extends ConstantNode implements MachNode {
+    public LUI( int imm20 ) {
+        super(SONTypeInteger.constant(imm20));
+        assert riscv.imm20Exact((SONTypeInteger)_con);
+    }
+    @Override public RegMask regmap(int i) { return null; }
+    @Override public RegMask outregmap() { return riscv.WMASK; }
+    @Override public boolean isClone() { return true; }
+    @Override public LUI copy() { return new LUI((int)((SONTypeInteger)_con).value()); }
+    @Override public String op() { return "lui"; }
+    @Override public void encoding( Encoding enc ) {
+        long x = ((SONTypeInteger)_con).value();
+        int imm20 = (int)(x>>12) & 0xFFFFF;
+        short dst = enc.reg(this);
+        int lui = riscv.u_type(0x17, dst, imm20);
+        enc.add4(lui);
+    }
+
+    @Override public void asm(CodeGen code, SB sb) {
+        String reg = code.reg(this);
+        sb.p(reg).p(" = #").hex4((int)(((SONTypeInteger)_con).value()));
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/LoadRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/LoadRISC.java
new file mode 100644
index 0000000..d3e6479
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/LoadRISC.java
@@ -0,0 +1,28 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+// Load memory addressing on RISC
+// Support imm, reg(direct), or reg+off(indirect) addressing
+// Base = base - base pointer, offset is added to base
+// idx  = null
+// off  = off - imm12 added to base
+public class LoadRISC extends MemOpRISC {
+    LoadRISC(LoadNode ld, Node base, int off) { super(ld, base, off, null); }
+    @Override public String op() { return "ld" +_sz; }
+    @Override public RegMask regmap(int i) { return riscv.RMASK; }
+    @Override public RegMask outregmap() { return riscv.MEM_MASK; }
+    @Override public void encoding( Encoding enc ) {
+        short dst = xreg(enc);
+        short ptr = enc.reg(ptr());
+        int body = riscv.i_type(opcode(enc), dst, func3(), ptr, _off);
+        enc.add4(body);
+    }
+
+    @Override public void asm(CodeGen code, SB sb) {
+        sb.p(code.reg(this)).p(",");
+        asm_address(code,sb);
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/MemOpRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/MemOpRISC.java
new file mode 100644
index 0000000..23988e6
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/MemOpRISC.java
@@ -0,0 +1,76 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv;
+
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.Utils;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+import com.compilerprogramming.ezlang.compiler.sontypes.*;
+import java.util.BitSet;
+
+public abstract class MemOpRISC extends MemOpNode implements MachNode {
+    final int _off;             // Limit 12 bits
+    final char _sz = (char)('0'+(1<<_declaredType.log_size()));
+    MemOpRISC(MemOpNode mop, Node base, int off, Node val) {
+        super(mop,mop);
+        assert base._type instanceof SONTypeMemPtr;
+        _inputs.setX(2, base ); // Base can be an Add, is no longer raw object base
+        _inputs.setX(3, null);  // Never an index
+        _inputs.setX(4, val );
+        _off = off;
+    }
+
+    @Override public String label() { return op(); }
+    Node val() { return in(4); } // Only for stores
+
+    @Override public StringBuilder _printMach(StringBuilder sb, BitSet visited) { return sb.append(".").append(_name); }
+
+    @Override public SONType compute() { throw Utils.TODO(); }
+    @Override public Node idealize() { throw Utils.TODO(); }
+
+    // func3 is based on load/store size and extend
+    int func3() {
+        int func3 = -1;
+        if( _declaredType == SONTypeInteger. I8 ) func3=0; // LB   SB
+        if( _declaredType == SONTypeInteger.I16 ) func3=1; // LH   SH
+        if( _declaredType == SONTypeInteger.I32 ) func3=2; // LW   SW
+        if( _declaredType == SONTypeInteger.BOT ) func3=3; // LD   SD
+        if( _declaredType == SONTypeInteger. U8 ) func3=4; // LBU
+        if( _declaredType == SONTypeInteger.BOOL) func3=4; // LBU
+        if( _declaredType == SONTypeInteger.U16 ) func3=5; // LHU
+        if( _declaredType == SONTypeInteger.U32 ) func3=6; // LWU
+        // float
+        if(_declaredType == SONTypeFloat.F32) func3 = 2; // fLW   fSW
+        if(_declaredType == SONTypeFloat.F64) func3 = 3; // fLD   fSD
+        if( _declaredType instanceof SONTypeMemPtr ) func3=6; // 4 byte pointers, assumed unsigned?
+        if( func3 == -1 ) throw Utils.TODO();
+        return func3;
+    }
+
+    // 7 bits, 00 000 11 or 00 001 11 for FP
+    int opcode(Encoding enc) {
+        if( enc.reg(this) < riscv.F_OFFSET ) return 3;
+        else if( this instanceof StoreRISC )
+            // opcode is the same for 32 bit and 64 bits
+            return 39;
+        return 7;
+    }
+    short xreg(Encoding enc) {
+        short xreg = enc.reg(this );
+        return xreg < riscv.F_OFFSET ? xreg : (short)(xreg-riscv.F_OFFSET);
+    }
+
+    // Register mask allowed on input i.
+    @Override public RegMask regmap(int i) {
+        // 0 - ctrl
+        // 1 - memory
+        if( i==2 ) return riscv.RMASK;    // base
+        // 2 - index
+        if( i==4 ) return riscv.MEM_MASK; // value
+        throw Utils.TODO();
+    }
+
+    SB asm_address(CodeGen code, SB sb) {
+        sb.p("[").p(code.reg(ptr())).p("+");
+        return sb.p(_off).p("]");
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/MulFRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/MulFRISC.java
new file mode 100644
index 0000000..56cbf7b
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/MulFRISC.java
@@ -0,0 +1,17 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv;
+
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+// fmul.d
+public class MulFRISC extends MachConcreteNode implements MachNode {
+    MulFRISC(Node mulf) {super(mulf);}
+    @Override public String op() { return "mulf"; }
+    @Override public RegMask regmap(int i) { assert i==1 || i==2; return riscv.FMASK; }
+    @Override public RegMask outregmap() { return riscv.FMASK; }
+    @Override public void encoding( Encoding enc ) { riscv.rf_type(enc,this,riscv.RM.RNE,9); }
+    @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)));
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/MulRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/MulRISC.java
new file mode 100644
index 0000000..1930da7
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/MulRISC.java
@@ -0,0 +1,17 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv;
+
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+// mulh signed multiply instruction(no-imm form)
+public class MulRISC extends MachConcreteNode implements MachNode{
+    MulRISC(Node mul) {super(mul);}
+    @Override public String op() { return "mul"; }
+    @Override public RegMask regmap(int i) { assert i==1 || i==2; return riscv.RMASK; }
+    @Override public RegMask outregmap() { return riscv.WMASK; }
+    @Override public void encoding( Encoding enc ) { riscv.r_type(enc,this,0,1);  }
+    @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)));
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/NewRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/NewRISC.java
new file mode 100644
index 0000000..a561f3b
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/NewRISC.java
@@ -0,0 +1,34 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+import com.compilerprogramming.ezlang.compiler.print.ASMPrinter;
+
+public class NewRISC extends NewNode implements MachNode {
+    // A pre-zeroed chunk of memory.
+    NewRISC( NewNode nnn ) { super(nnn); }
+    @Override public String op() { return "alloc"; }
+    @Override public RegMask regmap(int i) {
+        return i==1 ? riscv.A0_MASK : null; // Size input or mem aliases
+    }
+    @Override public RegMask outregmap(int i) { return i == 1 ? riscv.A0_MASK : null; }
+    @Override public RegMask outregmap() { return null; }
+    @Override public RegMask killmap() { return riscv.riscCallerSave(); }
+
+    // Encoding is appended into the byte array; size is returned
+    @Override public void encoding( Encoding enc ) {
+        // Generic external encoding; 2 ops.
+        enc.external(this,"calloc");
+        // A1 is a caller-save, allowed to crush building external address
+        // auipc
+        enc.add4(riscv.u_type(0x17, riscv.A1, 0));
+        enc.add4(riscv.i_type(0x67, riscv.RPC, 0, riscv.A1, 0));
+    }
+
+    // General form: "alloc #bytes  PC"
+    @Override public void asm(CodeGen code, SB sb) {
+        sb.p("auipc a1=#calloc\n");
+        sb.p("call  a1+#calloc, a0");
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/NotRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/NotRISC.java
new file mode 100644
index 0000000..94d2a92
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/NotRISC.java
@@ -0,0 +1,16 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv;
+
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.NotNode;
+
+public class NotRISC extends ImmRISC {
+    NotRISC(NotNode not) { super(not,1); }
+    @Override public RegMask regmap(int i) { return riscv.RMASK; }
+    @Override public RegMask outregmap() { return riscv.RMASK;  }
+    @Override public String op() { return "not"; }
+    // sltiu: x <u 1 ? 1 : 0;  only zero is unsigned-less-than 1
+    @Override int opcode() { return 19; }
+    @Override int func3() { return 2; }
+    @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(this)); }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/OrIRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/OrIRISC.java
new file mode 100644
index 0000000..5f42d2c
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/OrIRISC.java
@@ -0,0 +1,12 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv;
+
+import com.compilerprogramming.ezlang.compiler.Utils;
+import com.compilerprogramming.ezlang.compiler.nodes.Node;
+
+public class OrIRISC extends ImmRISC {
+    OrIRISC( Node and, int imm) { super(and,imm); }
+    @Override int opcode() {  return riscv.I_TYPE; }
+    @Override int func3() {  return 6; }
+    @Override public String glabel() { return "|"; }
+    @Override public String op() { return "ori"; }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/OrRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/OrRISC.java
new file mode 100644
index 0000000..2a3c298
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/OrRISC.java
@@ -0,0 +1,16 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv;
+
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+public class OrRISC extends MachConcreteNode implements MachNode {
+    OrRISC(Node or) { super(or); }
+    @Override public String op() { return "or"; }
+    @Override public RegMask regmap(int i) { return riscv.RMASK; }
+    @Override public RegMask outregmap() { return riscv.WMASK; }
+    @Override public void encoding( Encoding enc ) { riscv.r_type(enc,this,6,0);  }
+    @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)));
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/ParmRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/ParmRISC.java
new file mode 100644
index 0000000..07b6884
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/ParmRISC.java
@@ -0,0 +1,17 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv;
+
+import com.compilerprogramming.ezlang.compiler.codegen.Encoding;
+import com.compilerprogramming.ezlang.compiler.codegen.RegMask;
+import com.compilerprogramming.ezlang.compiler.nodes.MachNode;
+import com.compilerprogramming.ezlang.compiler.nodes.ParmNode;
+
+public class ParmRISC extends ParmNode implements MachNode {
+    final RegMask _rmask;
+    ParmRISC(ParmNode parm) {
+        super(parm);
+        _rmask = riscv.callInMask(fun().sig(),_idx);
+    }
+    @Override public RegMask regmap(int i) { return null; }
+    @Override public RegMask outregmap() { return _rmask; }
+    @Override public void encoding( Encoding enc ) { }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/ProjRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/ProjRISC.java
new file mode 100644
index 0000000..cfef87a
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/ProjRISC.java
@@ -0,0 +1,15 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv;
+
+import com.compilerprogramming.ezlang.compiler.codegen.Encoding;
+import com.compilerprogramming.ezlang.compiler.codegen.RegMask;
+import com.compilerprogramming.ezlang.compiler.nodes.MachNode;
+import com.compilerprogramming.ezlang.compiler.nodes.ProjNode;
+
+public class ProjRISC extends ProjNode implements MachNode {
+    ProjRISC(ProjNode p) { super(p); }
+    @Override public RegMask regmap(int i) { return null; }
+    @Override public RegMask outregmap() {
+        return ((MachNode)in(0)).outregmap(_idx);
+    }
+    @Override public void encoding( Encoding enc ) { }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/RetRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/RetRISC.java
new file mode 100644
index 0000000..14dba81
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/RetRISC.java
@@ -0,0 +1,50 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.CodeGen;
+import com.compilerprogramming.ezlang.compiler.codegen.Encoding;
+import com.compilerprogramming.ezlang.compiler.codegen.RegMask;
+import com.compilerprogramming.ezlang.compiler.nodes.FunNode;
+import com.compilerprogramming.ezlang.compiler.nodes.ReturnNode;
+import com.compilerprogramming.ezlang.compiler.nodes.MachNode;
+
+public class RetRISC extends ReturnNode implements MachNode {
+    public RetRISC(ReturnNode ret, FunNode fun) {
+        super(ret, fun);
+        if( fun != null ) fun.setRet(this);
+    }
+    @Override public String op() {
+        return fun()._frameAdjust > 0 ? "addi" : "ret";
+    }
+    // Correct Nodes outside the normal edges
+    @Override public void postSelect(CodeGen code) {
+        FunNode fun = (FunNode)rpc().in(0);
+        _fun = fun;
+        fun.setRet(this);
+    }
+    @Override public RegMask regmap(int i) { return riscv.retMask(_fun.sig(),i); }
+    @Override public RegMask outregmap() { return null; }
+    @Override public void encoding( Encoding enc ) {
+        int frameAdjust = fun()._frameAdjust;
+        if( frameAdjust > 0 )
+            enc.add4(riscv.i_type(riscv.I_TYPE, riscv.SP, 0, riscv.SP, (frameAdjust*-8) & 0xFFF));
+        short rpc = enc.reg(rpc());
+        enc.add4(riscv.i_type(0x67, riscv.ZERO, 0, rpc, 0));
+    }
+
+    @Override public void asm(CodeGen code, SB sb) {
+        int frameAdjust = fun()._frameAdjust;
+        if( frameAdjust>0 )
+            sb.p("rsp += #").p(frameAdjust*-8).p("\nret");
+        // Post code-gen, just print the "ret"
+        if( code._phase.ordinal() <= CodeGen.Phase.RegAlloc.ordinal() )
+            // Prints return reg (either A0 or FA0), RPC (always R1) and then
+            // the callee-save registers.
+            for( int i=2; i<nIns(); i++ )
+                sb.p(code.reg(in(i))).p("  ");
+
+        // Post-allocation, if we did not get the expected return register, print what we got
+        else if( code._regAlloc.regnum(rpc()) != riscv.RPC )
+            sb.p("[").p(code.reg(rpc())).p("]");
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SetFRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SetFRISC.java
new file mode 100644
index 0000000..dff08f4
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SetFRISC.java
@@ -0,0 +1,33 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv;
+
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.BoolNode;
+import com.compilerprogramming.ezlang.compiler.nodes.MachConcreteNode;
+import com.compilerprogramming.ezlang.compiler.nodes.MachNode;
+
+// corresponds to feq.d = equal
+//                fle.d = less than or equal
+//                flt.d = less than
+public class SetFRISC extends MachConcreteNode implements MachNode {
+    final String _bop;          // One of <,<=,==
+    SetFRISC( BoolNode bool ) {
+        super(bool);
+        assert bool.isFloat();
+        _bop = bool.op();
+    }
+    @Override public String op() { return "set"+_bop; }
+    @Override public RegMask regmap(int i) { return riscv.FMASK; }
+    @Override public RegMask outregmap() { return riscv.WMASK; }
+    @Override public void encoding( Encoding enc ) {
+        short dst  =         enc.reg(this );
+        short src1 = (short)(enc.reg(in(1))-riscv.F_OFFSET);
+        short src2 = (short)(enc.reg(in(2))-riscv.F_OFFSET);
+        int body = riscv.r_type(0x53,dst,riscv.fsetop(_bop),src1,src2,0);
+        enc.add4(body);
+    }
+
+    @Override public void asm(CodeGen code, SB sb) {
+        sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" ").p(_bop).p(" ").p(code.reg(in(2)));
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SetIRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SetIRISC.java
new file mode 100644
index 0000000..fe388c2
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SetIRISC.java
@@ -0,0 +1,17 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv;
+
+import com.compilerprogramming.ezlang.compiler.Utils;
+import com.compilerprogramming.ezlang.compiler.nodes.Node;
+
+// corresponds to slti,sltiu
+public class SetIRISC extends ImmRISC {
+    final boolean _unsigned;    // slti vs sltiu
+    SetIRISC( Node src, int imm12, boolean unsigned ) {
+        super(src,imm12);
+        _unsigned = unsigned;
+    }
+    @Override public String op() { return "setlt" + (_unsigned ? "u":""); }
+    @Override public String glabel() { return  "<"+ (_unsigned ? "u":""); }
+    @Override int opcode() { return 19; }
+    @Override int func3() { return 2; }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SetRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SetRISC.java
new file mode 100644
index 0000000..c65c9c3
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SetRISC.java
@@ -0,0 +1,17 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv;
+
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+// corresponds to slt (and NOT sltu, slti, sltiu)
+public class SetRISC extends MachConcreteNode implements MachNode {
+    SetRISC( Node cmp ) { super(cmp); }
+    @Override public String op() { return "slt"; }
+    @Override public RegMask regmap(int i) { return riscv.RMASK; }
+    @Override public RegMask outregmap() { return riscv.WMASK; }
+    @Override public void encoding( Encoding enc ) { riscv.r_type(enc,this,2,0); }
+    @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)));
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SllIRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SllIRISC.java
new file mode 100644
index 0000000..c0f419f
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SllIRISC.java
@@ -0,0 +1,12 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv;
+
+import com.compilerprogramming.ezlang.compiler.Utils;
+import com.compilerprogramming.ezlang.compiler.nodes.Node;
+
+public class SllIRISC extends ImmRISC {
+    SllIRISC( Node and, int imm) { super(and,imm); }
+    @Override int opcode() {  return riscv.I_TYPE; }
+    @Override int func3() {  return 1; }
+    @Override public String glabel() { return "<<"; }
+    @Override public String op() { return "slli"; }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SllRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SllRISC.java
new file mode 100644
index 0000000..01e6e5a
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SllRISC.java
@@ -0,0 +1,17 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv;
+
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+// Shift left logical
+public class SllRISC extends MachConcreteNode implements MachNode {
+    SllRISC(Node sll) { super(sll); }
+    @Override public String op() { return "shl"; }
+    @Override public RegMask regmap(int i) { return riscv.RMASK; }
+    @Override public RegMask outregmap() { return riscv.WMASK; }
+    @Override public void encoding( Encoding enc ) { riscv.r_type(enc,this,1,0);  }
+    @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)));
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SplitRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SplitRISC.java
new file mode 100644
index 0000000..bface26
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SplitRISC.java
@@ -0,0 +1,55 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv;
+
+import com.compilerprogramming.ezlang.compiler.Utils;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.Node;
+import com.compilerprogramming.ezlang.compiler.nodes.SplitNode;
+
+public class SplitRISC extends SplitNode {
+    SplitRISC( String kind, byte round ) { super(kind,round, new Node[2]); }
+    @Override public RegMask regmap(int i) { return riscv.SPLIT_MASK; }
+    @Override public RegMask outregmap() { return riscv.SPLIT_MASK; }
+    @Override public void encoding( Encoding enc ) {
+        short dst = enc.reg(this );
+        short src = enc.reg(in(1));
+
+        // Flag for being either XMM or stack
+        boolean dstX = dst >= riscv.F_OFFSET;
+        boolean srcX = src >= riscv.F_OFFSET;
+
+        if( dst >= riscv.MAX_REG ) {
+            // Store to SP
+            if( src >= riscv.MAX_REG ) {
+                throw Utils.TODO(); // Very rare stack-stack move
+            }
+
+            int off = enc._fun.computeStackSlot(dst - riscv.MAX_REG)*8;
+            // store 64 bit values
+            enc.add4(riscv.s_type(39, 3, riscv.SP, dst, off));
+        }
+        if( src >= riscv.MAX_REG ) {
+            // Load from SP
+            int off = enc._fun.computeStackSlot(src - riscv.MAX_REG)*8;
+            enc.add4(riscv.i_type(7, dst, 3,riscv.SP, off));
+        }
+        // pick opcode based on regs
+        if( !dstX && !srcX ) {
+            // GPR->GPR
+            enc.add4(riscv.r_type(riscv.OP,dst,0,src,riscv.ZERO,0));
+        } else if( dstX && srcX ) {
+            // FPR->FPR
+            // Can do: FPR->GPR->FPR
+            enc.add4(riscv.r_type(0b1010011, dst, 0, src, 0, 0b1110001));
+            enc.add4(riscv.r_type(0b1010011, dst, 0, src, 0, 0b0100000));
+        } else if( dstX && !srcX ) {
+            //GPR->FPR
+            // fmv.d.x
+            enc.add4(riscv.r_type(0b1010011, dst, 0, src, 0, 0b0100000));
+        } else if( !dstX && srcX ) {
+            //FPR->GPR
+            //fmv.x.d
+            enc.add4(riscv.r_type(0b1010011, dst, 0, src, 0, 0b1110001));
+        }
+
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SraIRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SraIRISC.java
new file mode 100644
index 0000000..95b66c7
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SraIRISC.java
@@ -0,0 +1,12 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv;
+
+import com.compilerprogramming.ezlang.compiler.Utils;
+import com.compilerprogramming.ezlang.compiler.nodes.Node;
+
+public class SraIRISC extends ImmRISC {
+    SraIRISC( Node and, int imm) { super(and,imm); }
+    @Override int opcode() {  return riscv.I_TYPE; }
+    @Override int func3() {  return 5;}
+    @Override public String glabel() { return ">>"; }
+    @Override public String op() { return "srai"; }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SraRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SraRISC.java
new file mode 100644
index 0000000..9d919cb
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SraRISC.java
@@ -0,0 +1,17 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv;
+
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+// Right Shift Arithmetic
+public class SraRISC extends MachConcreteNode implements MachNode {
+    SraRISC(Node sra) { super(sra); }
+    @Override public String op() { return "sar"; }
+    @Override public RegMask regmap(int i) { return riscv.RMASK; }
+    @Override public RegMask outregmap() { return riscv.WMASK; }
+    @Override public void encoding( Encoding enc ) { riscv.r_type(enc,this,5,0x20);  }
+    @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)));
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SrlIRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SrlIRISC.java
new file mode 100644
index 0000000..a19c72b
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SrlIRISC.java
@@ -0,0 +1,12 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv;
+
+import com.compilerprogramming.ezlang.compiler.Utils;
+import com.compilerprogramming.ezlang.compiler.nodes.Node;
+
+public class SrlIRISC extends ImmRISC {
+    SrlIRISC( Node and, int imm, boolean pop) { super(and,imm,pop); }
+    @Override int opcode() {  return riscv.I_TYPE; }
+    @Override int func3() {  return 5; }
+    @Override public String glabel() { return ">>>"; }
+    @Override public String op() { return "srli"; }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SrlRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SrlRISC.java
new file mode 100644
index 0000000..37b7059
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SrlRISC.java
@@ -0,0 +1,17 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv;
+
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+// Right Shift Logical
+public class SrlRISC extends MachConcreteNode implements MachNode {
+    SrlRISC(Node srli) { super(srli); }
+    @Override public String op() { return "shr"; }
+    @Override public RegMask regmap(int i) { return riscv.RMASK; }
+    @Override public RegMask outregmap() { return riscv.WMASK; }
+    @Override public void encoding( Encoding enc ) { riscv.r_type(enc,this,7,0);  }
+    @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)));
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/StoreRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/StoreRISC.java
new file mode 100644
index 0000000..7f16a1b
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/StoreRISC.java
@@ -0,0 +1,46 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv;
+
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.Utils;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.codegen.RegMask;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+// Store memory addressing on ARM
+// Support imm, reg(direct), or reg+off(indirect) addressing
+// Base = base - base pointer, offset is added to base
+// idx  = null
+// off  = off - offset added to base)
+// imm  = imm - immediate value to store
+// val  = Node of immediate value to store(null if its a constant immediate)
+
+//e.g s.cs[0] =  67; // C
+// base = s.cs, off = 4, imm = 67, val = null
+
+// sw rs2,offset(rs1)
+public class StoreRISC extends MemOpRISC {
+    StoreRISC( StoreNode st, Node base, int off, Node val ) { super(st, base, off, val); }
+    @Override public String op() { return "st"+_sz; }
+    @Override public RegMask regmap(int i) {
+        // 0 - ctrl
+        if( i==1 ) return null; // mem
+        if( i==2 ) return riscv.RMASK; // ptr
+        // 2 - index
+        if( i==4 ) return riscv.MEM_MASK; // Wide mask to store GPR and FPR
+        throw Utils.TODO();
+    }
+    @Override public RegMask outregmap() { return null; }
+
+    @Override public void encoding( Encoding enc ) {
+        short val = xreg(enc);
+        short ptr = enc.reg(ptr());
+        int body = riscv.s_type(opcode(enc), func3()&3, ptr, val, _off);
+        enc.add4(body);
+    }
+
+    @Override public void asm(CodeGen code, SB sb) {
+        asm_address(code,sb).p(",");
+        if( val()==null ) sb.p("#").p("0");
+        else sb.p(code.reg(val()));
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SubFRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SubFRISC.java
new file mode 100644
index 0000000..dd20d7a
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SubFRISC.java
@@ -0,0 +1,17 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv;
+
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+// fsub.d
+public class SubFRISC extends MachConcreteNode implements MachNode {
+    SubFRISC(Node subf) {super(subf);}
+    @Override public String op() { return "subf"; }
+    @Override public RegMask regmap(int i) { assert i==1 || i==2; return riscv.FMASK; }
+    @Override public RegMask outregmap() { return riscv.FMASK; }
+    @Override public void encoding( Encoding enc ) { riscv.rf_type(enc,this,riscv.RM.RNE,5); }
+    @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)));
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SubRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SubRISC.java
new file mode 100644
index 0000000..652210f
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SubRISC.java
@@ -0,0 +1,16 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+public class SubRISC extends MachConcreteNode implements MachNode {
+    SubRISC( Node sub ) { super(sub); }
+    @Override public String op() { return "sub"; }
+    @Override public RegMask regmap(int i) { assert i==1 || i==2; return riscv.RMASK; }
+    @Override public RegMask outregmap() { return riscv.WMASK; }
+    @Override public void encoding( Encoding enc ) { riscv.r_type(enc,this,0,0x20);  }
+    @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)));
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/TFPRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/TFPRISC.java
new file mode 100644
index 0000000..e0cf2df
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/TFPRISC.java
@@ -0,0 +1,54 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.ConstantNode;
+import com.compilerprogramming.ezlang.compiler.nodes.MachNode;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFunPtr;
+
+public class TFPRISC extends ConstantNode implements MachNode, RIPRelSize {
+    TFPRISC(ConstantNode con) { super(con); }
+    @Override public String op() { return "ldx"; }
+    @Override public RegMask regmap(int i) { return null; }
+    @Override public RegMask outregmap() { return riscv.WMASK; }
+    @Override public boolean isClone() { return true; }
+    @Override public TFPRISC copy() { return new TFPRISC(this); }
+    @Override public void encoding( Encoding enc ) {
+        enc.relo(this);
+        // TODO: 1 op encoding, plus a TODO if it does not fit
+        short dst = enc.reg(this);
+        SONTypeFunPtr tfp = (SONTypeFunPtr)_con;
+        // auipc  t0,0
+        int auipc = riscv.u_type(0b0010111, dst, 0);
+        // addi   t1,t0 + #0
+        int addi = riscv.i_type(0b0010011, dst, 0, dst, 0);
+        enc.add4(auipc);
+        enc.add4(addi);
+    }
+
+    @Override public byte encSize(int delta) {
+        if( -(1L<<11) <= delta && delta < (1L<<11) ) return 4;
+        throw Utils.TODO();
+    }
+
+    // Delta is from opcode start
+    @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) {
+        short rpc = enc.reg(this);
+        if( opLen==4 ) {
+            // AUIPC (upper 20 bits)
+            // opstart of add
+            int next = opStart + opLen;
+            enc.patch4(opStart,riscv.u_type(0b0010111, rpc, delta));
+            // addi(low 12 bits)
+            enc.patch4(next,riscv.i_type(0b0010011, rpc, 0, rpc, delta & 0xFFF));
+            // addi
+        } else {
+            throw Utils.TODO();
+        }
+    }
+
+    @Override public void asm(CodeGen code, SB sb) {
+        _con.print(sb.p(code.reg(this)).p(" #"));
+    }
+
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/UJmpRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/UJmpRISC.java
new file mode 100644
index 0000000..ca5087c
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/UJmpRISC.java
@@ -0,0 +1,49 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
+import java.util.BitSet;
+
+// unconditional jump
+public class UJmpRISC extends CFGNode implements MachNode, RIPRelSize {
+    //UJmpRISC() { }
+    @Override public String op() { return "jmp"; }
+    @Override public String label() { return op(); }
+    @Override public StringBuilder _print1( StringBuilder sb, BitSet visited ) {
+        return sb.append("jmp ");
+    }
+    @Override public RegMask regmap(int i) {return null; }
+    @Override public RegMask outregmap() { return null; }
+    @Override public SONType compute() { throw Utils.TODO(); }
+    @Override public Node idealize() { throw Utils.TODO(); }
+    @Override public void encoding( Encoding enc ) {
+        // Short form +/-4K:  beq r0,r0,imm12
+        // Long form:  auipc rX,imm20/32; jal r0,[rX+imm12/32]
+        enc.jump(this,uctrl());
+        enc.add4(riscv.j_type(riscv.J_JAL, 0, 0));
+    }
+
+    // Delta is from opcode start
+    @Override public byte encSize(int delta) {
+        if( -(1L<<20) <= delta && delta < (1L<<20) ) return 4;
+        // 2 word encoding needs a tmp register, must teach RA
+        throw Utils.TODO();
+    }
+
+    // Delta is from opcode start
+    @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) {
+        if( opLen==4 ) {
+            enc.patch4(opStart,riscv.j_type(riscv.J_JAL, 0, delta));
+        } else {
+            throw Utils.TODO();
+        }
+    }
+
+    @Override public void asm(CodeGen code, SB sb) {
+        CFGNode target = uctrl();
+        assert target.nOuts() > 1; // Should optimize jmp to empty targets
+        sb.p(label(target));
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/XorIRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/XorIRISC.java
new file mode 100644
index 0000000..0f2338c
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/XorIRISC.java
@@ -0,0 +1,12 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv;
+
+import com.compilerprogramming.ezlang.compiler.Utils;
+import com.compilerprogramming.ezlang.compiler.nodes.Node;
+
+public class XorIRISC extends ImmRISC {
+    XorIRISC( Node and, int imm) { super(and,imm); }
+    @Override int opcode() {  return riscv.I_TYPE; }
+    @Override int func3() {  return 4; }
+    @Override public String glabel() { return "^"; }
+    @Override public String op() { return "xori"; }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/XorRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/XorRISC.java
new file mode 100644
index 0000000..2542201
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/XorRISC.java
@@ -0,0 +1,16 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv;
+
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+public class XorRISC extends MachConcreteNode implements MachNode {
+    XorRISC(Node or) { super(or); }
+    @Override public String op() { return "xor"; }
+    @Override public RegMask regmap(int i) { return riscv.RMASK; }
+    @Override public RegMask outregmap() { return riscv.WMASK; }
+    @Override public void encoding( Encoding enc ) { riscv.r_type(enc,this,4,0);  }
+    @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)));
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/riscv.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/riscv.java
new file mode 100644
index 0000000..7cead1f
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/riscv.java
@@ -0,0 +1,557 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv;
+
+import com.compilerprogramming.ezlang.compiler.Utils;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+import com.compilerprogramming.ezlang.compiler.sontypes.*;
+
+public class riscv extends Machine {
+
+    public riscv(CodeGen code) {
+        if( !"SystemV".equals(code._callingConv) )
+            throw new IllegalArgumentException("Unknown calling convention "+code._callingConv);
+    }
+
+    @Override public String name() {return "riscv";}
+
+    // Using ABI names instead of register names
+    public static int ZERO =  0,  RPC=  1,  SP =  2,  S12=  3,  S13=  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
+    static int F0   = 32,  F1  = 33,  F2  = 34,  F3  = 35,  F4  = 36,  F5  = 37,  F6  = 38,  F7   = 39;
+    static int FS0  = 40,  FS1 = 41,  FA0 = 42,  FA1 = 43,  FA2 = 44,  FA3 = 45,  FA4 = 46,  FA5  = 47;
+    static int FA6  = 48,  FA7 = 49,  FS2 = 50,  FS3 = 51,  FS4 = 52,  FS5 = 53,  FS6 = 54,  FS7  = 55;
+    static int FS8  = 56,  FS9 = 57,  FS10 = 58, FS11 = 59, FT8 = 60,  FT9 = 61,  FT10 = 62, FT11 = 63;
+
+    static final int MAX_REG = 64;
+
+    static final int F_OFFSET = 32;
+
+    static final String[] REGS = new String[] {
+            "zero","rpc"  , "sp"  , "s12" , "s13" , "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"
+    };
+
+    // General purpose register mask: pointers and ints, not floats
+    static final long RD_BITS = 0b11111111111111111111111111111111L; // All the GPRs
+    static final RegMask RMASK = new RegMask(RD_BITS);
+
+    static final long WR_BITS = 0b11111111111111111111111111111010L; // All the GPRs, minus ZERO and SP
+    static final RegMask WMASK = new RegMask(WR_BITS);
+    // Float mask from(f0-ft10).  TODO: ft10,ft11 needs a larger RegMask
+    static final long FP_BITS = 0b11111111111111111111111111111111L<<F0;
+    static final RegMask FMASK = new RegMask(FP_BITS);
+
+    // Load/store mask; both GPR and FPR
+    static final RegMask MEM_MASK = new RegMask(WR_BITS | FP_BITS);
+
+    static final RegMask SPLIT_MASK = new RegMask( WR_BITS | FP_BITS, -1L );
+
+
+    // Arguments masks
+    public static RegMask A0_MASK = new RegMask(A0);
+    public static RegMask A1_MASK = new RegMask(A1);
+    public static RegMask A2_MASK = new RegMask(A2);
+    public static RegMask A3_MASK = new RegMask(A3);
+    public static RegMask A4_MASK = new RegMask(A4);
+    public static RegMask A5_MASK = new RegMask(A5);
+    public static RegMask A6_MASK = new RegMask(A6);
+    public static RegMask A7_MASK = new RegMask(A7);
+
+    public static final RegMask RPC_MASK = new RegMask(RPC);
+
+    // Float arguments masks
+    public static RegMask FA0_MASK = new RegMask(FA0);
+    public static RegMask FA1_MASK = new RegMask(FA1);
+    public static RegMask FA2_MASK = new RegMask(FA2);
+    public static RegMask FA3_MASK = new RegMask(FA3);
+    public static RegMask FA4_MASK = new RegMask(FA4);
+    public static RegMask FA5_MASK = new RegMask(FA5);
+    public static RegMask FA6_MASK = new RegMask(FA6);
+    public static RegMask FA7_MASK = new RegMask(FA7);
+
+    // Int arguments calling conv
+    static RegMask[] CALLINMASK = new RegMask[] {
+        A0_MASK,
+        A1_MASK,
+        A2_MASK,
+        A3_MASK,
+        A4_MASK,
+        A5_MASK,
+        A6_MASK,
+        A7_MASK
+    };
+
+    static RegMask[] XMMS = new RegMask[] {
+            FA0_MASK,
+            FA1_MASK,
+            FA2_MASK,
+            FA3_MASK,
+            FA4_MASK,
+            FA5_MASK,
+            FA6_MASK,
+            FA7_MASK
+    };
+    //
+    // major opcode: OP
+    public static int OP    = 0b01_100_11;
+    public static int OP_FP = 0b10_100_11;
+    public static int OP_CUSTOM0 = 0b00_010_11;
+    public static int OP_BRANCH  = 0b11_000_11;
+
+    //I_type opcode: 0010 0011
+    public static int I_TYPE = 0x13;
+
+    // 0110 0111
+    public static int J_JAL = 0b1101111;
+
+
+    // Since riscv instructions are fixed we can just or them togehter
+    public static int r_type(int opcode, int rd, int func3, int rs1, int rs2, int func7) {
+        return (func7 << 25) | (rs2 << 20) | (rs1 << 15) | (func3 << 12) | (rd << 7) | opcode;
+    }
+    public static void r_type(Encoding enc, Node n, int func3, int func7) {
+        short dst  = enc.reg(n);
+        short src1 = enc.reg(n.in(1));
+        short src2 = enc.reg(n.in(2));
+        int body = r_type(OP,dst,func3,src1,src2,func7);
+        enc.add4(body);
+    }
+    public static void rf_type(Encoding enc, Node n, RM func3, int func7) {
+        short dst  = (short)(enc.reg(n      )-F_OFFSET);
+        short src1 = (short)(enc.reg(n.in(1))-F_OFFSET);
+        short src2 = (short)(enc.reg(n.in(2))-F_OFFSET);
+        int body = r_type(OP_FP,dst,func3.ordinal(),src1,src2,func7);
+        enc.add4(body);
+    }
+
+
+    public static int u_type(int opcode, int rd, int imm20) {
+        return (imm20 << 12) | (rd << 7) | opcode;
+    }
+
+    public static int j_type(int opcode, int rd, int delta) {
+        assert -(1L<<20) <= delta && delta < (1L<<20);
+        // Messy branch offset encoding
+        // 31 30-21 20 19-12 11-7  6-0
+        // 20 10- 1 11 19-12 rpc   JAL
+        assert (delta&1)==0;    // Low bit is always zero, not encoded
+        int imm10_01 = (delta>> 1) & 0x3FF;
+        int imm11    = (delta>>11) &     1;
+        int imm12_19 = (delta>>12) &  0xFF;
+        int imm20    = (delta>>19) &     1;
+        int bits = imm20<<20 | imm12_19 << 12 | imm11 << 11 | imm10_01;
+        return bits << 12 | rd << 7 | opcode;
+    }
+
+
+    public static int i_type(int opcode, int rd, int func3, int rs1, int imm12) {
+        assert opcode >= 0 && rd >=0 && func3 >=0 && rs1 >=0 && imm12 >= 0; // Zero-extend by caller
+        return  (imm12 << 20) | (rs1 << 15) | (func3 << 12) | (rd << 7) | opcode;
+    }
+
+
+    // S-type instructions(store)
+    public static int s_type(int opcode, int offset1, int func3, int rs1, int rs2, int offset2) {
+        return (offset2 << 25) | (rs2 << 20) | (rs1 << 15) | (func3 << 12) | (offset1 << 7) | opcode;
+    }
+    public static int s_type(int opcode, int func3, int rs1, int rs2, int imm12) {
+        assert imm12 >= 0;      // Masked to high zero bits by caller
+        int imm_lo = imm12 & 0x1F;
+        int imm_hi = imm12 >> 5;
+        return (imm_hi << 25) | (rs2 << 20) | (rs1 << 15) | (func3 << 12) | (imm_lo << 7) | opcode;
+    }
+
+    // BRANCH
+    public static int b_type(int opcode, int func3, short rs1, short rs2, int delta) {
+        assert -4*1024 <= delta && delta < 4*1024;
+        assert (delta&1)==0;    // Low bit is always zero, not encoded
+        // Messy branch offset encoding
+        // 31 30 29 28 27 26 25 24-20 19-15 14-12 11 10  9  8  7  6-0
+        // 12 10  9  8  7  6  5 SRC2  SRC1  FUNC3  4  3  2  1 11  OP
+        int imm4_1 = (delta>> 1) & 0xF;
+        int imm10_5= (delta>> 5) &0x3F;
+        int imm11  = (delta>>11) &   1;
+        int imm12  = (delta>>12) &   1;
+        int imm5 = imm4_1<<1 | imm11;
+        int imm7 = imm12<<6 | imm10_5;
+        return (imm7 << 25 ) | (rs2 << 20) | (rs1 << 15) | (func3 << 12) | (imm5 << 7) | opcode;
+    }
+
+    public enum RM {
+        RNE,       // Round to Nearest, ties to Even
+        RTZ,       // Round towards Zero
+        RDN,       // Round Down
+        RUP,       // Round Up
+        DIRECT,    // Round to Nearest, ties to Max Magnitude
+        RESERVED1, // Reserved for futue use
+        RESERVED2, // Reserved for future use
+        DYN,       // In instruction’s rm field, selects dynamic rounding mode; In Rounding Mode register, reserved
+    }
+
+    // Since opcode is the same just return back func3
+    static public int jumpop(String op) {
+        return switch(op) {
+        case "=="  -> 0x0;
+        case "!="  -> 0x1;
+        case "<"   -> 0x4;
+        case "<="  -> 0x5;
+        case "u<"  -> 0x6;
+        case "u<=" -> 0x7;
+        default  ->  throw Utils.TODO();
+        };
+    }
+
+    // Since opcode is the same just return back func3
+    static public int fsetop(String op) {
+        return switch(op) {
+        case "<"  -> 1;
+        case "<=" -> 0;
+        case "==" -> 2;
+        default   -> throw Utils.TODO();
+        };
+    }
+
+    static RegMask callInMask( SONTypeFunPtr tfp, int idx ) {
+        if( idx==0 ) return RPC_MASK;
+        if( idx==1 ) return null;
+        // Count floats in signature up to index
+        int fcnt=0;
+        for( int i=2; i<idx; i++ )
+            if( tfp.arg(i-2) instanceof SONTypeFloat )
+                fcnt++;
+        // Floats up to XMMS in XMM registers
+        if( tfp.arg(idx-2) instanceof SONTypeFloat ) {
+            if( fcnt < XMMS.length )
+                return XMMS[fcnt];
+        } else {
+            RegMask[] cargs = CALLINMASK;
+            if( idx-2-fcnt < cargs.length )
+                return cargs[idx-2-fcnt];
+        }
+        throw Utils.TODO(); // Pass on stack slot
+    }
+
+    // Return the max stack slot used by this signature, or 0
+    static short maxSlot( SONTypeFunPtr tfp ) {
+        // Count floats in signature up to index
+        int fcnt=0;
+        for( int i=0; i<tfp.nargs(); i++ )
+            if( tfp.arg(i) instanceof SONTypeFloat )
+                fcnt++;
+        if( fcnt >= XMMS.length )
+            throw Utils.TODO();
+        RegMask[] cargs = CALLINMASK;
+        if( tfp.nargs()-fcnt >= cargs.length )
+            throw Utils.TODO();
+        return 0;               // No stack args
+    }
+
+    // callee saved(riscv)
+    static final long CALLEE_SAVE =
+        (1L<< S0) | (1L<< S1) | (1L<< S2 ) | (1L<< S3 ) |
+        (1L<< S4) | (1L<< S5) | (1L<< S6 ) | (1L<< S7 ) |
+        (1L<< S8) | (1L<< S9) | (1L<< S10) | (1L<< S11) |
+        (1L<<S12) | (1L<<S13) |
+        (1L<<FS0) | (1L<<FS1) | (1L<<FS2 ) | (1L<<FS3 ) |
+        (1L<<FS4) | (1L<<FS5) | (1L<<FS6 ) | (1L<<FS7 ) |
+        (1L<<FS8) | (1L<<FS9) | (1L<<FS10) | (1L<<FS11);
+
+    static final RegMask CALLER_SAVE_MASK;
+    static {
+        long caller = ~CALLEE_SAVE;
+        caller &= ~(1L<<SP);
+        CALLER_SAVE_MASK = new RegMask(caller,0);
+    }
+    static RegMask riscCallerSave() { return CALLER_SAVE_MASK; }
+    @Override public RegMask callerSave() { return riscCallerSave(); }
+
+    static final RegMask CALLEE_SAVE_MASK = new RegMask(CALLEE_SAVE);
+    static RegMask riscCalleeSave() { return CALLEE_SAVE_MASK; }
+    @Override public RegMask calleeSave() { return riscCalleeSave(); }
+
+    // Return single int/ptr register.  Used by CallEnd output and Return input.
+    static final RegMask[] RET_MASKS;
+    static {
+        int nSaves = CALLEE_SAVE_MASK.size();
+        RET_MASKS = new RegMask[4 + nSaves];
+        RET_MASKS[0] = null;
+        RET_MASKS[1] = null;     // Memory
+        RET_MASKS[2] = null;     // Varies, either XMM0 or RAX
+        RET_MASKS[3] = RMASK;    // Expected R1 but could be any register really
+        short reg = CALLEE_SAVE_MASK.firstReg();
+        for( int i=0; i<nSaves; i++ ) {
+            RET_MASKS[i+4] = new RegMask(reg);
+            reg = CALLEE_SAVE_MASK.nextReg(reg);
+        }
+    }
+    static RegMask retMask( SONTypeFunPtr tfp, int i ) {
+        return i==2
+            ? (tfp.ret() instanceof SONTypeFloat ? FA0_MASK : A0_MASK)
+            : RET_MASKS[i];
+    }
+
+    @Override public String reg( int reg ) {
+        return reg < REGS.length ? REGS[reg] : "[rsp+"+(reg-REGS.length)*8+"]";
+    }
+
+    // Stack slots, in units of 8 bytes.
+    @Override public int stackSlot( int reg ) {
+        return reg < REGS.length ? -1 : reg-REGS.length;
+    }
+
+
+
+    // Return a MachNode unconditional branch
+    @Override public CFGNode jump() {
+        return new UJmpRISC();
+    }
+
+    // Create a split op; any register to any register, including stack slots
+    @Override  public SplitNode split(String kind, byte round, LRG lrg) { return new SplitRISC(kind,round);  }
+
+    // Break an infinite loop
+    @Override public IfNode never( CFGNode ctrl ) {
+        throw Utils.TODO();
+    }
+
+    // True if signed 12-bit immediate
+    public static boolean imm12(SONTypeInteger ti) {
+        // 52 = 64-12
+        return ti.isConstant() && ((ti.value()<<52)>>52) == ti.value();
+    }
+    // True if HIGH 20-bit signed immediate, with all zeros low.
+    public static boolean imm20Exact(SONTypeInteger ti) {
+        // shift left 32 to clear out the upper 32 bits.
+        // shift right SIGNED to sign-extend upper 32 bits; then shift 12 more to clear out lower 12 bits.
+        // shift left 12 to re-center the bits.
+        return ti.isConstant() && (((ti.value()<<32)>>>44)<<12) == ti.value();
+    }
+
+    @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 CallNode call -> call(call);
+        case CastNode cast  -> new CastRISC(cast);
+        case CallEndNode cend -> new CallEndRISC(cend);
+        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 -> new MulRISC(mul);
+        case NewNode nnn -> nnn(nnn);
+        case NotNode not -> new NotRISC(not);
+        case OrNode or -> or(or);
+        case ParmNode parm -> new ParmRISC(parm);
+        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 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) {
+        if( add.in(2) instanceof ConstantNode off && off._con instanceof SONTypeInteger ti && imm12(ti) )
+            return new AddIRISC(add, (int)ti.value(),true);
+        return new AddRISC(add);
+    }
+
+    private Node and(AndNode and) {
+        if( and.in(2) instanceof ConstantNode con && con._con instanceof SONTypeInteger ti ) {
+            if( imm12(ti) )
+                return new AndIRISC(and, (int)ti.value());
+            // Could be any size low bit mask
+            if( ti.value() == 0xFFFFFFFFL )
+                return new SrlIRISC(new SllIRISC(and,32),32,false);
+        }
+        return new AndRISC(and);
+    }
+
+    private Node call(CallNode call) {
+        return call.fptr() instanceof ConstantNode con && con._con instanceof SONTypeFunPtr tfp
+            ? new CallRISC(call, tfp)
+            : new CallRRISC(call);
+    }
+    private Node nnn(NewNode nnn) {
+        // TODO: pass in the TFP for alloc
+        return new NewRISC(nnn);
+    }
+
+    private Node cmp(BoolNode bool) {
+        // Float variant directly implemented in hardware
+        if( bool.isFloat() )
+            return new SetFRISC(bool);
+
+        // Only < and <u are implemented in hardware.
+        // x <  y - as-is
+        // x <= y - flip and invert; !(y < x); `slt tmp=y,x; xori dst=tmp,#1`
+        // x == y - sub and vs0 == `sub tmp=x-y; sltu dst=tmp,#1`
+
+        // x >  y - swap; y < x
+        // x >= y - swap and invert; !(x < y); `slt tmp=y,x;` then NOT.
+        // x != y - sub and vs0 == `sub tmp=x-y; sltu dst=tmp,#1` then NOT.
+
+        // The ">", ">=" and "!=" in Simple include a NotNode, which can be
+        // implemented with a XOR.  If one of the above is followed by a NOT
+        // we can remove the double XOR in the encodings.
+
+        return switch( bool.op() ) {
+        case "<" -> bool.in(2) instanceof ConstantNode con && con._con instanceof SONTypeInteger ti && imm12(ti)
+            ? new SetIRISC(bool, (int)ti.value(),false)
+            : new SetRISC(bool);
+        // x <= y - flip and invert; !(y < x); `slt tmp=y,x; xori dst=tmp,#1`
+        case "<=" -> new XorIRISC(new SetRISC(bool.swap12()),1);
+        // x == y - sub and vs0 == `sub tmp=x-y; sltu dst=tmp,#1`
+        case "==" -> new SetIRISC(new SubRISC(bool),1,true);
+        default -> throw Utils.TODO();
+        };
+    }
+
+    private Node con( ConstantNode con ) {
+        if( !con._con.isConstant() ) return new ConstantNode( con ); // Default unknown caller inputs
+        return switch( con._con ) {
+        case SONTypeInteger ti -> {
+            if( imm12(ti) ) yield new IntRISC(con);
+            long x = ti.value();
+            if( imm20Exact(ti) ) yield new LUI((int)x);
+            if( (x<<32)>>32 == x ) { // Signed lower 32-bit immediate
+                // Here, the low 12 bits get sign-extended, which means if
+                // bit11 is set, the value is negative and lowers the LUI
+                // value.  Add a bit 12 to compensate
+                if( ((x>>11)&1)==1 ) x += 0x1000;
+                yield new AddIRISC(new LUI((int)(x & ~0xFFF)), (int)(x & 0xFFF),false);
+            }
+            // Need more complex sequence for larger constants... or a load
+            // from a constant pool, which does not need an extra register
+            throw Utils.TODO();
+        }
+        case SONTypeFloat   tf  -> new FltRISC(con);
+        case SONTypeFunPtr  tfp -> new TFPRISC(con);
+        case SONTypeMemPtr  tmp -> throw Utils.TODO();
+        case SONTypeNil     tn  -> throw Utils.TODO();
+        // TOP, BOTTOM, XCtrl, Ctrl, etc.  Never any executable code.
+        case SONType t -> t==SONType.NIL ? new IntRISC(con) : new ConstantNode(con);
+        };
+    }
+
+    private Node jmp( IfNode iff ) {
+        if( iff.in(1) instanceof BoolNode bool && !bool.isFloat() ) {
+            // if less than or equal switch inputs
+            String bop = bool.op();
+            if( bop.equals(">=") || bop.equals(">") )
+                return new BranchRISC(iff, IfNode.invert(bop), bool.in(2), bool.in(1));
+            return new BranchRISC(iff, bop, bool.in(1), bool.in(2));
+        }
+        // Vs zero
+        return new BranchRISC(iff, "==", iff.in(1), null);
+    }
+
+    private Node or(OrNode or) {
+        if( or.in(2) instanceof ConstantNode con && con._con instanceof SONTypeInteger ti && imm12(ti))
+            return new OrIRISC(or, (int)ti.value());
+        return new OrRISC(or);
+    }
+
+    private Node xor(XorNode xor) {
+        if( xor.in(2) instanceof ConstantNode con && con._con instanceof SONTypeInteger ti && imm12(ti))
+            return new XorIRISC(xor, (int)ti.value());
+        return new XorRISC(xor);
+    }
+
+    private Node sra(SarNode sar) {
+        if( sar.in(2) instanceof ConstantNode con && con._con instanceof SONTypeInteger ti && imm12(ti))
+            return new SraIRISC(sar, (int)ti.value());
+        return new SraRISC(sar);
+    }
+
+    private Node srl(ShrNode shr) {
+        if( shr.in(2) instanceof ConstantNode con && con._con instanceof SONTypeInteger ti && imm12(ti))
+            return new SrlIRISC(shr, (int)ti.value(),true);
+        return new SrlRISC(shr);
+    }
+
+    private Node sll(ShlNode sll) {
+        if( sll.in(2) instanceof ConstantNode con && con._con instanceof SONTypeInteger ti && imm12(ti))
+            return new SllIRISC(sll, (int)ti.value());
+        return new SllRISC(sll);
+    }
+
+    private Node sub(SubNode sub) {
+        return sub.in(2) instanceof ConstantNode con && con._con instanceof SONTypeInteger ti && imm12(ti)
+            ? new AddIRISC(sub, (int)(-ti.value()),true)
+            : new SubRISC(sub);
+    }
+
+    private Node i2f8(ToFloatNode tfn) {
+        assert tfn.in(1)._type instanceof SONTypeInteger ti;
+        return new I2F8RISC(tfn);
+    }
+
+    private Node prj(ProjNode prj) {
+        return new ProjRISC(prj);
+    }
+
+    private Node ld(LoadNode ld) {
+        return new LoadRISC(ld,address(ld),off);
+    }
+
+    private Node st(StoreNode st) {
+        Node xval = st.val() instanceof ConstantNode con && con._con == SONTypeInteger.ZERO ? null : st.val();
+        return new StoreRISC(st,address(st), off, xval);
+    }
+
+    // Gather 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;
+    private Node address( MemOpNode mop ) {
+        off = 0;  // Reset
+        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 SONTypeMemPtr; // Base ptr always, not some derived
+        if( mop.off() instanceof ConstantNode con && con._con instanceof SONTypeInteger ti && imm12(ti) ) {
+            off = (int)ti.value();
+        } else {
+            base = new AddRISC(base,mop.off());
+            base._type = mop.ptr()._type;
+        }
+        return base;
+    }
+
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/AddFMemX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/AddFMemX86.java
new file mode 100644
index 0000000..92c8f15
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/AddFMemX86.java
@@ -0,0 +1,44 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2;
+
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.Utils;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+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 String op() { return "addf"+_sz; }
+    @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();
+    }
+    @Override public RegMask outregmap() { return x86_64_v2.XMASK; }
+    @Override public int twoAddress() { return 4; }
+    @Override public void encoding( Encoding enc ) {
+        //  addsd xmm0, DWORD PTR [rdi+0xc]
+        short dst = enc.reg(this );
+        short ptr = enc.reg(ptr());
+        short idx = enc.reg(idx());
+        // F opcode
+        enc.add1(0xF2);
+        // rex prefix must come next (REX.W is not set)
+        x86_64_v2.rexF(dst, ptr, idx, false, enc);
+
+        // FP ADD
+        enc.add1(0x0F).add1(0x58);
+
+        x86_64_v2.indirectAdr(_scale, idx, ptr, _off, dst, enc);
+    }
+
+    // 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/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/AddFX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/AddFX86.java
new file mode 100644
index 0000000..6681c1c
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/AddFX86.java
@@ -0,0 +1,33 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+public class AddFX86 extends MachConcreteNode implements MachNode {
+    AddFX86( Node addf ) { super(addf); }
+    @Override public String op() { return "addf"; }
+    @Override public RegMask regmap(int i) { assert i==1 || i==2; return x86_64_v2.XMASK; }
+    @Override public RegMask outregmap() { return x86_64_v2.XMASK; }
+    @Override public int twoAddress() { return 1; }
+    @Override public boolean commutes() { return true; }
+
+    @Override public void encoding( Encoding enc ) {
+        // F2 0F 58 /r ADDSD xmm1, xmm2/m64
+        short dst = (short)(enc.reg(this ) - x86_64_v2.XMM_OFFSET);
+        short src = (short)(enc.reg(in(2)) - x86_64_v2.XMM_OFFSET);
+        // Fopcode
+        enc.add1(0xF2);
+        // rex prefix must come next (REX.W is not set)
+        x86_64_v2.rexF(dst, src, 0, false, enc);
+
+        enc.add1(0x0F).add1(0x58);
+
+        enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, src));
+    }
+
+    // General form: "addf  dst += src"
+    @Override public void asm(CodeGen code, SB sb) {
+        sb.p(code.reg(this)).p(" += ").p(code.reg(in(2)));
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/AddIX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/AddIX86.java
new file mode 100644
index 0000000..7659e35
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/AddIX86.java
@@ -0,0 +1,13 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2;
+
+import com.compilerprogramming.ezlang.compiler.nodes.Node;
+
+public class AddIX86 extends ImmX86 {
+    AddIX86( Node add, int imm ) { super(add,imm); }
+    @Override public String op() {
+        return _imm == 1  ? "inc" : (_imm == -1 ? "dec" : "addi");
+    }
+    @Override public String glabel() { return "+"; }
+    @Override int opcode() { return 0x81; }
+    @Override int mod() { return 0; }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/AddMemX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/AddMemX86.java
new file mode 100644
index 0000000..89dbcd9
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/AddMemX86.java
@@ -0,0 +1,35 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2;
+
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+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; }
+    @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
+    @Override public int twoAddress() { return 4; }
+    @Override public void encoding( Encoding enc ) {
+        // add something to register from memory
+        // add   eax,DWORD PTR [rdi+0xc]
+        // REX.W + 03 /r	ADD r64, r/m64
+        short dst = enc.reg(this );
+        short ptr = enc.reg(ptr());
+        short idx = enc.reg(idx());
+
+        enc.add1(x86_64_v2.rex(dst, ptr, idx));
+        // opcode
+        enc.add1(0x03);
+
+        x86_64_v2.indirectAdr(_scale, idx, ptr, _off, dst, enc);
+    }
+
+    // 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/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/AddX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/AddX86.java
new file mode 100644
index 0000000..fe1808b
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/AddX86.java
@@ -0,0 +1,11 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2;
+
+import com.compilerprogramming.ezlang.compiler.nodes.Node;
+
+public class AddX86 extends RegX86 {
+    AddX86( Node add ) { super(add); }
+    @Override public String op() { return "add"; }
+    @Override public String glabel() { return "+"; }
+    @Override int opcode() { return 0x03; }
+    @Override public boolean commutes() { return true; }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/AndIX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/AndIX86.java
new file mode 100644
index 0000000..446da90
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/AndIX86.java
@@ -0,0 +1,11 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2;
+
+import com.compilerprogramming.ezlang.compiler.nodes.Node;
+
+public class AndIX86 extends ImmX86 {
+    AndIX86( Node add, int imm ) { super(add,imm); }
+    @Override public String op() { return "andi"; }
+    @Override public String glabel() { return "&"; }
+    @Override int opcode() { return 0x81; }
+    @Override int mod() { return 4; }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/AndX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/AndX86.java
new file mode 100644
index 0000000..3c09cc6
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/AndX86.java
@@ -0,0 +1,11 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2;
+
+import com.compilerprogramming.ezlang.compiler.nodes.Node;
+
+public class AndX86 extends RegX86 {
+    AndX86( Node add ) { super(add); }
+    @Override public String op() { return "and"; }
+    @Override public String glabel() { return "&"; }
+    @Override int opcode() { return 0x23; }
+    @Override public boolean commutes() { return true; }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CallEndX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CallEndX86.java
new file mode 100644
index 0000000..4cf1f22
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CallEndX86.java
@@ -0,0 +1,23 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2;
+
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.CallEndNode;
+import com.compilerprogramming.ezlang.compiler.nodes.MachNode;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFunPtr;
+
+public class CallEndX86 extends CallEndNode implements MachNode {
+    final SONTypeFunPtr _tfp;
+    CallEndX86( CallEndNode cend ) {
+        super(cend);
+        _tfp = (SONTypeFunPtr)(cend.call().fptr()._type);
+    }
+    @Override public String op() { return "cend"; }
+    @Override public String label() { return op(); }
+    @Override public RegMask regmap(int i) { return null; }
+    @Override public RegMask outregmap() { return null; }
+    @Override public RegMask outregmap(int idx) { return idx == 2 ? x86_64_v2.retMask(_tfp,2) : null; }
+    @Override public RegMask killmap() { return x86_64_v2.x86CallerSave(); }
+    @Override public void encoding( Encoding enc ) { }
+    @Override public void asm(CodeGen code, SB sb) {  }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CallRX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CallRX86.java
new file mode 100644
index 0000000..82a9999
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CallRX86.java
@@ -0,0 +1,33 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2;
+
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.CallNode;
+import com.compilerprogramming.ezlang.compiler.nodes.MachNode;
+
+public class CallRX86 extends CallNode implements MachNode {
+    CallRX86( CallNode call ) { super(call); }
+    @Override public String op() { return "callr"; }
+    @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 null; }
+    @Override public void encoding( Encoding enc ) {
+        // FF /2	CALL r/m64
+        // calls the function in the register
+        short src = enc.reg(fptr());
+        enc.add1(0xFF);
+        enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.INDIRECT,2,src));
+    }
+
+    @Override public void asm(CodeGen code, SB sb) {
+        sb.p(code.reg(fptr())).p("  ");
+        for( int i=0; i<nargs(); i++ )
+            sb.p(code.reg(arg(i))).p("  ");
+        sb.unchar(2);
+    }
+
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CallX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CallX86.java
new file mode 100644
index 0000000..c18e77f
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CallX86.java
@@ -0,0 +1,46 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2;
+
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.CallNode;
+import com.compilerprogramming.ezlang.compiler.nodes.MachNode;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFunPtr;
+
+public class CallX86 extends CallNode implements MachNode, RIPRelSize {
+    final SONTypeFunPtr _tfp;
+    final String _name;
+    CallX86( CallNode call, SONTypeFunPtr tfp ) {
+        super(call);
+        _inputs.pop(); // Pop constant target
+        assert tfp.isConstant();
+        _tfp = tfp;
+        _name = CodeGen.CODE.link(tfp)._name;
+    }
+    @Override public String op() { return "call"; }
+    @Override public String label() { return op(); }
+    @Override public String name() { return _name; }
+    @Override public SONTypeFunPtr tfp() { return _tfp; }
+    @Override public RegMask regmap(int i) { return x86_64_v2.callInMask(_tfp,i);  }
+    @Override public RegMask outregmap() { return null; }
+    @Override public int nargs() { return nIns()-2; } // Minus control, memory, fptr
+
+    @Override public void encoding( Encoding enc ) {
+        enc.relo(this).add1(0xe8).add4(0);
+    }
+
+    // Delta is from opcode start, but X86 measures from the end of the 5-byte encoding
+    @Override public byte encSize(int delta) { return 5; }
+
+    // Delta is from opcode start
+    @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) {
+        enc.patch4(opStart+1, delta-5);
+    }
+
+
+    @Override public void asm(CodeGen code, SB sb) {
+        sb.p(_name).p("  ");
+        for( int i=0; i<nargs(); i++ )
+            sb.p(code.reg(in(i+2))).p("  ");
+        sb.unchar(2);
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CastX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CastX86.java
new file mode 100644
index 0000000..f494e79
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CastX86.java
@@ -0,0 +1,16 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2;
+
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.CastNode;
+import com.compilerprogramming.ezlang.compiler.nodes.MachNode;
+
+public class CastX86 extends CastNode implements MachNode {
+    CastX86( CastNode cast ) { super(cast); }
+    @Override public String op() { return label(); }
+    @Override public RegMask regmap(int i) { assert i==1; return RegMask.FULL; }
+    @Override public RegMask outregmap() { return RegMask.FULL; }
+    @Override public int twoAddress( ) { return 1; }
+    @Override public void encoding( Encoding enc ) { }
+    @Override public void asm(CodeGen code, SB sb) { _t.print(sb.p(code.reg(in(1))).p(" isa ")); }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CmpFX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CmpFX86.java
new file mode 100644
index 0000000..44df96f
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CmpFX86.java
@@ -0,0 +1,32 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+public class CmpFX86 extends MachConcreteNode implements MachNode {
+    CmpFX86( Node cmp ) { super(cmp); }
+    @Override public String op() { return "cmpf"; }
+    @Override public RegMask regmap(int i) { return x86_64_v2.XMASK; }
+    @Override public RegMask outregmap() { return x86_64_v2.FLAGS_MASK; }
+
+    @Override public void encoding( Encoding enc ) {
+        // UCOMISD
+        short src1 = (short)(enc.reg(in(1)) - x86_64_v2.XMM_OFFSET);
+        short src2 = (short)(enc.reg(in(2)) - x86_64_v2.XMM_OFFSET);
+        // float opcode
+        enc.add1(0x66);
+        // rex prefix must come next (REX.W is not set)
+        x86_64_v2.rexF(src1, src2, 0, false, enc);
+
+        enc.add1(0x0F).add1(0x2E);
+        enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, src1, src2));
+    }
+
+    // General form: "cmp src1,src2"
+    @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)));
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CmpIX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CmpIX86.java
new file mode 100644
index 0000000..fe08e20
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CmpIX86.java
@@ -0,0 +1,49 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2;
+
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+// Compare immediate.  Sets flags.
+public class CmpIX86 extends MachConcreteNode implements MachNode {
+    final int _imm;
+    CmpIX86( Node cmp, int imm ) {
+        super(cmp);
+        _inputs.pop();
+        _imm = imm;
+    }
+    CmpIX86( CmpIX86 cmp ) {
+        super((Node)null);
+        addDef(null);
+        addDef(cmp.in(1));
+        _imm = cmp._imm;
+    }
+    CmpIX86( Node lhs, int imm, double ignore ) {
+        super(lhs);
+        _inputs.del(1);
+        _imm = imm;
+    }
+
+    @Override public String op() { return _imm==0 ? "test" : "cmp"; }
+    @Override public RegMask regmap(int i) { return x86_64_v2.RMASK; }
+    @Override public RegMask outregmap() { return x86_64_v2.FLAGS_MASK; }
+    @Override public boolean isClone() { return true; }
+    @Override public Node copy() { return new CmpIX86(this); }
+
+    @Override public final void encoding( Encoding enc ) {
+        short dst = enc.reg(this); // Also src1
+        enc.add1(x86_64_v2.rex(0, dst, 0));
+        // opcode; 0x81 or 0x83; 0x69 or 0x6B
+        enc.add1( 0x81 + (x86_64_v2.imm8(_imm) ? 2 : 0) );
+        enc.add1( x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, 7, dst) );
+        // immediate(4 bytes) 32 bits or (1 byte)8 bits
+        if( x86_64_v2.imm8(_imm) ) enc.add1(_imm);
+        else                       enc.add4(_imm);
+    }
+    @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);
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CmpMemX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CmpMemX86.java
new file mode 100644
index 0000000..f5d1fb5
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CmpMemX86.java
@@ -0,0 +1,43 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2;
+
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+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;
+    }
+    @Override public String op() { return ((val()==null && _imm==0) ? "test" : "cmp") + _sz; }
+    @Override public RegMask outregmap() { return x86_64_v2.FLAGS_MASK; }
+    @Override public void encoding( Encoding enc ) {
+        // REX.W + 81 /7 id	CMP r/m64, imm32 | REX.W + 39 /r	CMP r/m64,r64
+        // CMP [mem], imm32
+        short ptr = enc.reg(ptr());
+        short idx = enc.reg(idx());
+        short src = enc.reg(val());
+
+        enc.add1(x86_64_v2.rex(src, ptr, idx));
+        // opcode varies by immediate
+        enc.add1( src == -1 ? 0x39 : 0x81 );
+
+        // includes modrm
+        x86_64_v2.indirectAdr(_scale, idx, ptr, _off, src == -1 ? 7 : src, enc);
+        if( src == -1 ) enc.add4(_imm);
+    }
+
+    // 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(", "));
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CmpX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CmpX86.java
new file mode 100644
index 0000000..3965433
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CmpX86.java
@@ -0,0 +1,18 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2;
+
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.Node;
+
+public class CmpX86 extends RegX86 {
+    CmpX86( Node add ) { super(add); }
+    @Override public String op() { return "cmp"; }
+    @Override int opcode() { return 0x3B; }
+    @Override public RegMask outregmap() { return x86_64_v2.FLAGS_MASK; }
+    @Override public int twoAddress() { return 0; }
+    @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)));
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/DivFX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/DivFX86.java
new file mode 100644
index 0000000..91d0c15
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/DivFX86.java
@@ -0,0 +1,34 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+public class DivFX86 extends MachConcreteNode implements MachNode {
+    DivFX86( Node divf) { super(divf); }
+    @Override public String op() { return "divf"; }
+    @Override public RegMask regmap(int i) { return x86_64_v2.XMASK; }
+    @Override public RegMask outregmap() { return x86_64_v2.XMASK; }
+    @Override public int twoAddress() { return 1; }
+
+    @Override public void encoding( Encoding enc ) {
+        // F2 0F 5E /r DIVSD xmm1, xmm2/m64
+        short dst = (short)(enc.reg(this ) - x86_64_v2.XMM_OFFSET);
+        short src = (short)(enc.reg(in(2)) - x86_64_v2.XMM_OFFSET);
+
+        // Fopcode
+        enc.add1(0xF2);
+        // rex prefix must come next (REX.W is not set)
+        x86_64_v2.rexF(dst, src, 0, false, enc);
+
+        enc.add1(0x0F).add1(0x5E);
+
+        enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, src));
+    }
+
+
+    // General form: "divf  dst /= src"
+    @Override public void asm(CodeGen code, SB sb) {
+        sb.p(code.reg(this)).p(" /= ").p(code.reg(in(2)));
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/DivX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/DivX86.java
new file mode 100644
index 0000000..301cb2c
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/DivX86.java
@@ -0,0 +1,33 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+public class DivX86 extends MachConcreteNode implements MachNode {
+    DivX86( Node div ) { super(div); }
+    @Override public String op() { return "div"; }
+    @Override public RegMask regmap(int i) {
+        return (i==1) ? x86_64_v2.RAX_MASK : x86_64_v2.RMASK;
+    }
+    @Override public RegMask outregmap() { return x86_64_v2.RAX_MASK; }
+    @Override public RegMask killmap() { return x86_64_v2.RDX_MASK; }
+
+    @Override public void encoding( Encoding enc ) {
+        // REX.W + F7 /7	IDIV r/m64
+        short src = enc.reg(in(2));
+        // sign extend rax before 128/64 bits division
+        // sign extend rax to rdx:rax
+        enc.add1(x86_64_v2.REX_W);
+        enc.add1(0x99);
+        enc.add1(x86_64_v2.rex(0, src, 0));
+        // divide
+        enc.add1(0xF7); // opcode
+        enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, 0x07, src));
+    }
+    // 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 comment() { return "kill rdx"; }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/FltX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/FltX86.java
new file mode 100644
index 0000000..a351d6b
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/FltX86.java
@@ -0,0 +1,47 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2;
+
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+public class FltX86 extends ConstantNode implements MachNode, RIPRelSize {
+    private byte _encSize;
+    FltX86(ConstantNode con ) { super(con); }
+    @Override public String op() { return "ld8"; }
+    @Override public RegMask regmap(int i) { return null; }
+    @Override public RegMask outregmap() { return x86_64_v2.XMASK; }
+    @Override public boolean isClone() { return true; }
+    @Override public Node copy() { return new FltX86(this); }
+
+    @Override public void encoding( Encoding enc ) {
+        // Load the constant into a FPR
+        // movsd xmm, [rip + 0]
+        // F2 0F 10 /r MOVSD xmm1, m64
+        _encSize = 1/*Fop*/+0/*REX*/+2/*op*/+1/*MODRM*/+4/*offset*/;
+        short dst = (short)(enc.reg(this ) - x86_64_v2.XMM_OFFSET);
+        enc.largeConstant(this,_con);
+        // Fopcode
+        enc.add1(0xF2);
+        // rex prefix must come next (REX.W is not set)
+        _encSize += x86_64_v2.rexF(dst, 0, 0, false, enc);
+        enc.add1(0x0F).add1(0x10);
+        // hard-code rip here
+        enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.INDIRECT, dst, 0x05));
+        enc.add4(0);
+    }
+
+    // Delta is from opcode start
+    @Override public byte encSize(int delta) {
+        // TODO: 1-byte RIP offset?
+        return (byte)_encSize;
+    }
+
+    // Delta is from opcode start
+    @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) {
+        enc.patch4(opStart+opLen-4,delta-opLen);
+    }
+
+    @Override public void asm(CodeGen code, SB sb) {
+        _con.print(sb.p(code.reg(this)).p(" = #"));
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/FunX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/FunX86.java
new file mode 100644
index 0000000..27da886
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/FunX86.java
@@ -0,0 +1,44 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2;
+
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.FunNode;
+import com.compilerprogramming.ezlang.compiler.nodes.MachNode;
+
+public class FunX86 extends FunNode implements MachNode {
+    FunX86( FunNode fun ) { super(fun); }
+    @Override public String op() { return "addi"; }
+    @Override public RegMask regmap(int i) { return null; }
+    @Override public RegMask outregmap() { return null; }
+    @Override public void postSelect(CodeGen code) { code.link(this);  }
+
+    // Actual stack layout is up to each CPU.
+    // X86, with too many args & spills:
+    // | CALLER |
+    // |  argN  | // slot 1, required by callER
+    // +--------+
+    // |  RPC   | // slot 0, required by callER
+    // | callee | // slot 3, callEE
+    // | callee | // slot 2, callEE
+    // |  PAD16 |
+    // +--------+
+    @Override public void encoding( Encoding enc ) {
+        // Stack frame size: _maxSlot - max(arg), padded to 16.
+        _maxArgSlot = x86_64_v2.maxSlot(enc._fun.sig());
+        int sz = _maxSlot - _maxArgSlot;
+        // If non-leaf function, pad to 16b
+        if( _hasCalls ) sz = ((sz+1) & -2)+1;
+        assert x86_64_v2.imm8(sz*8);
+        _frameAdjust = (short)sz;
+        if( sz == 0 ) return; // Skip if no frame adjust
+
+        // opcode: 0x83, addi rsp with immediate 8
+        enc.add1( 0x83 );
+        enc.add1( x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, 0, x86_64_v2.RSP) );
+        enc.add1(sz*8);
+    }
+
+    @Override public void asm(CodeGen code, SB sb) {
+        sb.p("rsp += #").p(_frameAdjust*8);
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/I2f8X86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/I2f8X86.java
new file mode 100644
index 0000000..d203d86
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/I2f8X86.java
@@ -0,0 +1,31 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+public class I2f8X86 extends MachConcreteNode implements MachNode {
+    I2f8X86(Node i2f8 ) { super(i2f8); }
+    @Override public String op() { return "cvtf"; }
+    @Override public RegMask regmap(int i) { assert i==1; return x86_64_v2.WMASK; }
+    @Override public RegMask outregmap() { return x86_64_v2.XMASK; }
+
+    @Override public void encoding( Encoding enc ) {
+        // F2 0F 2A /r CVTSI2SD xmm1, r32/m32
+        short dst = (short)(enc.reg(this ) - x86_64_v2.XMM_OFFSET);
+        short src =         enc.reg(in(1));
+
+        // Fopcode
+        enc.add1(0xF2);
+        // rex prefix must come next (REX.W is not set)
+        x86_64_v2.rexF(dst, src, 0, true, enc);
+
+        enc.add1(0x0F).add1(0x2A);
+
+        enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, src));
+    }
+
+    @Override public void asm(CodeGen code, SB sb) {
+        sb.p(code.reg(this)).p(" = ").p("(flt)").p(code.reg(in(1)));
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/ImmX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/ImmX86.java
new file mode 100644
index 0000000..f86e852
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/ImmX86.java
@@ -0,0 +1,37 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeInteger;
+
+public abstract class ImmX86 extends MachConcreteNode implements MachNode {
+    final int _imm;
+    ImmX86( Node add, int imm ) {
+        super(add);
+        _inputs.pop();          // Pop constant input off
+        _imm = imm;
+    }
+    @Override public RegMask regmap(int i) { return x86_64_v2.RMASK; }
+    @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
+    @Override public int twoAddress() { return 1; }
+
+    abstract int opcode();
+    abstract int mod();
+
+    @Override public final void encoding( Encoding enc ) {
+        short dst = enc.reg(this); // Also src1
+        enc.add1(x86_64_v2.rex(0, dst, 0));
+        // opcode; 0x81 or 0x83; 0x69 or 0x6B
+        enc.add1( opcode() + (x86_64_v2.imm8(_imm) ? 2 : 0) );
+        enc.add1( x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, mod(), dst) );
+        // immediate(4 bytes) 32 bits or (1 byte)8 bits
+        if( x86_64_v2.imm8(_imm) ) enc.add1(_imm);
+        else                       enc.add4(_imm);
+    }
+
+    // General form: "addi  dst += #imm"
+    @Override public void asm(CodeGen code, SB sb) {
+        sb.p(code.reg(this)).p(" ").p(glabel()).p("= #").p(_imm);
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/IntX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/IntX86.java
new file mode 100644
index 0000000..6067ac8
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/IntX86.java
@@ -0,0 +1,76 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeInteger;
+
+// Integer constants
+public class IntX86 extends ConstantNode implements MachNode {
+    IntX86( ConstantNode con ) { super(con); }
+    @Override public String op() {
+        return _con == SONType.NIL || _con == SONTypeInteger.ZERO ? "xor" : "ldi";
+    }
+    @Override public boolean isClone() { return true; }
+    @Override public Node copy() { return new IntX86(this); }
+    @Override public RegMask regmap(int i) { return null; }
+    @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
+    // Zero-set uses XOR kills flags
+    @Override public RegMask killmap() {
+        return _con == SONType.NIL || _con == SONTypeInteger.ZERO ? x86_64_v2.FLAGS_MASK : null;
+    }
+
+    @Override public void encoding( Encoding enc ) {
+        short dst = enc.reg(this);
+        // Short form for zero
+        if( _con==SONType.NIL || _con==SONTypeInteger.ZERO ) {
+            // XOR dst,dst.  Can skip REX is dst is low 8, makes this a 32b
+            // xor, which will also zero the high bits.
+            if( dst >= 8 ) enc.add1(x86_64_v2.rex(dst, dst, 0));
+            enc.add1(0x33); // opcode
+            enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, dst));
+            return;
+        }
+
+        // Simply move the constant into a GPR
+        // Conditional encoding based on 64 or 32 bits
+        //REX.W + C7 /0 id	MOV r/m64, imm32
+
+        long imm = ((SONTypeInteger)_con).value();
+        if (Integer.MIN_VALUE <= imm && imm < 0) {
+            // We need sign extension, so use imm32 into 64 bit register
+            // REX.W + C7 /0 id    MOV r/m64, imm32
+            enc.add1(x86_64_v2.rex(0, dst, 0));
+            enc.add1(0xC7); // 32 bits encoding
+            enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, 0x00, dst));
+            enc.add4((int)imm);
+        } else if (0 <= imm && imm <= 0xFFFFFFFFL) {
+            // We want zero extension into 64 bit register
+            // so move 32 bit into 32 bit register which zeros
+            // the upper bits in the 64 bit register.
+            // B8+ rd id     MOV r32, imm32
+            if (dst >= 8) enc.add1(0x41);
+            enc.add1(0xB8 + (dst & 0x07));
+            enc.add4((int)imm);
+        } else {
+            // Just write the full 64 bit constant
+            // REX.W + B8+ rd io     MOV r64, imm64
+            enc.add1(x86_64_v2.rex(0, dst, 0));
+            enc.add1(0xB8 + (dst & 0x07));
+            enc.add8(imm);               // 64 bits encoding
+        }
+    }
+
+    // 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 == SONType.NIL || _con == SONTypeInteger.ZERO )
+            sb.p(reg).p(",").p(reg);
+        else
+            _con.print(sb.p(reg).p(" = #"));
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/JmpX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/JmpX86.java
new file mode 100644
index 0000000..426dde7
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/JmpX86.java
@@ -0,0 +1,69 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+// Jump on flags, uses flags
+public class JmpX86 extends IfNode implements MachNode, RIPRelSize {
+    String _bop;
+    JmpX86( IfNode iff, String bop ) {
+        super(iff);
+        _bop = bop;
+    }
+    @Override public String op() { return "j"+_bop; }
+    @Override public String label() { return op(); }
+    @Override public void postSelect(CodeGen code) {
+        Node set = in(1);
+        Node cmp = set.in(1);
+        // Bypass an expected Set and just reference the cmp directly
+        if( set instanceof SetX86 setx && (cmp instanceof CmpX86 || cmp instanceof CmpIX86 || cmp instanceof CmpMemX86 || cmp instanceof CmpFX86) ) {
+            _inputs.set( 1, cmp );
+            _bop = setx._bop;
+        } 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; }
+    @Override public void invert() { _bop = invert(_bop);  }
+
+    @Override public void encoding( Encoding enc ) {
+        enc.jump(this,cproj(0));
+        int op = x86_64_v2.jumpop(_bop);
+        enc.add1(op-16);      // Short form jump
+        enc.add1(0);          // Offset
+        //enc.add1(0x0F).add1(x86_64_v2.jumpop(_bop));
+        //enc.add4(0);            // Offset patched later
+    }
+
+    // Delta is from opcode start, but X86 measures from the end of the 2-byte encoding
+    @Override public byte encSize(int delta) { return (byte)(x86_64_v2.imm8(delta-2) ? 2 : 6); }
+
+    // Delta is from opcode start
+    @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) {
+        byte[] bits = enc.bits();
+        if( opLen==2 ) {
+            assert bits[opStart] == x86_64_v2.jumpop(_bop)-16;
+            delta -= 2;         // Offset from opcode END
+            assert (byte)delta==delta;
+            bits[opStart+1] = (byte)delta;
+        } else {
+            assert bits[opStart] == x86_64_v2.jumpop(_bop)-16;
+            delta -= 6;         // Offset from opcode END
+            bits[opStart] = 0x0F;
+            bits[opStart+1] = (byte)x86_64_v2.jumpop(_bop);
+            enc.patch4(opStart+2,delta);
+        }
+    }
+
+    @Override public void asm(CodeGen code, SB sb) {
+        String src = code.reg(in(1));
+        if( src!="flags" ) sb.p(src).p(" ");
+        CFGNode prj = cproj(0); // 0 is True is jump target
+        while( prj.nOuts() == 1 && !(prj instanceof ReturnNode) )
+            prj = prj.uctrl(); // Skip empty blocks
+        sb.p(label(prj));
+    }
+
+    @Override public String comment() { return "L"+cproj(1)._nid; }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/LeaX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/LeaX86.java
new file mode 100644
index 0000000..3050699
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/LeaX86.java
@@ -0,0 +1,55 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+public class LeaX86 extends MachConcreteNode implements MachNode {
+    final int _scale;
+    final int _offset;
+    LeaX86( Node add, Node base, Node idx, int scale, int offset ) {
+        super(add);
+        assert scale==0 || scale==1 || scale==2 || scale==3;
+        _inputs.pop();
+        _inputs.pop();
+        _inputs.push(base);
+        _inputs.push(idx);
+        _scale = scale;
+        _offset = offset;
+    }
+
+    @Override public String op() { return "lea"; }
+    @Override public RegMask regmap(int i) { assert i==1 || i==2; return x86_64_v2.RMASK; }
+    @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
+
+    @Override public void encoding( Encoding enc ) {
+        // REX.W + 8D /r	LEA r64,m
+        short dst = enc.reg(this);
+        short ptr = enc.reg(in(1));
+        short idx = enc.reg(in(2));
+        // ptr is null
+        // just do: [(index * s) + disp32]
+        if( ptr == -1 ) {
+            enc.add1(x86_64_v2.rex(dst, 0, idx));
+            enc.add1(0x8D); // opcode
+            enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.INDIRECT, dst, 0x04));
+            enc.add1(x86_64_v2.sib(_scale, idx, x86_64_v2.RBP));
+            enc.add4(_offset);
+            return;
+        }
+
+        enc.add1(x86_64_v2.rex(dst, ptr, idx));
+        enc.add1(0x8D); // opcode
+        // rsp is hard-coded here(0x04)
+        x86_64_v2.indirectAdr(_scale, idx, ptr, _offset, dst, enc);
+    }
+
+    // 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);
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/LoadX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/LoadX86.java
new file mode 100644
index 0000000..1f5dc56
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/LoadX86.java
@@ -0,0 +1,80 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2;
+
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.LoadNode;
+import com.compilerprogramming.ezlang.compiler.nodes.Node;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFloat;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeInteger;
+
+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; }
+    @Override public RegMask outregmap() { return x86_64_v2.MEM_MASK; }
+    @Override public void encoding( Encoding enc ) {
+        // REX.W + 8B /r	MOV r64, r/m64
+        // Zero extension for u8, u16 and u32 but sign extension i8, i16, i32
+        // Use movsx and movzx
+
+        short dst = enc.reg(this );
+        short ptr = enc.reg(ptr());
+        short idx = enc.reg(idx());
+
+        if (_declaredType != SONTypeInteger.U32 && _declaredType != SONTypeFloat.F32 && _declaredType != SONTypeFloat.F64) {
+            enc.add1(x86_64_v2.rex(dst, ptr, idx));
+        }
+
+        if(_declaredType == SONTypeFloat.F32) {
+            // F3 0F 10 /r MOVSS xmm1, m32
+            enc.add1(0xF3);
+            enc.add1(0x0F);
+            enc.add1(0x10);
+            dst -= (short)x86_64_v2.XMM_OFFSET;
+        }
+        if(_declaredType == SONTypeFloat.F64) {
+            //  F2 0F 10 /r MOVSD xmm1, m64
+            enc.add1(0xF2);
+            enc.add1(0x0F);
+            enc.add1(0x10);
+            dst -= (short)x86_64_v2.XMM_OFFSET;
+        }
+        if(_declaredType == SONTypeInteger.I8) {
+            // sign extend: REX.W + 0F BE /r	MOVSX r64, r/m8
+            enc.add1(0x0F);
+            enc.add1(0xBE);
+        } else if(_declaredType == SONTypeInteger.I16) {
+            // sign extend: REX.W + 0F BF /r	MOVSX r64, r/m16
+            enc.add1(0x0F);
+            enc.add1(0xBF);
+        } else if(_declaredType == SONTypeInteger.I32) {
+            // sign extend: REX.W + 63 /r	MOVSXD r64, r/m32
+            enc.add1(0x63);
+        } else if(_declaredType == SONTypeInteger.U8) {
+            // zero extend: REX.W + 0F B6 /r	MOVZX r64, r/m8
+            enc.add1(0x0F);
+            enc.add1(0xB6);
+        } else if(_declaredType == SONTypeInteger.U16) {
+            // zero extend:   REX.W + 0F B7 /r	MOVZX r64, r/m16
+            enc.add1(0x0F);
+            enc.add1(0xB7);
+        } else if(_declaredType == SONTypeInteger.U32) {
+            // zero extend:   8B /r	MOV r32, r/m32
+            enc.add1(0x8B);
+        } else if(_declaredType == SONTypeInteger.BOT) {
+            // REX.W + 8B /r    MOV r64, r/m64
+            enc.add1(0x8B);
+        }
+
+        // includes modrm internally
+        x86_64_v2.indirectAdr(_scale, idx, ptr, _off, dst, enc);
+    }
+
+    // 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/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/MemAddX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/MemAddX86.java
new file mode 100644
index 0000000..1096341
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/MemAddX86.java
@@ -0,0 +1,39 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2;
+
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+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 );
+    }
+    @Override public String op() {
+        return (_imm == 1 ? "inc" : (_imm == -1 ? "dec" : "add")) + _sz;
+    }
+    // Register mask allowed as a result.  0 for no register.
+    @Override public RegMask outregmap() { return null; }
+    @Override public void encoding( Encoding enc ) {
+        // add something to memory
+        // REX.W + 01 /r | REX.W + 81 /0 id
+        // ADD [mem], imm32/reg
+        short ptr = enc.reg(ptr());
+        short idx = enc.reg(idx());
+        short src = enc.reg(val());
+
+        enc.add1(x86_64_v2.rex(src, ptr, idx));
+        // opcode
+        enc.add1( src == -1 ? 0x01 : 0x81 );
+
+        // includes modrm
+        x86_64_v2.indirectAdr(_scale, idx, ptr, _off, src == -1 ? 0 : src, enc);
+        if( src == -1 ) enc.add4(_imm);
+    }
+    // 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()));
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/MemOpX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/MemOpX86.java
new file mode 100644
index 0000000..f511e97
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/MemOpX86.java
@@ -0,0 +1,85 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+import com.compilerprogramming.ezlang.compiler.sontypes.*;
+
+import java.lang.StringBuilder;
+import java.util.BitSet;
+
+// 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 SONTypeMemPtr && !(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); } // Only for stores, including op-to-memory
+
+    @Override public  StringBuilder _printMach(StringBuilder sb, BitSet visited) {
+        return sb.append(".").append(_name);
+    }
+
+    @Override public String label() { return op(); }
+    @Override public SONType 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  in GPR
+        if( i==3 ) return x86_64_v2.RMASK;    // index in GPR
+        if( i==4 ) return x86_64_v2.MEM_MASK; // value in GPR or XMM
+        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((1<<_scale));
+        }
+        if( _off != 0 )
+            sb.p("+").p(_off);
+        return sb.p("]");
+    }
+
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/MulFX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/MulFX86.java
new file mode 100644
index 0000000..73bca29
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/MulFX86.java
@@ -0,0 +1,32 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+public class MulFX86 extends MachConcreteNode implements MachNode {
+    MulFX86( Node mulf) { super(mulf); }
+    @Override public String op() { return "mulf"; }
+    @Override public RegMask regmap(int i) { assert i==1 || i==2; return x86_64_v2.XMASK; }
+    @Override public RegMask outregmap() { return x86_64_v2.XMASK; }
+    @Override public int twoAddress() { return 1; }
+    @Override public boolean commutes() { return true; }
+
+    @Override public void encoding( Encoding enc ) {
+        // F2 0F 59 /r MULSD xmm1,xmm2/m64
+        short dst = (short)(enc.reg(this ) - x86_64_v2.XMM_OFFSET);
+        short src = (short)(enc.reg(in(2)) - x86_64_v2.XMM_OFFSET);
+
+        // Fopcode
+        enc.add1(0xF2);
+        // rex prefix must come next (REX.W is not set)
+        x86_64_v2.rexF(dst, src, 0, false, enc);
+
+        enc.add1(0x0F).add1(0x59).add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, src));
+    }
+
+    // General form: "mulf  dst *= src"
+    @Override public void asm(CodeGen code, SB sb) {
+        sb.p(code.reg(this)).p(" *= ").p(code.reg(in(2)));
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/MulIX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/MulIX86.java
new file mode 100644
index 0000000..c53599d
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/MulIX86.java
@@ -0,0 +1,11 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2;
+
+import com.compilerprogramming.ezlang.compiler.nodes.Node;
+
+public class MulIX86 extends ImmX86 {
+    MulIX86( Node add, int imm ) { super(add,imm); }
+    @Override public String op() { return "muli"; }
+    @Override public String glabel() { return "*"; }
+    @Override int opcode() { return 0x69; }
+    @Override int mod() { return 0; }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/MulX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/MulX86.java
new file mode 100644
index 0000000..04c9a51
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/MulX86.java
@@ -0,0 +1,29 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+public class MulX86 extends MachConcreteNode implements MachNode {
+    MulX86( Node mul ) { super(mul); }
+    @Override public String op() { return "mul"; }
+    @Override public RegMask regmap(int i) { assert i==1 || i==2; return x86_64_v2.RMASK; }
+    @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
+    @Override public int twoAddress() { return 1; }
+    @Override public boolean commutes() { return true; }
+
+    @Override public void encoding( Encoding enc ) {
+        // REX.W + 0F AF /r	IMUL r64, r/m64
+        short dst = enc.reg(this ); // src1
+        short src = enc.reg(in(2)); // src2
+        enc.add1(x86_64_v2.rex(dst, src, 0));
+        enc.add1(0x0F); // opcode
+        enc.add1(0xAF); // opcode
+        enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, src));
+    }
+
+    // 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)));
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/NewX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/NewX86.java
new file mode 100644
index 0000000..6d5639b
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/NewX86.java
@@ -0,0 +1,30 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+public class NewX86 extends NewNode implements MachNode {
+    // A pre-zeroed chunk of memory.
+    NewX86( NewNode nnn ) { super(nnn); }
+    @Override public String op() { return "alloc"; }
+    // Size and pointer result in standard calling convention; null for all the
+    // memory aliases edges
+    @Override public RegMask    regmap(int i) { return i == 1 ? x86_64_v2.RDI_MASK : null; }
+    @Override public RegMask outregmap(int i) { return i == 1 ? x86_64_v2.RAX_MASK : null; }
+    @Override public RegMask outregmap() { return null; }
+    @Override public RegMask killmap() { return x86_64_v2.x86CallerSave(); }
+
+    @Override public void encoding( Encoding enc ) {
+        enc.external(this,"calloc");
+        // E8 cd    CALL rel32;
+        enc.add1(0xE8);
+        enc.add4(0);            // offset
+    }
+
+    // General form: "alloc #bytes"
+    @Override public void asm(CodeGen code, SB sb) {
+        sb.p("#calloc, ").p(code.reg(size()));
+    }
+
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/NotX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/NotX86.java
new file mode 100644
index 0000000..3a270d9
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/NotX86.java
@@ -0,0 +1,39 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2;
+
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+public class NotX86 extends MachConcreteNode implements MachNode {
+    NotX86(NotNode not) { super(not); }
+    @Override public String op() { return "not"; }
+    @Override public RegMask regmap(int i) { return x86_64_v2.RMASK; }
+    @Override public RegMask outregmap() { return x86_64_v2.RMASK;  }
+    @Override public RegMask killmap() { return x86_64_v2.FLAGS_MASK; }
+
+    @Override public void encoding( Encoding enc ) {
+        assert !(in(1) instanceof  NotNode); // Cleared out by peeps
+        assert !(in(1) instanceof BoolNode); // Cleared out by peeps
+        short dst = enc.reg(this );
+        short src = enc.reg(in(1));
+
+        // Pre-zero using XOR dst,dst; since zero'd will not have a
+        // byte-dependency from the setz.  Can skip REX is dst is low 8, makes
+        // this a 32b xor, which will also zero the high bits.
+        if( dst >= 8 ) enc.add1(x86_64_v2.rex(dst, dst, 0));
+        enc.add1(0x33); // opcode
+        enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, dst));
+
+        // test   rdi,rdi
+        enc.add1(x86_64_v2.rex(src, src, 0));
+        enc.add1(0x85);
+        enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, src, src));
+
+        // setz (sete dil)
+        enc.add1(x86_64_v2.rex(dst, 0, 0));
+        enc.add1(0x0F);
+        enc.add1(0x94);
+        enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, 0));
+    }
+    @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(this)); }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/OrIX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/OrIX86.java
new file mode 100644
index 0000000..cac195d
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/OrIX86.java
@@ -0,0 +1,11 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2;
+
+import com.compilerprogramming.ezlang.compiler.nodes.Node;
+
+public class OrIX86 extends ImmX86 {
+    OrIX86( Node add, int imm ) { super(add,imm); }
+    @Override public String op() { return "ori"; }
+    @Override public String glabel() { return "|"; }
+    @Override int opcode() { return 0x81; }
+    @Override int mod() { return 1; }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/OrX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/OrX86.java
new file mode 100644
index 0000000..99a73db
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/OrX86.java
@@ -0,0 +1,11 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2;
+
+import com.compilerprogramming.ezlang.compiler.nodes.Node;
+
+public class OrX86 extends RegX86 {
+    OrX86( Node add ) { super(add); }
+    @Override public String op() { return "or"; }
+    @Override public String glabel() { return "|"; }
+    @Override int opcode() { return 0x0B; }
+    @Override public boolean commutes() { return true; }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/ParmX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/ParmX86.java
new file mode 100644
index 0000000..33995f1
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/ParmX86.java
@@ -0,0 +1,17 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2;
+
+import com.compilerprogramming.ezlang.compiler.codegen.Encoding;
+import com.compilerprogramming.ezlang.compiler.codegen.RegMask;
+import com.compilerprogramming.ezlang.compiler.nodes.MachNode;
+import com.compilerprogramming.ezlang.compiler.nodes.ParmNode;
+
+public class ParmX86 extends ParmNode implements MachNode {
+    final RegMask _rmask;
+    ParmX86( ParmNode parm ) {
+        super(parm);
+        _rmask = x86_64_v2.callInMask(fun().sig(),_idx);
+    }
+    @Override public RegMask regmap(int i) { return null; }
+    @Override public RegMask outregmap() { return _rmask; }
+    @Override public void encoding( Encoding enc ) { }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/ProjX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/ProjX86.java
new file mode 100644
index 0000000..f5c765c
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/ProjX86.java
@@ -0,0 +1,15 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2;
+
+import com.compilerprogramming.ezlang.compiler.codegen.Encoding;
+import com.compilerprogramming.ezlang.compiler.codegen.RegMask;
+import com.compilerprogramming.ezlang.compiler.nodes.MachNode;
+import com.compilerprogramming.ezlang.compiler.nodes.ProjNode;
+
+public class ProjX86 extends ProjNode implements MachNode {
+    ProjX86( ProjNode p ) { super(p); }
+    @Override public RegMask regmap(int i) { return null; }
+    @Override public RegMask outregmap() {
+        return ((MachNode)in(0)).outregmap(_idx);
+    }
+    @Override public void encoding( Encoding enc ) {}
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/RegX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/RegX86.java
new file mode 100644
index 0000000..fc49513
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/RegX86.java
@@ -0,0 +1,25 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+public abstract class RegX86 extends MachConcreteNode {
+    RegX86( Node add ) { super(add); }
+    @Override public RegMask regmap(int i) { return x86_64_v2.RMASK; }
+    @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
+    @Override public int twoAddress() { return 1; }
+    abstract int opcode();
+    @Override public void encoding( Encoding enc ) {
+        // REX.W + 01 /r
+        short dst = enc.reg(this ); // src1
+        short src = enc.reg(in(2)); // src2
+        enc.add1(x86_64_v2.rex(dst, src, 0));
+        enc.add1(opcode()); // opcode
+        enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, src));
+    }
+    // General form: "add  dst += src"
+    @Override public void asm(CodeGen code, SB sb) {
+        sb.p(code.reg(this)).p(" ").p(glabel()).p("= ").p(code.reg(in(2)));
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/RetX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/RetX86.java
new file mode 100644
index 0000000..ce4d36f
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/RetX86.java
@@ -0,0 +1,41 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+public class RetX86 extends ReturnNode implements MachNode {
+    RetX86( ReturnNode ret, FunNode fun ) { super(ret, fun); fun.setRet(this); }
+    @Override public String op() {
+        return ((FunX86)fun())._frameAdjust > 0 ? "addi" : "ret";
+    }
+    // Correct Nodes outside the normal edges
+    @Override public void postSelect(CodeGen code) {
+        FunNode fun = (FunNode)rpc().in(0);
+        _fun = fun;
+        fun.setRet(this);
+    }
+    @Override public RegMask regmap(int i) { return x86_64_v2.retMask(_fun.sig(),i); }
+    @Override public RegMask outregmap() { return null; }
+    @Override public void encoding( Encoding enc ) {
+        int frameAdjust = ((FunX86)fun())._frameAdjust;
+        if( frameAdjust > 0 ) {
+            enc.add1( 0x83 );
+            enc.add1( x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, 0, x86_64_v2.RSP) );
+            enc.add1(frameAdjust*-8);
+        }
+        enc.add1(0xC3);
+    }
+
+    @Override public void asm(CodeGen code, SB sb) {
+        int frameAdjust = ((FunX86)fun())._frameAdjust;
+        if( frameAdjust>0 )
+            sb.p("rsp += #").p(frameAdjust*-8).p("\nret");
+        // Post code-gen, just print the "ret"
+        if( code._phase.ordinal() <= CodeGen.Phase.RegAlloc.ordinal() )
+            // Prints return reg (either RAX or XMM0), RPC (always [rsp-4]) and
+            // then the callee-save registers.
+            for( int i=2; i<nIns(); i++ )
+                sb.p(code.reg(in(i))).p("  ");
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/SarIX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/SarIX86.java
new file mode 100644
index 0000000..ac1a420
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/SarIX86.java
@@ -0,0 +1,12 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2;
+
+import com.compilerprogramming.ezlang.compiler.nodes.Node;
+
+// Arithmetic Right Shift
+public class SarIX86 extends ImmX86 {
+    SarIX86( Node add, int imm ) { super(add,imm); assert x86_64_v2.imm8(imm); }
+    @Override public String op() { return "sari"; }
+    @Override public String glabel() { return ">>"; }
+    @Override int opcode() { return 0xC1; }
+    @Override int mod() { return 7; }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/SarX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/SarX86.java
new file mode 100644
index 0000000..7b91b69
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/SarX86.java
@@ -0,0 +1,24 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2;
+
+import com.compilerprogramming.ezlang.compiler.Utils;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.Node;
+
+public class SarX86 extends RegX86 {
+    SarX86( Node add ) { super(add); }
+    @Override public String op() { return "sar"; }
+    @Override public String glabel() { return ">>"; }
+    @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 int opcode() { return 0xD3; }
+    @Override public void encoding( Encoding enc ) {
+        short dst = enc.reg(this ); // src1
+        short src = enc.reg(in(2)); // src2
+        enc.add1(x86_64_v2.rex(0, dst, 0));
+        enc.add1(opcode()); // opcode
+        enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, 7, dst));
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/SetX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/SetX86.java
new file mode 100644
index 0000000..7ee2305
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/SetX86.java
@@ -0,0 +1,50 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+// 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 String op() { return "set"+_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; }
+
+    @Override public void encoding( Encoding enc ) {
+        // REX + 0F 94
+        short dst = enc.reg(this );
+
+        // Optional rex, for dst
+        if( dst >= 8 ) enc.add1(x86_64_v2.rex(0, dst, 0));
+        enc.add1(0x0F);         // opcode
+        enc.add1(switch (_bop) {
+            case "==" -> 0x94;  // SETE
+            case "<"  -> 0x9C;  // SETL
+            case "<=" -> 0X9E;  // SETLE
+            default -> throw Utils.TODO();
+            });
+        enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, 0, dst));
+
+        // low 8 bites are set, now zero extend for next instruction
+        if( dst >= 8 ) enc.add1(x86_64_v2.rex(0, dst, 0));
+        enc.add1(0x0F); // opcode
+        enc.add1(0xB6); // opcode
+        enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, dst));
+    }
+
+    @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);
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/ShlIX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/ShlIX86.java
new file mode 100644
index 0000000..ee8dfb6
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/ShlIX86.java
@@ -0,0 +1,11 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2;
+
+import com.compilerprogramming.ezlang.compiler.nodes.Node;
+
+public class ShlIX86 extends ImmX86 {
+    ShlIX86( Node add, int imm ) { super(add,imm); }
+    @Override public String op() { return "shli"; }
+    @Override public String glabel() { return "<<"; }
+    @Override int opcode() { return 0xC1; }
+    @Override int mod() { return 4; }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/ShlX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/ShlX86.java
new file mode 100644
index 0000000..8aeb594
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/ShlX86.java
@@ -0,0 +1,24 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2;
+
+import com.compilerprogramming.ezlang.compiler.Utils;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.Node;
+
+public class ShlX86 extends RegX86 {
+    ShlX86( Node add ) { super(add); }
+    @Override public String op() { return "shl"; }
+    @Override public String glabel() { return "<<"; }
+    @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 int opcode() { return 0xD3; }
+    @Override public final void encoding( Encoding enc ) {
+        short dst = enc.reg(this ); // src1
+        short src = enc.reg(in(2)); // src2
+        enc.add1(x86_64_v2.rex(0, dst, 0));
+        enc.add1(opcode()); // opcode
+        enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, 4, dst));
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/ShrIX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/ShrIX86.java
new file mode 100644
index 0000000..de39108
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/ShrIX86.java
@@ -0,0 +1,11 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2;
+
+import com.compilerprogramming.ezlang.compiler.nodes.Node;
+
+public class ShrIX86 extends ImmX86 {
+    ShrIX86( Node add, int imm ) { super(add,imm); }
+    @Override public String op() { return "shri"; }
+    @Override public String glabel() { return ">>>"; }
+    @Override int opcode() { return 0xC1; }
+    @Override int mod() { return 5; }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/ShrX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/ShrX86.java
new file mode 100644
index 0000000..d0a42f5
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/ShrX86.java
@@ -0,0 +1,24 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2;
+
+import com.compilerprogramming.ezlang.compiler.Utils;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.Node;
+
+public class ShrX86 extends RegX86 {
+    ShrX86( Node add ) { super(add); }
+    @Override public String op() { return "shr"; }
+    @Override public String glabel() { return ">>>"; }
+    @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 int opcode() { return 0xD3; }
+    @Override public final void encoding( Encoding enc ) {
+        short dst = enc.reg(this ); // src1
+        short src = enc.reg(in(2)); // src2
+        enc.add1(x86_64_v2.rex(0, dst, 0));
+        enc.add1(opcode()); // opcode
+        enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, 5, dst));
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/SplitX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/SplitX86.java
new file mode 100644
index 0000000..49129a2
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/SplitX86.java
@@ -0,0 +1,99 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2;
+
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.Utils;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.Node;
+import com.compilerprogramming.ezlang.compiler.nodes.SplitNode;
+
+public class SplitX86 extends SplitNode {
+    SplitX86( String kind, byte round ) { super(kind,round, new Node[2]); }
+    @Override public String op() { return "mov"; }
+    @Override public RegMask regmap(int i) { return x86_64_v2.SPLIT_MASK; }
+    @Override public RegMask outregmap() { return x86_64_v2.SPLIT_MASK; }
+
+    // Need to handle 8 cases: . reg->reg, reg->xmm, reg->flags, xmm->reg, xmm->xmm, xmm->flags, flags->reg, flags->xmm,
+    // flags->flags.
+    @Override public void encoding( Encoding enc ) {
+        // REX.W + 8B /r	MOV r64, r/m64
+        short dst = enc.reg(this );
+        short src = enc.reg(in(1));
+
+        if( dst == x86_64_v2.FLAGS ) {
+            // mov reg, flags
+            // push rcx
+            // popf (Pop the top of the stack into the FLAGS register)
+            // 50+rd	PUSH r64
+            enc.add1(0x50 + src);
+            // popf
+            enc.add1(0x9D);
+            return;
+        }
+        if( src == x86_64_v2.FLAGS ) {
+            // mov flags, reg
+            // pushf; pop reg
+            enc.add1(0x9C);
+            // 58+ rd	POP r64
+            enc.add1(0x58 + dst);
+            return;
+        }
+
+        // Flag for being either XMM or stack
+        boolean dstX = dst >= x86_64_v2.XMM_OFFSET;
+        boolean srcX = src >= x86_64_v2.XMM_OFFSET;
+
+        // Stack spills
+        if( dst >= x86_64_v2.MAX_REG ) {
+            if( src >= x86_64_v2.MAX_REG )
+                throw Utils.TODO(); // Very rare stack-stack move
+            // TODO: Missing FP 0x66 prefix
+            if( srcX ) { src -= (short)x86_64_v2.XMM_OFFSET;  enc.add1(0x66); }
+            int off = enc._fun.computeStackSlot(dst - x86_64_v2.MAX_REG)*8;
+            enc.add1(x86_64_v2.rex(src, x86_64_v2.RSP, -1));
+            enc.add1( 0x89 );
+            x86_64_v2.indirectAdr(0, (short)-1, (short)x86_64_v2.RSP, off, src, enc);
+            return;
+        }
+        if( src >= x86_64_v2.MAX_REG ) {
+            if( dstX ) { dst -= (short)x86_64_v2.XMM_OFFSET;  enc.add1(0x66); }
+            int off = enc._fun.computeStackSlot(src - x86_64_v2.MAX_REG)*8;
+            enc.add1(x86_64_v2.rex(dst, x86_64_v2.RSP, -1));
+            enc.add1( 0x8B );
+            x86_64_v2.indirectAdr(0, (short)-1, (short)x86_64_v2.RSP, off, dst, enc);
+            return;
+        }
+
+        // reg-reg move.  Adjust numbering for GPR vs FPR reg set
+        if( dstX ) dst -= (short) x86_64_v2.XMM_OFFSET;
+        if( srcX ) src -= (short) x86_64_v2.XMM_OFFSET;
+
+        // 0x66 if moving between register classes
+        if( dstX ^ srcX )  enc.add1(0x66);
+        enc.add1(x86_64_v2.rex(dst, src, 0));
+
+        // pick opcode based on regs
+        if( !dstX && !srcX ) {
+            // reg->reg (MOV r64, r/m64)
+            enc.add1(0x8B);
+        } else if( dstX && srcX ) {
+            // xmm->xmm (NP 0F 28 /r MOVAPS xmm1, xmm2/m128)
+            enc.add1(0x0F);
+            enc.add1(0x28);
+        } else if( dstX && !srcX ) {
+            // xmm->reg (66 REX.W 0F 6E /r MOVQ xmm, r/m64)
+            enc.add1(0x0F);
+            enc.add1(0x6E);
+        } else if( !dstX && srcX ) {
+            // reg->xmm(66 REX.W 0F 7E /r MOVQ r/m64, xmm)
+            enc.add1(0x0F);
+            enc.add1(0x7E);
+        }
+
+        enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, src));
+    }
+
+    // General form: "mov  dst = src"
+    @Override public void asm(CodeGen code, SB sb) {
+        sb.p(code.reg(this)).p(" = ").p(code.reg(in(1)));
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/StoreX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/StoreX86.java
new file mode 100644
index 0000000..501121f
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/StoreX86.java
@@ -0,0 +1,63 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2;
+
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.ConstantNode;
+import com.compilerprogramming.ezlang.compiler.nodes.Node;
+import com.compilerprogramming.ezlang.compiler.nodes.StoreNode;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFloat;
+
+import java.util.BitSet;
+
+public class StoreX86 extends MemOpX86 {
+    StoreX86( StoreNode st, Node base, Node idx, int off, int scale, int imm, Node val ) {
+        super(st,st, base, idx, off, scale, imm, val);
+    }
+    @Override public String op() { return "st"+_sz; }
+    @Override public StringBuilder _printMach(StringBuilder sb, BitSet visited) {
+        Node val = val();
+        sb.append(".").append(_name).append("=");
+        if( val==null ) sb.append(_imm);
+        else val._print0(sb,visited);
+        return sb.append(";");
+    }
+    // Register mask allowed as a result.  0 for no register.
+    @Override public RegMask outregmap() { return null; }
+    @Override public void encoding( Encoding enc ) {
+        // REX.W + C7 /0 id	MOV r/m64, imm32 |
+        // REX.W + 89 /r        MOV r/m64, r64
+        short ptr = enc.reg(ptr());
+        short idx = enc.reg(idx());
+        short src = enc.reg(val());
+
+        int imm_op = x86_64_v2.selectOpcodeForImmStore(_imm);
+        if(src == -1 && _imm != 0) {
+            if(imm_op == -1)         {enc.add1(x86_64_v2.rex(src, ptr, idx));enc.add1(0xC7); }
+            else enc.add1(imm_op);
+        } else {
+            if(_declaredType == SONTypeFloat.F32) {src -= (short)x86_64_v2.XMM_OFFSET; enc.add1(0xF3); enc.add1(0x0F); enc.add1(0x11);}
+            else if(_declaredType == SONTypeFloat.F64) {src -= (short)x86_64_v2.XMM_OFFSET; enc.add1(0xF2); enc.add1(0x0F); enc.add1(0x11);}
+            else if(_declaredType.log_size() == 0) enc.add1(0x88);
+            else if(_declaredType.log_size() == 1) enc.add1(0x89);
+            else if(_declaredType.log_size() == 2) enc.add1(0x89);
+            else if(_declaredType.log_size() == 3) {enc.add1(x86_64_v2.rex(src, ptr, idx)); enc.add1(0x89);}
+        }
+
+        x86_64_v2.indirectAdr(_scale, idx, ptr, _off, src, enc);
+        if( src == -1 ) {
+            switch (x86_64_v2.imm_size(_imm)) {
+                case 8: enc.add1(_imm); break;
+                case 16: enc.add2(_imm); break;
+                case 32: enc.add4(_imm); break;
+                case 64: enc.add8(_imm); break;
+            }
+        }
+    }
+
+    // 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/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/SubFX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/SubFX86.java
new file mode 100644
index 0000000..2b03bc0
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/SubFX86.java
@@ -0,0 +1,31 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+public class SubFX86 extends MachConcreteNode implements MachNode {
+    SubFX86( Node subf) {  super(subf); }
+    @Override public String op() { return "subf"; }
+    @Override public RegMask regmap(int i) { assert i==1 || i==2; return x86_64_v2.XMASK; }
+    @Override public RegMask outregmap() { return x86_64_v2.XMASK; }
+    @Override public int twoAddress() { return 1; }
+
+    @Override public void encoding( Encoding enc ) {
+        // F2 0F 5C /r SUBSD xmm1, xmm2/m64
+        short dst = (short)(enc.reg(this ) - x86_64_v2.XMM_OFFSET);
+        short src = (short)(enc.reg(in(2)) - x86_64_v2.XMM_OFFSET);
+        // Fopcode
+        enc.add1(0xF2);
+        // rex prefix must come next (REX.W is not set)
+        x86_64_v2.rexF(dst, src, 0, false, enc);
+
+        enc.add1(0x0F).add1(0x5C);
+        enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, src));
+    }
+
+    // General form: "subf  dst -= src"
+    @Override public void asm(CodeGen code, SB sb) {
+        sb.p(code.reg(this)).p(" -= ").p(code.reg(in(2)));
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/SubX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/SubX86.java
new file mode 100644
index 0000000..e5156a4
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/SubX86.java
@@ -0,0 +1,10 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2;
+
+import com.compilerprogramming.ezlang.compiler.nodes.Node;
+
+public class SubX86 extends RegX86 {
+    SubX86( Node add ) { super(add); }
+    @Override public String op() { return "sub"; }
+    @Override public String glabel() { return "-"; }
+    @Override int opcode() { return 0x2B; }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/TFPX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/TFPX86.java
new file mode 100644
index 0000000..4861b8d
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/TFPX86.java
@@ -0,0 +1,66 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+import com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv.riscv;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFunPtr;
+
+// Function constants
+public class TFPX86 extends ConstantNode implements MachNode, RIPRelSize {
+    TFPX86( ConstantNode con ) {  super(con); }
+    @Override public String op() {
+        return _con == SONType.NIL ? "xor" : "ldx";
+    }
+    @Override public boolean isClone() { return true; }
+    @Override public Node copy() { return new TFPX86(this); }
+    @Override public RegMask regmap(int i) { return null; }
+    @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
+    // Zero-set uses XOR kills flags
+    @Override public RegMask killmap() {
+        return _con == SONType.NIL ? x86_64_v2.FLAGS_MASK : null;
+    }
+
+    @Override public void encoding( Encoding enc ) {
+        short dst = enc.reg(this);
+        // Short form for zero
+        if( _con==SONType.NIL ) {
+            // XOR dst,dst.  Can skip REX is dst is low 8, makes this a 32b
+            // xor, which will also zero the high bits.
+            if( dst >= 8 ) enc.add1(x86_64_v2.rex(dst, dst, 0));
+            enc.add1(0x33); // opcode
+            enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, dst));
+            return;
+        }
+
+        // lea to load function pointer address
+        // lea rax, [rip+disp32]
+        enc.relo(this);
+        enc.add1(x86_64_v2.rex(dst, 0, 0));
+        enc.add1(0x8D); // opcode
+        enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.INDIRECT, dst, 0b101));
+        enc.add4(0);
+    }
+
+    @Override public byte encSize(int delta) {
+        return 7;
+    }
+
+    // Delta is from opcode start
+    @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) {
+        enc.patch4(opStart + 3, delta - 7);
+    }
+
+    // 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 == SONType.NIL )
+            sb.p(reg).p(",").p(reg);
+        else
+            _con.print(sb.p(reg).p(" = #"));
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/UJmpX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/UJmpX86.java
new file mode 100644
index 0000000..c70a884
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/UJmpX86.java
@@ -0,0 +1,53 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
+import java.util.BitSet;
+
+// unconditional jump
+public class UJmpX86 extends CFGNode implements MachNode, RIPRelSize {
+    UJmpX86() { }
+    @Override public String op() { return "jmp"; }
+    @Override public String label() { return op(); }
+    @Override public StringBuilder _print1( StringBuilder sb, BitSet visited ) {
+        return sb.append("jmp ");
+    }
+    @Override public RegMask regmap(int i) {return null; }
+    @Override public RegMask outregmap() { return null; }
+    @Override public SONType compute() { throw Utils.TODO(); }
+    @Override public Node idealize() { throw Utils.TODO(); }
+    @Override public void encoding( Encoding enc ) {
+        enc.jump(this,uctrl());
+        // Short-form jump
+        enc.add1(0xEB).add1(0);
+        //// E9 cd	JMP rel32
+        //enc.add1(0xE9).add4(0);
+    }
+
+    // Delta is from opcode start, but X86 measures from the end of the 2-byte encoding
+    @Override public byte encSize(int delta) { return (byte)(x86_64_v2.imm8(delta-2) ? 2 : 5); }
+
+    // Delta is from opcode start
+    @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) {
+        byte[] bits = enc.bits();
+        if( opLen==2 ) {
+            assert bits[opStart] == (byte)0xEB;
+            delta -= 2;         // Offset from opcode END
+            assert (byte)delta==delta;
+            bits[opStart+1] = (byte)delta;
+        } else {
+            assert bits[opStart] == (byte)0xEB;
+            bits[opStart] = (byte)0xE9; // Long form
+            delta -= 5;         // Offset from opcode END
+            enc.patch4(opStart+1,delta);
+        }
+    }
+
+    @Override public void asm(CodeGen code, SB sb) {
+        CFGNode target = uctrl();
+        assert target.nOuts() > 1; // Should optimize jmp to empty targets
+        sb.p(label(target));
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/XorIX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/XorIX86.java
new file mode 100644
index 0000000..ab7ceb8
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/XorIX86.java
@@ -0,0 +1,11 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2;
+
+import com.compilerprogramming.ezlang.compiler.nodes.Node;
+
+public class XorIX86 extends ImmX86 {
+    XorIX86( Node add, int imm ) { super(add,imm); }
+    @Override public String op() { return "xori"; }
+    @Override public String glabel() { return "^"; }
+    @Override int opcode() { return 0x81; }
+    @Override int mod() { return 6; }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/XorX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/XorX86.java
new file mode 100644
index 0000000..155c5ba
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/XorX86.java
@@ -0,0 +1,11 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2;
+
+import com.compilerprogramming.ezlang.compiler.nodes.Node;
+
+public class XorX86 extends RegX86 {
+    XorX86( Node add ) { super(add); }
+    @Override public String op() { return "xor"; }
+    @Override public String glabel() { return "^"; }
+    @Override int opcode() { return 0x33; }
+    @Override public boolean commutes() { return true; }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/x86_64_v2.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/x86_64_v2.java
new file mode 100644
index 0000000..a7691d7
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/x86_64_v2.java
@@ -0,0 +1,678 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2;
+
+import com.compilerprogramming.ezlang.compiler.Utils;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+import com.compilerprogramming.ezlang.compiler.sontypes.*;
+
+import java.io.ByteArrayOutputStream;
+
+public class x86_64_v2 extends Machine {
+    public x86_64_v2( CodeGen code ) {}
+    // X86-64 V2.  Includes e.g. SSE4.2 and POPCNT.
+    @Override public String name() { return "x86_64_v2"; }
+    @Override public int defaultOpSize() { return 5; }
+
+    public static final int RAX = 0, RCX = 1, RDX =  2, RBX =  3, RSP =  4, RBP =  5, RSI =  6, RDI =  7;
+    public static final int R08 = 8, R09 = 9, R10 = 10, R11 = 11, R12 = 12, R13 = 13, R14 = 14, R15 = 15;
+
+    public static final int XMM0  = 16, XMM1  = 17, XMM2  = 18, XMM3  = 19, XMM4  = 20, XMM5  = 21, XMM6  = 22, XMM7  = 23;
+    public static final int XMM8  = 24, XMM9  = 25, XMM10 = 26, XMM11 = 27, XMM12 = 28, XMM13 = 29, XMM14 = 30, XMM15 = 31;
+    public static final int FLAGS = 32;
+    public static final int MAX_REG = 33;
+    public static final int RPC = 33;
+
+    public static int XMM_OFFSET = 16;
+    // General purpose register mask: pointers and ints, not floats
+    static final long RD_BITS = 0b1111111111111111L; // All the GPRs
+    static RegMask RMASK = new RegMask(RD_BITS);
+    // No RSP in the *write* general set.
+    static final long WR_BITS = 0b1111111111101111L; // All the GPRs minus RSP
+    static RegMask WMASK = new RegMask(WR_BITS);
+    // Xmm register mask
+    static final long FP_BITS = 0b1111111111111111L << XMM0; // All the XMMs
+    static RegMask XMASK = new RegMask(FP_BITS);
+    static RegMask FLAGS_MASK = new RegMask(FLAGS);
+    static RegMask RPC_MASK = new RegMask(RPC);
+
+    static final long SPILLS = -(1L << MAX_REG);
+    static final RegMask SPLIT_MASK = new RegMask(WR_BITS | FP_BITS | (1L<<FLAGS) | SPILLS, -1L );
+
+    // Load/store mask; both GPR and FPR
+    static RegMask MEM_MASK = new RegMask(WR_BITS | FP_BITS);
+
+    public static RegMask RAX_MASK = new RegMask(RAX);
+    public static RegMask RCX_MASK = new RegMask(RCX);
+    public static RegMask RDX_MASK = new RegMask(RDX);
+    public static RegMask RDI_MASK = new RegMask(RDI);
+    public static RegMask R08_MASK = new RegMask(R08);
+    public static RegMask R09_MASK = new RegMask(R09);
+    public static RegMask RSI_MASK = new RegMask(RSI);
+
+    public static RegMask XMM0_MASK = new RegMask(XMM0);
+
+    // Encoding
+    public static int REX = 0x40;
+    public static int REX_W  = 0x48;
+    public static int REX_WR = 0x4C;
+    public static int REX_WRB= 0x4D;
+    public static int REX_WB = 0x49;
+
+    public enum MOD {
+        INDIRECT, //  [mem]
+        INDIRECT_disp8, // [mem + 0x12]
+        INDIRECT_disp32,// [mem + 0x12345678]
+        DIRECT,          // mem
+    };
+
+    // opcode included here
+    // 0F 84 cd	JE  rel32
+    // 0F 85 cd	JNE rel32
+    // 0F 8F cd	JG  rel32
+    // 0F 8C cd	JL  rel32
+    // 0F 8E cd	JLE rel32
+    // 0F 8D cd	JGE rel32
+    static public int jumpop(String op) {
+        return switch (op) {
+        case "==" -> 0x84;
+        case "!=" -> 0x85;
+        case ">"  -> 0x8F;
+        case "<"  -> 0x8C;
+        case "<=" -> 0x8E;
+        case ">=" -> 0X8D;
+        default -> throw new IllegalArgumentException("Too many arguments");
+        };
+    }
+
+    public static int modrm(MOD mod, int reg, int m_r) {
+        // combine all the bits
+        return (mod.ordinal() << 6) | ((reg & 0x07) << 3) | m_r & 0x07;
+    }
+
+    // 00 000 000
+    // same bit-layout as modrm
+    public static int sib(int scale, int index, int base) {
+        return (scale << 6) | ((index & 0x07) << 3) | base & 0x07;
+    }
+
+    // reg1 is reg(R)
+    // reg2 is r/mem(B)
+    // reg3 is X(index)
+    // reg4 is X(base)
+
+    // 0 denotes no direct register
+    public static int rex(int reg, int ptr, int idx, boolean wide) {
+        // assuming 64 bit by default so: 0100 1000
+        int rex = wide ? REX_W : REX;
+        if( 8 <= reg && reg <= 15 ) rex |= 0b00000100; // REX.R
+        if( 8 <= ptr && ptr <= 15 ) rex |= 0b00000001; // REX.B
+        if( 8 <= idx && idx <= 15 ) rex |= 0b00000010; // REX.X
+        return rex;
+    }
+    // return opcode for optimised immediate store
+    public static int selectOpcodeForImmStore(long imm) {
+        if(imm8(imm)) return 0xC6;
+        if(imm16(imm)) return 0xC7;
+        if(imm32(imm)) return 0xC7;
+        return -1;
+    }
+    // return the size of the immediate
+    public static int imm_size(long imm) {
+        if(imm8(imm)) return 8;
+        if(imm16(imm)) return 16;
+        if(imm32(imm)) return 32;
+        return 64;
+    }
+
+    public static int rex(int reg, int ptr, int idx) {
+        return rex(reg, ptr, idx, true);
+    }
+
+    // rex for floats.  Return size (0 or 1)
+    // don't need to use REX if 0x40.
+    public static byte rexF(int reg, int ptr, int idx, boolean wide, Encoding enc) {
+        int rex = rex(reg, ptr, idx, wide);
+        if (rex == REX) return 0;
+        enc.add1(rex);
+        return 1;
+    }
+    // Function used for encoding indirect memory addresses
+    // Does not always generate SIB byte e.g index == -1.
+    // -1 denotes empty value, not set - note 0 is different from -1 as it can represent rax.
+    // Looks for best mod locally
+    public static void indirectAdr( int scale, short index, short base, int offset, int reg, Encoding enc ) {
+        // Assume indirect
+        assert 0 <= base && base < 16;
+        assert index != RSP;
+
+        MOD mod = MOD.INDIRECT;
+        // is 1 byte enough or need more?
+        if( offset != 0 )
+            mod = imm8(offset)
+                ? MOD.INDIRECT_disp8
+                : MOD.INDIRECT_disp32;
+
+        // needs to pick optimal displacement mod if we want to encode base
+        if( mod == MOD.INDIRECT && (base == RBP || base == R13) )
+            mod = MOD.INDIRECT_disp8;
+
+        // special encoding for [base +offset]
+        if( index == -1 ) {
+            // Case for mov reg, [disp] (load)
+            enc.add1(modrm(mod, reg == -1 ? 0 : reg, base));
+        } else {
+            // rsp is hard-coded here(0x04)
+            enc.add1(modrm(mod, reg, 0x04));
+            enc.add1(sib(scale, index, base));
+        }
+
+        if( mod == MOD.INDIRECT_disp8 ) {
+            enc.add1(offset);
+        } else if( mod == MOD.INDIRECT_disp32 ) {
+            enc.add4(offset);
+        }
+    }
+
+    // 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)*8+"]";
+    }
+
+    // Stack slots, in units of 8 bytes.
+    @Override public int stackSlot( int reg ) {
+        return reg < REGS.length ? -1 : reg-REGS.length;
+    }
+
+
+    // WIN64(param passing)
+    static RegMask[] CALLINMASK_WIN64 = new RegMask[] {
+        RCX_MASK,
+        RDX_MASK,
+        R08_MASK,
+        R09_MASK,
+    };
+
+    // SystemV(param passing)
+    static RegMask[] CALLINMASK_SYSTEMV = new RegMask[] {
+        RDI_MASK,
+        RSI_MASK,
+        RDX_MASK,
+        RCX_MASK,
+        R08_MASK,
+        R09_MASK,
+    };
+
+    // Limit of float args passed in registers
+    static RegMask[] XMMS4 = new RegMask[]{
+        new RegMask(XMM0), new RegMask(XMM1), new RegMask(XMM2), new RegMask(XMM3),
+    };
+
+    // Map from function signature and argument index to register.
+    // Used to set input registers to CallNodes, and ParmNode outputs.
+    static RegMask callInMask( SONTypeFunPtr tfp, int idx ) {
+        if( idx==0 ) return RPC_MASK;
+        if( idx==1 ) return null;
+        // Count floats in signature up to index
+        int fcnt=0;
+        for( int i=2; i<idx; i++ )
+            if( tfp.arg(i-2) instanceof SONTypeFloat )
+                fcnt++;
+        // Floats up to XMMS in XMM registers
+        if( tfp.arg(idx-2) instanceof SONTypeFloat ) {
+            if( fcnt < XMMS4.length )
+                return XMMS4[fcnt];
+        } else {
+            RegMask[] cargs = switch( CodeGen.CODE._callingConv ) {
+            case "SystemV" -> CALLINMASK_SYSTEMV;
+            case "Win64"   -> CALLINMASK_WIN64;
+            default        -> throw new IllegalArgumentException("Unknown calling convention: "+CodeGen.CODE._callingConv);
+            };
+            if( idx-2-fcnt < cargs.length )
+                return cargs[idx-2-fcnt];
+        }
+        throw Utils.TODO(); // Pass on stack slot
+    }
+
+    // Return the max stack slot used by this signature, or 0
+    static short maxSlot( SONTypeFunPtr tfp ) {
+        // Count floats in signature up to index
+        int fcnt=0;
+        for( int i=0; i<tfp.nargs(); i++ )
+            if( tfp.arg(i) instanceof SONTypeFloat )
+                fcnt++;
+        if( fcnt >= XMMS4.length )
+            throw Utils.TODO();
+        RegMask[] cargs = switch( CodeGen.CODE._callingConv ) {
+        case "SystemV" -> CALLINMASK_SYSTEMV;
+        case "Win64"   -> CALLINMASK_WIN64;
+        default        -> throw new IllegalArgumentException("Unknown calling convention: "+CodeGen.CODE._callingConv);
+        };
+        if( tfp.nargs()-fcnt >= cargs.length )
+            throw Utils.TODO();
+        return 0;               // No stack args
+    }
+
+    // Return single int/ptr register.  Used by CallEnd output and Return input.
+    static RegMask retMask( SONTypeFunPtr tfp ) {
+        return tfp.ret() instanceof SONTypeFloat ? XMM0_MASK : RAX_MASK;
+    }
+
+
+    // caller saved(systemv)
+    static final long SYSTEM5_CALLER_SAVE =
+        (1L << RAX) | (1L << RCX) | (1L << RDX) |
+        (1L << RDI) | (1L << RSI) |
+        (1L << R08) | (1L << R09) | (1L << R10) | (1L << R11) |
+        (1L << FLAGS) |           // Flags are killed
+        // All FP regs are killed
+        FP_BITS;
+    static final RegMask SYSTEM5_CALLER_SAVE_MASK = new RegMask(SYSTEM5_CALLER_SAVE);
+
+    // caller saved(win64)
+    static final long WIN64_CALLER_SAVE =
+        (1L<< RAX) | (1L<< RCX) | (1L<< RDX) |
+        (1L<< R08) | (1L<< R09) | (1L<< R10) | (1L<< R11) |
+        (1L<<FLAGS)|           // Flags are killed
+        // Only XMM0-XMM5 are killed; XMM6-XMM15 are preserved
+        (1L<<XMM0) | (1L<<XMM1) | (1L<<XMM2) | (1L<<XMM3) |
+        (1L<<XMM4) | (1L<<XMM5);
+    static final RegMask WIN64_CALLER_SAVE_MASK = new RegMask(WIN64_CALLER_SAVE);
+
+    static RegMask x86CallerSave() {
+        return switch (CodeGen.CODE._callingConv) {
+        case "SystemV" -> SYSTEM5_CALLER_SAVE_MASK;
+        case "Win64"   ->   WIN64_CALLER_SAVE_MASK;
+        default -> throw new IllegalArgumentException("Unknown calling convention: " + CodeGen.CODE._callingConv);
+        };
+    }
+
+    @Override public RegMask callerSave() { return x86CallerSave(); }
+
+    static final RegMask SYSTEM5_CALLEE_SAVE_MASK;
+    static final RegMask   WIN64_CALLEE_SAVE_MASK;
+
+    static {
+        long callee = ~SYSTEM5_CALLER_SAVE;
+        // Remove the spills
+        callee &= (1L<<MAX_REG)-1;
+        callee &= ~(1L<<FLAGS);
+        callee &= ~(1L<<RSP);
+        SYSTEM5_CALLEE_SAVE_MASK = new RegMask(callee);
+
+        callee = ~WIN64_CALLER_SAVE;
+        // Remove the spills
+        callee &= (1L<<MAX_REG)-1;
+        callee &= ~(1L<<FLAGS);
+        callee &= ~(1L<<RSP);
+        WIN64_CALLEE_SAVE_MASK = new RegMask(callee);
+    }
+    static RegMask x86CalleeSave() {
+        return switch( CodeGen.CODE._callingConv ) {
+        case "SystemV" -> SYSTEM5_CALLEE_SAVE_MASK;
+        case "Win64"   ->   WIN64_CALLEE_SAVE_MASK;
+        default        -> throw new IllegalArgumentException("Unknown calling convention: "+CodeGen.CODE._callingConv);
+        };
+    }
+    @Override public RegMask calleeSave() { return x86CalleeSave(); }
+
+    static final RegMask[] WIN64_RET_MASKS, SYS5_RET_MASKS;
+    static {
+        WIN64_RET_MASKS = makeRetMasks(  WIN64_CALLEE_SAVE_MASK);
+         SYS5_RET_MASKS = makeRetMasks(SYSTEM5_CALLEE_SAVE_MASK);
+    }
+    private static RegMask[] makeRetMasks(RegMask mask) {
+        int nSaves = mask.size();
+        RegMask[] masks = new RegMask[4 + nSaves];
+        masks[0] = null;
+        masks[1] = null;     // Memory
+        masks[2] = null;     // Varies, either XMM0 or RAX
+        masks[3] = RPC_MASK;
+        short reg = mask.firstReg();
+        for( int i=0; i<nSaves; i++ ) {
+            masks[i+4] = new RegMask(reg);
+            reg = mask.nextReg(reg);
+        }
+        return masks;
+    }
+
+    // Return single int/ptr register.  Used by CallEnd output and Return input.
+    static RegMask retMask( SONTypeFunPtr tfp, int i ) {
+        if( i==2 )
+            return tfp.ret() instanceof SONTypeFloat ? XMM0_MASK : RAX_MASK;
+        RegMask[] masks = switch (CodeGen.CODE._callingConv) {
+        case "SystemV" ->  SYS5_RET_MASKS;
+        case "Win64"   -> WIN64_RET_MASKS;
+        default        -> throw new IllegalArgumentException("Unknown calling convention: "+CodeGen.CODE._callingConv);
+        };
+        return masks[i];
+    }
+
+    // Create a split op; any register to any register, including stack slots
+    @Override public SplitNode split(String kind, byte round, LRG lrg) {
+        return new SplitX86(kind, round);
+    }
+
+    // Return a MachNode unconditional branch
+    @Override public CFGNode jump() {
+        return new UJmpX86();
+    }
+
+    // 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 CastNode cast -> new CastX86(cast);
+            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 NotNode not -> new NotX86(not);
+            case OrNode or -> or(or);
+            case ParmNode parm -> new ParmX86(parm);
+            case PhiNode phi -> new PhiNode(phi);
+            case ProjNode prj -> prj(prj);
+            case ReadOnlyNode read -> new ReadOnlyNode(read);
+            case ReturnNode ret -> new RetX86(ret, ret.fun());
+            case SarNode sar -> sar(sar);
+            case ShlNode shl -> shl(shl);
+            case ShrNode shr -> shr(shr);
+            case StartNode start -> new StartNode(start);
+            case StopNode stop -> new StopNode(stop);
+            case StoreNode st -> st(st);
+            case SubFNode subf -> new SubFX86(subf);
+            case SubNode sub -> sub(sub);
+            case ToFloatNode tfn -> i2f8(tfn);
+            case XorNode xor -> xor(xor);
+
+            case LoopNode loop -> new LoopNode(loop);
+            case RegionNode region -> new RegionNode(region);
+            default -> throw Utils.TODO();
+        };
+    }
+
+    public static boolean imm8( long imm ) { return -128 <= imm && imm <= 127; }
+    public static boolean imm16(long imm) {return -32768 <= imm && imm <= 32767; }
+    public static boolean imm32( long imm ) { return (int)imm==imm; }
+
+    // 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 && ld._declaredType.log_size() >= 3)
+            return new AddMemX86(add, address(ld), ld.ptr(), idx, off, scale, imm(rhs), val);
+
+//        if(rhs instanceof LoadNode ld && ld.nOuts() == 1 && ld._declaredType.log_size() >= 3) {
+//            throw Utils.TODO(); // Swap load sides
+//        }
+
+        // Attempt a full LEA-style break down.
+        // Returns one of AddX86, AddIX86, LeaX86, or LHS
+        if( rhs instanceof ConstantNode off && off._con instanceof SONTypeInteger toff ) {
+            long imm = toff.value();
+            assert imm!=0;        // Folded in peeps
+            if( (int)imm != imm ) // Full 64bit immediate
+                return new AddX86(add);
+            // Now imm <= 32bits
+            if( lhs instanceof AddNode ladd )
+                // ((base + (idx << scale)) + off)
+                return _lea(add, ladd.in(1), ladd.in(2), (int)imm);
+            if( lhs instanceof ShlNode shift )
+                // (idx << scale) + off; no base
+                return _lea(add, null, shift, (int)imm);
+
+            // lhs + rhs1
+            return new AddIX86(add, (int)imm);
+        }
+        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, int off) {
+        int scale = 0;
+        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 SONTypeInteger tscale && 0 <= tscale.value() && tscale.value() <= 3 ) {
+            idx = shift.in(1);
+            scale = ((int) tscale.value());
+        }
+        // (base + idx) + off
+        return off == 0 && scale == 0
+            ? 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 SONTypeInteger ti && imm32(ti.value()) )
+            return new AndIX86(and, (int)ti.value());
+        return new AndX86(and);
+    }
+
+    private Node call(CallNode call) {
+        return call.fptr() instanceof ConstantNode con && con._con instanceof SONTypeFunPtr tfp
+            ? new CallX86(call, tfp)
+            : 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 static boolean invert;
+    private Node cmp( BoolNode bool ) {
+        invert = false;
+        Node cmp = _cmp(bool);
+        return new SetX86(cmp, invert ? IfNode.invert(bool.op()) : bool.op());
+    }
+
+    private Node _cmp(BoolNode bool) {
+        // Float variant
+        if( bool.isFloat() )
+            return new CmpFX86(bool);
+        Node lhs = bool.in(1);
+        Node rhs = bool.in(2);
+
+        // Vs memory
+        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);
+
+        // Vs immediate
+        if( rhs instanceof ConstantNode con && con._con instanceof SONTypeInteger ti && imm32(ti.value()) )
+            return new CmpIX86(bool, (int)ti.value());
+
+        if( lhs instanceof ConstantNode con && con._con instanceof SONTypeInteger ti && imm32(ti.value()) ) {
+            invert = true;
+            return new CmpIX86(bool, (int)ti.value(), 0.5);
+        }
+
+        // x vs y
+        return new CmpX86(bool);
+    }
+
+    private Node con(ConstantNode con) {
+        if(!con._con.isConstant()) return new ConstantNode(con); // Default unknown caller inputs
+        return switch (con._con) {
+            case SONTypeInteger ti -> new IntX86(con);
+            case SONTypeFloat tf -> new FltX86(con);
+            case SONTypeFunPtr tfp -> new TFPX86(con);
+            case SONTypeMemPtr tmp -> throw Utils.TODO();
+            case SONTypeNil tn -> throw Utils.TODO();
+            // TOP, BOTTOM, XCtrl, Ctrl, etc.  Never any executable code.
+            case SONType t -> t == SONType.NIL ? new IntX86(con) : new ConstantNode(con);
+        };
+    }
+
+    private Node i2f8(ToFloatNode tfn) {
+        assert tfn.in(1)._type instanceof SONTypeInteger 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
+        BoolNode bool;
+        if( iff.in(1) instanceof BoolNode bool0 ) bool = bool0;
+        else iff.setDef(1, bool=new BoolNode.EQ(iff.in(1), new ConstantNode(SONTypeInteger.ZERO)));
+        return new JmpX86(iff, bool.op());
+    }
+
+    private Node ld(LoadNode ld) {
+        return new LoadX86(address(ld), ld.ptr(), idx, off, scale);
+    }
+
+    private Node mul(MulNode mul) {
+        if( mul.in(2) instanceof ConstantNode con && con._con instanceof SONTypeInteger ti && imm32(ti.value()) )
+            return new MulIX86(mul, (int)ti.value());
+        return new MulX86(mul);
+    }
+
+    private Node or(OrNode or) {
+        if( or.in(2) instanceof ConstantNode con && con._con instanceof SONTypeInteger ti && imm32(ti.value()) )
+            return new OrIX86(or, (int)ti.value());
+        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 SONTypeInteger ti )
+            return new SarIX86(sar, (int)(ti.value() & 0x03f) );
+        return new SarX86(sar);
+    }
+
+    private Node shl(ShlNode shl) {
+        if( shl.in(2) instanceof ConstantNode con && con._con instanceof SONTypeInteger ti )
+            return new ShlIX86(shl, (int)(ti.value() & 0x03f) );
+        return new ShlX86(shl);
+    }
+
+    private Node shr( ShrNode shr ) {
+        if( shr.in(2) instanceof ConstantNode con && con._con instanceof SONTypeInteger ti )
+            return new ShrIX86(shr, (int)(ti.value() & 0x03f) );
+        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 SONTypeInteger ti
+            ? new AddIX86(sub, (int)-ti.value())
+            : new SubX86(sub);
+    }
+
+    private Node xor (XorNode xor){
+        if( xor.in(2) instanceof ConstantNode con && con._con instanceof SONTypeInteger ti && imm32(ti.value()) )
+            return new XorIX86(xor, (int)ti.value());
+        return new XorX86(xor);
+    }
+
+    // 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 SONTypeMemPtr; // Base ptr always, not some derived
+        if(mop.off() instanceof AddNode add && add.in(2) instanceof ConstantNode con && con._con instanceof SONTypeInteger 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 SONTypeInteger 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 SONTypeInteger 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 SONTypeInteger ti) {
+            val = null;
+            imm = (int) ti.value();
+            assert imm == ti.value(); // In 32-bit range
+        } else {
+            val = xval;
+        }
+        return imm;
+    }
+
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/print/ASMPrinter.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/print/ASMPrinter.java
new file mode 100644
index 0000000..3c6df73
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/print/ASMPrinter.java
@@ -0,0 +1,254 @@
+package com.compilerprogramming.ezlang.compiler.print;
+
+import com.compilerprogramming.ezlang.compiler.codegen.CodeGen;
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.codegen.Encoding;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFunPtr;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeMem;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeRPC;
+
+public abstract class ASMPrinter {
+
+    public static SB print(SB sb, CodeGen code) {
+        if( code._cfg==null )
+            return sb.p("Need _cfg set, run after GCM");
+
+        // instruction address
+        int iadr = 0;
+        for( int i=0; i<code._cfg._len; i++ )
+            if( code._cfg.at(i) instanceof FunNode fun )
+                iadr = print(iadr,sb,code,fun,i);
+
+        // Skip padding
+        while( ((iadr+7) & -8) > iadr )
+            iadr++;
+
+        // constant pool
+        Encoding enc = code._encoding;
+        if(  enc!=null && !enc._bigCons.isEmpty() ) {
+            sb.p("--- Constant Pool ------").nl();
+            for( Node relo : enc._bigCons.keySet() ) {
+                SONType t = enc._bigCons.get(relo);
+                if( t.log_size()==3 ) {
+                    sb.hex2(iadr).p("  ").hex8(enc.read8(iadr)).p(" ");
+                    t.print(sb).nl();
+                    iadr += 8;
+                }
+            }
+            for( Node relo : enc._bigCons.keySet() ) {
+                SONType t = enc._bigCons.get(relo);
+                if( t.log_size()==2 ) {
+                    sb.hex2(iadr).p("  ").hex4(enc.read4(iadr)).fix(9,"");
+                    t.print(sb).nl();
+                    iadr += 4;
+                }
+            }
+        }
+
+
+        return sb;
+    }
+
+    private static int print(int iadr, SB sb, CodeGen code, FunNode fun, int cfgidx) {
+        // Function header
+        sb.nl().p("---");
+        if( fun._name != null ) sb.p(fun._name).p(" ");
+        fun.sig().print(sb);
+        sb.p("---------------------------").nl();
+
+        if( fun._frameAdjust != 0 )
+            iadr = doInst(iadr,sb,code,cfgidx,fun,true,true);
+        while( !(code._cfg.at(cfgidx) instanceof ReturnNode) )
+            iadr = doBlock(iadr,sb,code,fun,cfgidx++);
+
+        // Function separator
+        sb.p("---");
+        fun.sig().print(sb);
+        sb.p("---------------------------").nl();
+        return iadr;
+    }
+
+    static private final int opWidth = 5;
+    static private final int argWidth = 30;
+    static int doBlock(int iadr, SB sb, CodeGen code, FunNode fun, int cfgidx) {
+        final int encWidth = code._mach.defaultOpSize()*2;
+        CFGNode bb = code._cfg.at(cfgidx);
+        if( bb != fun && !(bb instanceof IfNode) && !(bb instanceof CallEndNode) && !(bb instanceof CallNode)  && !(bb instanceof CProjNode && bb.in(0) instanceof CallEndNode ))
+            sb.p(label(bb)).p(":").nl();
+        if( bb instanceof CallNode ) return iadr;
+        final boolean postAlloc = code._phase.ordinal() > CodeGen.Phase.RegAlloc.ordinal();
+        final boolean postEncode= code._phase.ordinal() >=CodeGen.Phase.Encoding.ordinal();
+
+        boolean once=false;
+        for( Node n : bb.outs() ) {
+            if( !(n instanceof PhiNode phi) ) continue;
+            if( phi._type instanceof SONTypeMem || phi._type instanceof SONTypeRPC ) continue; // Nothing for the hidden ones
+            // Post-RegAlloc phi prints all on one line
+            if( postAlloc ) {
+                if( !once ) { once=true; sb.fix(4," ").p(" ").fix(encWidth,"").p("  "); }
+                sb.p(phi._label).p(':').p(code.reg(phi)).p(',');
+            } else {
+                // Pre-RegAlloc phi prints one line per
+                sb.fix(4," ").p(" ").fix(encWidth,"").p("  ").fix(opWidth,phi._label).p(" ").p(code.reg(phi));
+                if( phi.getClass() == PhiNode.class ) {
+                    sb.p(" = phi( ");
+                    for( int i=1; i<phi.nIns(); i++ )
+                        sb.p("N").p(phi.in(i)._nid).p(",");
+                    sb.unchar().p(" )");
+                }
+                sb.nl();
+            }
+        }
+        if( once ) sb.unchar().nl();
+
+        // All the non-phis
+        for( int i=0; i<bb.nOuts(); i++ )
+            if( !(bb.out(i) instanceof PhiNode) )
+                iadr = doInst(iadr, sb,code, cfgidx, bb.out(i),postAlloc, postEncode );
+
+        return iadr;
+    }
+
+    static int doInst( int iadr, SB sb, CodeGen code, int cfgidx, Node n, boolean postAlloc, boolean postEncode ) {
+        if( n instanceof CProjNode ) return iadr;
+        if( postAlloc && n instanceof CalleeSaveNode ) return iadr;
+        if( postEncode && n instanceof ProjNode ) return iadr;
+        if( n instanceof MemMergeNode ) return iadr;
+        final int dopz = code._mach.defaultOpSize();
+        final int encWidth = dopz*2;
+
+        // All blocks ending in a Region will need to either fall into or jump
+        // to this block.  Until the post-reg-alloc block layout cleanup, we
+        // need to assume a jump.  There's no real hardware op here, yet.
+        if( n instanceof RegionNode cfg && !(n instanceof FunNode) ) {
+            if( postEncode ) return iadr; // All jumps inserted already
+            while( cfgidx < code._cfg._len-1 ) {
+                CFGNode next = code._cfg.at(++cfgidx);
+                if( next == n ) return iadr; // Fall-through, no branch
+                if( next.nOuts()>1 )
+                    break;      // Has code in the block, need to jump around
+                // No code in the block, can fall through it
+            }
+            sb.hex2(iadr++).p(" ").fix(encWidth,"??").p("  ").fix(opWidth,"JMP").p(" ").fix(argWidth,label(cfg)).nl();
+            return iadr;
+        }
+
+        // ProjNodes following a multi (e.g. Call or New results),
+        // get indent slightly and just print their index & node#
+        if( n instanceof ProjNode proj ) {
+            if( proj._type instanceof SONTypeMem ) return iadr; // Nothing for the hidden ones
+            sb.fix(4," ").p(" ").fix(encWidth,"").p("    ").fix(opWidth,proj._label==null ? "---" : proj._label).p(" ").p(code.reg(n)).nl();
+            return iadr;
+        }
+
+        // ADDR ENCODING  Op--- dst = src op src       // Comment
+        // 1234 abcdefgh  ld4   RAX = [Rbase + off]    // Comment
+        sb.hex2(iadr).p(" ");
+
+        // Encoding
+        int fatEncoding = 0;
+        if( code._encoding != null ) {
+            int size = code._encoding._opLen[n._nid];
+            for( int i=0; i<Math.min(size,encWidth>>1); i++ )
+                sb.hex1(code._encoding._bits.buf()[iadr++]);
+            for( int i=size*2; i<encWidth; i++ )
+                sb.p(" ");
+            fatEncoding = size - (encWidth>>1); // Not-printed parts of encoding
+        } else
+            sb.fix(encWidth,"");
+        sb.p("  ");
+
+        // Op; generally "ld4" or "call"
+        sb.fix(opWidth, n instanceof MachNode mach ? mach.op() : n.label()).p(" ");
+
+        // General asm args
+        String isMultiOp = null;
+        if( n instanceof MachNode mach ) {
+            int old = sb.len();
+            mach.asm(code,sb);
+            int len = sb.len();
+            isMultiOp = isMultiOp(sb,old,len);
+            sb.fix(argWidth-(len-old),""); // Pad out
+
+        } else if( !(n._type instanceof SONTypeMem) ) {
+            // Room for some inputs
+            sb.fix(5, n._nid+":" );
+            int off = 0;
+            for( int i=0; i<n.nIns(); i++ ) {
+                sb.fix(5, n.in(i) == null ? "___" : String.valueOf(n.in(i)._nid));
+                off += 5;
+                if( off >= argWidth ) break;
+            }
+            if( off < argWidth ) sb.fix(argWidth-off,"");
+        }
+        sb.p(" ");
+
+        // Comment
+        String comment = n.comment();
+        if( comment!=null ) sb.p("// ").p(comment);
+
+        sb.nl();
+
+        // Printing more op bits than fit
+        if( isMultiOp != null && code._encoding != null ) {
+            // Multiple ops, template style, no RA, no scheduling.  Print out
+            // one-line-per-newline, with encoding bits up front.
+            int size = code._encoding._opLen[n._nid];
+            int off = Math.min(size,dopz);
+            while( isMultiOp!=null ) {
+                sb.hex2(iadr).p(" ");
+                int len = Math.min(size-off,dopz);
+                for( int i=0; i<len; i++ )
+                    sb.hex1(code._encoding._bits.buf()[iadr++]);
+                off += len;
+                for( int i=size-off; i<dopz; i++ ) sb.p("  ");
+                sb.p("  ");
+                int x = isMultiOp.indexOf('\n');
+                if( x== -1 ) {  // Last line
+                    sb.p(isMultiOp).nl();
+                    isMultiOp = null;
+                } else {
+                    sb.p(isMultiOp.substring(0,x+1)); // Includes the newline
+                    isMultiOp = isMultiOp.substring(x+1);
+                }
+
+            }
+
+        } else if( fatEncoding > 0 ) {
+            // Extra bytes past the default encoding width, all put on a line by
+            // themselves.  X86 special for super long encodings
+            sb.hex2(iadr).p(" ");
+            for( int i=0; i<fatEncoding; i++ )
+                sb.hex1(code._encoding._bits.buf()[iadr++]);
+            sb.nl();
+        }
+
+
+        // MultiNodes are immediately followed by projection(s)
+        if( !(n instanceof CFGNode) && n instanceof MultiNode ) {
+            for( Node proj : n._outputs ) {
+                assert proj instanceof ProjNode;
+                doInst(iadr,sb,code, cfgidx, proj,postAlloc,postEncode);
+            }
+        }
+
+        return iadr;
+    }
+
+    private static String label( CFGNode bb ) {
+        return (bb instanceof LoopNode ? "LOOP" : "L")+bb._nid;
+    }
+
+    private static String isMultiOp(SB sb, int old, int len) {
+        for( int i=old; i<len; i++ )
+            if( sb.at(i)=='\n' ) {
+                String s = sb.subString(i+1,len);
+                sb.setLen(i);
+                return s;
+            }
+        return null;
+    }
+
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/print/GraphVisualizer.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/print/GraphVisualizer.java
new file mode 100644
index 0000000..325dfd1
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/print/GraphVisualizer.java
@@ -0,0 +1,287 @@
+package com.compilerprogramming.ezlang.compiler.print;
+
+import com.compilerprogramming.ezlang.compiler.Compiler;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+import java.util.*;
+import static com.compilerprogramming.ezlang.compiler.Utils.TODO;
+
+/**
+ * Simple visualizer that outputs GraphViz dot format.
+ * The dot output must be saved to a file and run manually via dot to generate the SVG output.
+ * Currently, this is done manually.
+ */
+public class GraphVisualizer {
+
+    /**
+     * If set to true we put the control nodes in a separate cluster from
+     * data nodes.
+     */
+    boolean _separateControlCluster = false;
+
+    public GraphVisualizer(boolean separateControlCluster) { this._separateControlCluster = separateControlCluster; }
+    public GraphVisualizer() { this(false); }
+
+    public String generateDotOutput(Compiler parse) { return generateDotOutput(parse._code._stop,parse._scope,parse._xScopes); }
+    public String generateDotOutput(StopNode stop, Node scope, Stack<ScopeNode> xScopes) {
+
+        // Since the graph has cycles, we need to create a flat list of all the
+        // nodes in the graph.
+        Collection<Node> all = findAll(xScopes, stop, scope);
+        StringBuilder sb = new StringBuilder();
+        sb.append("digraph chapter18 {\n");
+        sb.append("/*\n");
+        sb.append(stop._src);
+        sb.append("\n*/\n");
+
+        // To keep the Scopes below the graph and pointing up into the graph we
+        // need to group the Nodes in a subgraph cluster, and the scopes into a
+        // different subgraph cluster.  THEN we can draw edges between the
+        // scopes and nodes.  If we try to cross subgraph cluster borders while
+        // still making the subgraphs DOT gets confused.
+        sb.append("\trankdir=BT;\n"); // Force Nodes before Scopes
+
+        // CNC - turned off Apr/8/2024, gives more flex in the layout and
+        // removes some of the more ludicrous layout choices.
+        // Preserve node input order
+        //sb.append("\tordering=\"in\";\n");
+
+        // Merge multiple edges hitting the same node.  Makes common shared
+        // nodes much prettier to look at.
+        sb.append("\tconcentrate=\"true\";\n");
+
+        // Force nested scopes to order
+        sb.append("\tcompound=\"true\";\n");
+
+        // Just the Nodes first, in a cluster no edges
+        nodes(sb, all);
+
+        // Now the scopes, in a cluster no edges
+        if( xScopes != null )
+            for( ScopeNode xscope : xScopes )
+                if( !xscope.isDead() )
+                    scope( sb, xscope );
+
+        // Walk the Node edges
+        nodeEdges(sb, all);
+
+        // Walk the active Scope edges
+        if( xScopes != null )
+            for( ScopeNode xscope : xScopes )
+                if( !xscope.isDead() )
+                    scopeEdges( sb, xscope );
+
+        sb.append("}\n");
+        return sb.toString();
+    }
+
+    private void nodesByCluster(StringBuilder sb, boolean doCtrl, Collection<Node> all) {
+        if (!_separateControlCluster && doCtrl) // all nodes in 1 cluster
+            return;
+        // Just the Nodes first, in a cluster no edges
+        sb.append(doCtrl ? "\tsubgraph cluster_Controls {\n" : "\tsubgraph cluster_Nodes {\n"); // Magic "cluster_" in the subgraph name
+        for (Node n : all) {
+            if (n instanceof ProjNode || n instanceof CProjNode || n instanceof MemMergeNode || n==Compiler.XCTRL)
+                continue; // Do not emit, rolled into MultiNode or Scope cluster already
+            if( _separateControlCluster &&  doCtrl && !(n instanceof CFGNode) ) continue;
+            if( _separateControlCluster && !doCtrl &&  (n instanceof CFGNode) ) continue;
+            sb.append("\t\t").append(n.uniqueName()).append(" [ ");
+            String lab = n.glabel();
+            if (n instanceof MultiNode) {
+                // Make a box with the MultiNode on top, and all the projections on the bottom
+                sb.append("shape=plaintext label=<\n");
+                sb.append("\t\t\t<TABLE BORDER=\"0\" CELLBORDER=\"1\" CELLSPACING=\"0\" CELLPADDING=\"4\">\n");
+                sb.append("\t\t\t<TR><TD BGCOLOR=\"yellow\">").append(lab).append("</TD></TR>\n");
+                sb.append("\t\t\t<TR>");
+                boolean doProjTable = false;
+                n._outputs.sort((x,y) -> x instanceof ProjNode xp && y instanceof ProjNode yp ? (xp._idx - yp._idx) : (x._nid - y._nid));
+                for (Node use : n._outputs) {
+                    if (use instanceof ProjNode proj) {
+                        if (!doProjTable) {
+                            doProjTable = true;
+                            sb.append("<TD>").append("\n");
+                            sb.append("\t\t\t\t<TABLE BORDER=\"0\" CELLBORDER=\"1\" CELLSPACING=\"0\">").append("\n");
+                            sb.append("\t\t\t\t<TR>");
+                        }
+                        sb.append("<TD PORT=\"p").append(proj._idx).append("\">").append(proj.glabel()).append("</TD>");
+                    }
+                    if (use instanceof CProjNode proj) {
+                        if (!doProjTable) {
+                            doProjTable = true;
+                            sb.append("<TD>").append("\n");
+                            sb.append("\t\t\t\t<TABLE BORDER=\"0\" CELLBORDER=\"1\" CELLSPACING=\"0\">").append("\n");
+                            sb.append("\t\t\t\t<TR>");
+                        }
+                        sb.append("<TD PORT=\"p").append(proj._idx).append("\" BGCOLOR=\"yellow\">").append(proj.glabel()).append("</TD>");
+                    }
+                }
+                if (doProjTable) {
+                    sb.append("</TR>").append("\n");
+                    sb.append("\t\t\t\t</TABLE>").append("\n");
+                    sb.append("\t\t\t</TD>");
+                }
+                sb.append("</TR>\n");
+                sb.append("\t\t\t</TABLE>>\n\t\t");
+
+            } else {
+                // control nodes have box shape
+                // other nodes are ellipses, i.e. default shape
+                if( n instanceof CFGNode ) sb.append("shape=box style=filled fillcolor=yellow ");
+                else if (n instanceof PhiNode) sb.append("style=filled fillcolor=lightyellow ");
+                sb.append("label=\"").append(lab).append("\" ");
+            }
+            sb.append("];\n");
+        }
+        if (!_separateControlCluster) {
+            // Force Region & Phis to line up
+            for (Node n : all) {
+                if (n instanceof RegionNode region) {
+                    sb.append("\t\t{ rank=same; ");
+                    sb.append(region.uniqueName()).append(";");
+                    for (Node phi : region._outputs)
+                        if (phi instanceof PhiNode) sb.append(phi.uniqueName()).append(";");
+                    sb.append("}\n");
+                }
+            }
+        }
+        sb.append("\t}\n");     // End Node cluster
+    }
+
+    private void nodes(StringBuilder sb, Collection<Node> all) {
+        nodesByCluster(sb, true, all);
+        nodesByCluster(sb, false, all);
+    }
+
+    // Build a nested scope display, walking the _prev edge
+    private void scope( StringBuilder sb, ScopeNode scope ) {
+        sb.append("\tnode [shape=plaintext];\n");
+        int last = scope.nIns();
+        int max = scope._lexSize.size();
+        for( int i = 0; i < max; i++ ) {
+            int level = max-i-1;
+            String scopeName = makeScopeName(scope, level);
+            sb.append("\tsubgraph cluster_").append(scopeName).append(" {\n"); // Magic "cluster_" in the subgraph name
+            // Special for memory ScopeMinNode
+            if( level==0 ) {
+                MemMergeNode n = scope.mem();
+                sb.append("\t\t").append(n.uniqueName()).append(" [label=\"").append(n.glabel()).append("\"];\n");
+            }
+            sb.append("\t\t").append(scopeName).append(" [label=<\n");
+            sb.append("\t\t\t<TABLE BORDER=\"0\" CELLBORDER=\"1\" CELLSPACING=\"0\">\n");
+            // Add the scope level
+            sb.append("\t\t\t<TR><TD BGCOLOR=\"cyan\">").append(level).append("</TD>");
+            int lexStart=scope._lexSize.at(level);
+            for( int j=lexStart; j<last; j++ ) {
+                var v = scope._vars.at(j);
+                sb.append("<TD PORT=\"").append(makePortName(scopeName, v._name)).append("\">").append(v._name).append("</TD>");
+            }
+            last = lexStart;
+            sb.append("</TR>\n");
+            sb.append("\t\t\t</TABLE>>];\n");
+        }
+        // Scope clusters nest, so the graphics shows the nested scopes, so
+        // they are not closed as they are printed; so they just keep nesting.
+        // We close them all at once here.
+        sb.append( "\t}\n".repeat( max ) ); // End all Scope clusters
+    }
+
+    private String makeScopeName(ScopeNode sn, int level) { return sn.uniqueName() + "_" + level; }
+    private String makePortName(String scopeName, String varName) { return scopeName + "_" + varName; }
+
+    // Walk the node edges
+    private void nodeEdges(StringBuilder sb, Collection<Node> all) {
+        // All them edge labels
+        sb.append("\tedge [ fontname=Helvetica, fontsize=8 ];\n");
+        for( Node n : all ) {
+            // Do not display the Constant->Start edge;
+            // ProjNodes handled by Multi;
+            // ScopeNodes are done separately
+            if( n instanceof ConstantNode || n instanceof ProjNode || n instanceof CProjNode || n instanceof ScopeNode || n==Compiler.XCTRL )
+                continue;
+            for( int i=0; i<n.nIns(); i++ ) {
+                Node def = n.in(i);
+                if( n instanceof PhiNode && def instanceof RegionNode ) {
+                    // Draw a dotted use->def edge from Phi to Region.
+                    sb.append('\t').append(n.uniqueName());
+                    sb.append(" -> ");
+                    sb.append(def.uniqueName());
+                    sb.append(" [style=dotted taillabel=").append(i).append("];\n");
+                } else if( def != null ) {
+                    // Most edges land here use->def
+                    sb.append('\t').append(n.uniqueName()).append(" -> ");
+                    if( def instanceof CProjNode proj ) {
+                        String mname = proj.ctrl().uniqueName();
+                        sb.append(mname).append(":p").append(proj._idx);
+                    } else if( def instanceof ProjNode proj ) {
+                        String mname = proj.in(0).uniqueName();
+                        sb.append(mname).append(":p").append(proj._idx);
+                    } else sb.append(def.uniqueName());
+                    // Number edges, so we can see how they track
+                    sb.append("[taillabel=").append(i);
+                    // The edge from New to ctrl is just for anchoring the New
+                    if ( n instanceof NewNode )
+                        sb.append(" color=green");
+                    // control edges are colored red
+                    else if( def instanceof CFGNode )
+                        sb.append(" color=red");
+                    else if( def.isMem() )
+                        sb.append(" color=blue");
+                    // Backedges do not add a ranking constraint
+                    if( i==2 && (n instanceof PhiNode || n instanceof LoopNode) )
+                        sb.append(" constraint=false");
+                    sb.append("];\n");
+                }
+            }
+        }
+    }
+
+    // Walk the scope edges
+    private void scopeEdges( StringBuilder sb, ScopeNode scope ) {
+        sb.append("\tedge [style=dashed color=cornflowerblue];\n");
+        int level=0;
+        for( var v : scope._vars ) {
+            Node def = scope.in(v._idx);
+            while( def instanceof ScopeNode lazy )
+                def = lazy.in(v._idx);
+            if( def==null ) continue;
+            while( level < scope._lexSize.size() && v._idx >= scope._lexSize.at(level) )
+                level++;
+            String scopeName = makeScopeName(scope, level-1);
+            sb.append("\t")
+                .append(scopeName).append(":")
+                .append('"').append(makePortName(scopeName, v._name)).append('"') // wrap port name with quotes because $ctrl is not valid unquoted
+                .append(" -> ");
+            if( def instanceof CProjNode proj ) {
+                sb.append(def.in(0).uniqueName()).append(":p").append(proj._idx);
+            } else if( def instanceof ProjNode proj ) {
+                sb.append(def.in(0).uniqueName()).append(":p").append(proj._idx);
+            } else sb.append(def.uniqueName());
+            sb.append(";\n");
+        }
+    }
+
+    /**
+     * Finds all nodes in the graph.
+     */
+    public static Collection<Node> findAll(Stack<ScopeNode> ss, Node... ns) {
+        final HashMap<Integer, Node> all = new HashMap<>();
+        for( Node n : ns )
+            walk(all, n);
+        if( ss != null )
+            for( Node n : ss )
+                walk(all, n);
+        return all.values();
+    }
+
+    /**
+     * Walk a subgraph and populate distinct nodes in the all list.
+     */
+    private static void walk(HashMap<Integer,Node> all, Node n) {
+        if(n == null ) return;
+        if (all.get(n._nid) != null) return; // Been there, done that
+        all.put(n._nid, n);
+        for (Node c : n._inputs)
+            walk(all, c);
+        for( Node c : n._outputs )
+            walk(all, c);
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/print/IRPrinter.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/print/IRPrinter.java
new file mode 100644
index 0000000..a69581d
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/print/IRPrinter.java
@@ -0,0 +1,327 @@
+package com.compilerprogramming.ezlang.compiler.print;
+
+import com.compilerprogramming.ezlang.compiler.Ary;
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.Utils;
+import com.compilerprogramming.ezlang.compiler.codegen.CodeGen;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.HashMap;
+
+public abstract class IRPrinter {
+
+    // Print a node on 1 line, columnar aligned, as:
+    // NNID NNAME DDEF DDEF  [[  UUSE UUSE  ]]  TYPE
+    // 1234 sssss 1234 1234 1234 1234 1234 1234 tttttt
+    public static SB printLine( Node n, SB sb ) {
+        sb.p("%4d %-7.7s ".formatted(n._nid,n.label()));
+        if( n._inputs==null )
+            return sb.p("DEAD\n");
+        for( Node def : n._inputs )
+            sb.p(def==null ? "____" : "%4d".formatted(def._nid))
+                // Lazy Phi indicator
+                .p(n instanceof MemMergeNode && def instanceof MemMergeNode ? "^" : " ");
+        for( int i = n._inputs.size(); i<4; i++ )
+            sb.p("     ");
+        sb.p(" [[  ");
+        for( Node use : n._outputs )
+            sb.p(use==null ? "____ " : "%4d ".formatted(use._nid));
+        int lim = 6 - Math.max(n._inputs.size(),4);
+        for( int i = n._outputs.size(); i<lim; i++ )
+            sb.p("     ");
+        sb.p(" ]]  ");
+        if( n._type!= null ) sb.p(n._type.str());
+        return sb.p("\n");
+    }
+
+
+    public static String prettyPrint(Node node, int depth) {
+        return CodeGen.CODE._phase.ordinal() > CodeGen.Phase.Schedule.ordinal()
+            ? _prettyPrintScheduled( node, depth )
+            : _prettyPrint( node, depth );
+    }
+
+    // Another bulk pretty-printer.  Makes more effort at basic-block grouping.
+    private static String _prettyPrint( Node node, int depth ) {
+        // First, a Breadth First Search at a fixed depth.
+        BFS bfs = new BFS(node,depth);
+        // Convert just that set to a post-order
+        ArrayList<Node> rpos = new ArrayList<>();
+        BitSet visit = new BitSet();
+        for( int i=bfs._lim; i< bfs._bfs.size(); i++ )
+            postOrd( bfs._bfs.get(i), null, rpos, visit, bfs._bs);
+        // Reverse the post-order walk
+        SB sb = new SB();
+        boolean gap=false;
+        for( int i=rpos.size()-1; i>=0; i-- ) {
+            Node n = rpos.get(i);
+            if( n instanceof CFGNode || n instanceof MultiNode ) {
+                if( !gap ) sb.p("\n"); // Blank before multihead
+                if( n instanceof FunNode fun )
+                    fun.sig().print(sb.p("--- ").p(fun._name==null ? "" : fun._name).p(" "),false).p("----------------------\n");
+                printLine( n, sb );         // Print head
+                while( --i >= 0 ) {
+                    Node t = rpos.get(i);
+                    if( !(t.in(0) instanceof MultiNode) ) { i++; break; }
+                    printLine( t, sb );
+                }
+                if( n instanceof ReturnNode ret ) {
+                    FunNode fun = ret.fun();
+                    sb.p("--- ");
+                    if( fun != null )
+                        fun.sig().print(sb.p(fun._name==null ? "" : fun._name).p(" "),false);
+                    sb.p("----------------------\n");
+                }
+                if( !(n instanceof CallNode) ) {
+                    sb.p("\n"); // Blank after multitail
+                    gap = true;
+                }
+            } else {
+                printLine( n, sb );
+                gap = false;
+            }
+        }
+        return sb.toString();
+    }
+
+    private static void postOrd(Node n, Node prior, ArrayList<Node> rpos, BitSet visit, BitSet bfs) {
+        if( !bfs.get(n._nid) )
+            return;  // Not in the BFS visit
+        if( n instanceof FunNode && !(prior instanceof StartNode) )
+            return;                     // Only visit Fun from Start
+        if( visit.get(n._nid) ) return; // Already post-order walked
+        visit.set(n._nid);
+        // First walk the CFG, then everything
+        if( n instanceof CFGNode ) {
+            for( Node use : n._outputs )
+                // Follow CFG, not across call/function borders, and not around backedges
+                if( use instanceof CFGNode && !(n instanceof CallNode && use instanceof FunNode) &&
+                    use.nOuts() >= 1 &&  !(use._outputs.get(0) instanceof LoopNode) )
+                    postOrd(use, n, rpos,visit,bfs);
+            for( Node use : n._outputs )
+                // Follow CFG, not across call/function borders
+                if( use instanceof CFGNode && !(n instanceof CallNode && use instanceof FunNode) )
+                    postOrd(use,n,rpos,visit,bfs);
+        }
+        // Follow all outputs
+        for( Node use : n._outputs )
+            if( use != null &&
+                !(n instanceof CallNode && use instanceof FunNode) &&
+                (n instanceof FunNode || !(use instanceof ParmNode)) )
+                postOrd(use, n, rpos,visit,bfs);
+        // Post-order
+        rpos.add(n);
+    }
+
+    // Breadth-first search, broken out in a class to keep in more independent.
+    // Maintains a root-set of Nodes at the limit (or past by 1 if MultiHead).
+    public static class BFS {
+        // A breadth first search, plus MultiHeads for any MultiTails
+        public final ArrayList<Node> _bfs;
+        public final BitSet _bs; // Visited members by node id
+        public final int _depth; // Depth limit
+        public final int _lim; // From here to _bfs._len can be roots for a reverse search
+        public BFS( Node base, int d ) {
+            _depth = d;
+            _bfs = new ArrayList<>();
+            _bs = new BitSet();
+
+            add(base);                 // Prime the pump
+            int idx=0, lim=1;          // Limit is where depth counter changes
+            while( idx < _bfs.size() ) { // Ran out of nodes below depth
+                Node n = _bfs.get(idx++);
+                for( Node def : n._inputs )
+                    if( def!=null && !_bs.get(def._nid) )
+                        add(def);
+                if( idx==lim ) {    // Depth counter changes at limit
+                    if( --d < 0 )
+                        break;      // Ran out of depth
+                    lim = _bfs.size();  // New depth limit
+                }
+            }
+            // Toss things past the limit except multi-heads
+            while( idx < _bfs.size() ) {
+                Node n = _bfs.get(idx);
+                if( n instanceof MultiNode ) idx++;
+                else del(idx);
+            }
+            // Root set is any node with no inputs in the visited set
+            lim = _bfs.size();
+            for( int i=_bfs.size()-1; i>=0; i-- )
+                if( !any_visited(_bfs.get(i)) )
+                    swap( i,--lim);
+            _lim = lim;
+        }
+        void swap( int x, int y ) {
+            if( x==y ) return;
+            Node tx = _bfs.get(x);
+            Node ty = _bfs.get(y);
+            _bfs.set(x,ty);
+            _bfs.set(y,tx);
+        }
+        void add(Node n) {
+            _bfs.add(n);
+            _bs.set(n._nid);
+        }
+        void del(int idx) {
+            _bs.clear(_bfs.get(idx)._nid);
+            Utils.del(_bfs, idx);
+        }
+        boolean any_visited( Node n ) {
+            for( Node def : n._inputs )
+                if( def!=null && _bs.get(def._nid) )
+                    return true;
+            return false;
+        }
+    }
+
+    public static String _prettyPrint( CodeGen code ) {
+        SB sb = new SB();
+        // Print the Start "block"
+        printLine(code._start,sb);
+        for( Node n : code._start._outputs )
+            printLine(n,sb);
+        sb.nl();
+
+        // Skip start, stop
+        for( int i=1; i<code._cfg._len-1; i++ ) {
+            CFGNode blk = code._cfg.at(i);
+            if( blk.blockHead() && (!(blk instanceof CProjNode) || (blk.cfg0() instanceof IfNode )) ) {
+                if( blk instanceof FunNode fun )
+                    sb.p("--- ").p(fun.label()).p(" ---------------------------").nl();
+                // Print block header
+                sb.p("%-13.13s".formatted(label(blk)+":"));
+                sb.p( "     ".repeat(4) ).p(" [[  ");
+                if( blk instanceof RegionNode )
+                    for( int j=1; j<blk.nIns(); j++ )
+                        label(sb,blk.cfg(j));
+                else
+                    label(sb,blk.cfg(0));
+                sb.p(" ]]  \n");
+            }
+            printLine(blk,sb);
+            if( blk instanceof ReturnNode ret )
+                sb.p("--- ").p(ret.fun().label()).p(" ---------------------------").nl();
+
+            // Block contents
+            for( Node n : blk._outputs ) {
+                if( n instanceof CFGNode cfg ) continue;
+                printLine(n,sb);
+                if( !(n instanceof CFGNode) && n instanceof MultiNode )
+                    for( Node use : n._outputs )
+                        printLine(use,sb);
+            }
+        }
+
+        printLine(code._stop,sb);
+        return sb.toString();
+    }
+
+    // Bulk pretty printer, knowing scheduling information is available
+    private static String _prettyPrintScheduled( Node node, int depth ) {
+        // Backwards DFS walk to depth.
+        HashMap<Integer,Integer> ds = new HashMap<>();
+        Ary<Node> ns = new Ary<>(Node.class);
+        _walk(ds,ns,node,depth);
+        // Remove data projections, these are force-printed behind their multinode head
+        for( int i=0; i<ns.size(); i++ ) {
+            if( ns.get(i) instanceof ProjNode proj && !(proj.in(0) instanceof CFGNode) ) {
+                ns.del(i--);
+                ds.remove(proj._nid);
+            }
+        }
+        // Print by block with least idepth
+        SB sb = new SB();
+        Ary<Node> bns = new Ary<>(Node.class);
+        while( !ds.isEmpty() ) {
+            CFGNode blk = null;
+            for( Node n : ns ) {
+                CFGNode cfg = n instanceof CFGNode cfg0 && cfg0.blockHead() ? cfg0 : n.cfg0();
+                if( blk==null || cfg.idepth() < blk.idepth() )
+                    blk = cfg;
+            }
+            Integer d = ds.remove(blk._nid);
+            ns.del(ns.find(blk));
+
+            // Print block header
+            sb.p("%-13.13s".formatted(label(blk)+":"));
+            sb.p( "     ".repeat(4) ).p(" [[  ");
+            if( blk instanceof StartNode ) ;
+            else if( blk instanceof RegionNode || blk instanceof StopNode )
+                for( int i=(blk instanceof StopNode ? 3 : 1); i<blk.nIns(); i++ )
+                    label(sb,blk.cfg(i));
+            else
+                label(sb,blk.cfg(0));
+            sb.p(" ]]  \n");
+            printLine(blk,sb);
+
+            // Collect block contents that are in the depth limit
+            bns.clear();
+            int xd = Integer.MAX_VALUE;
+            for( Node use : blk._outputs ) {
+                Integer i = ds.get(use._nid);
+                if( i!=null && !(use instanceof CFGNode cfg && cfg.blockHead()) ) {
+                    if( bns.find(use)==-1 )
+                        bns.add(use);
+                    xd = Math.min(xd,i);
+                }
+            }
+            // Print Phis up front, if any
+            for( int i=0; i<bns.size(); i++ )
+                if( bns.get(i) instanceof PhiNode phi )
+                    printLine( phi, sb,bns,i--,ds,ns);
+
+            // Print block contents in depth order, bumping depth until whole block printed
+            for( ; !bns.isEmpty(); xd++ )
+                for( int i=0; i<bns.size(); i++ ) {
+                    Node n = bns.get(i);
+                    if( ds.get(n._nid)==xd ) {
+                        printLine( n, sb, bns, i--, ds,ns );
+                        if( n instanceof MultiNode && !(n instanceof CFGNode) ) {
+                            for( Node use : n._outputs ) {
+                                printLine(use,sb,bns,bns.indexOf(use),ds,ns);
+                            }
+                        }
+                    }
+                }
+            sb.p("\n");
+        }
+        return sb.toString();
+    }
+
+    private static void _walk( HashMap<Integer,Integer> ds, Ary<Node> ns, Node node, int d ) {
+        Integer nd = ds.get(node._nid);
+        if( nd!=null && d <= nd ) return; // Been there, done that
+        Integer old = ds.put(node._nid,d) ;
+        if( old == null )
+          ns.add(node);
+        if( d == 0 ) return;    // Depth cutoff
+        for( Node def : node._inputs )
+            if( def != null &&
+                !(node instanceof LoopNode loop && loop.back()==def) &&
+                // Don't walk into or out of functions
+                !(node instanceof CallEndNode && def instanceof ReturnNode) &&
+                !(node instanceof FunNode && def instanceof CallNode) &&
+                !(node instanceof ParmNode && !(def instanceof FunNode))
+            )
+                _walk(ds,ns,def,d-1);
+    }
+
+    static String label( CFGNode blk ) {
+        if( blk instanceof StartNode ) return "START";
+        return (blk instanceof LoopNode ? "LOOP" : "L")+blk._nid;
+    }
+    static void label( SB sb, CFGNode blk ) {
+        if( !blk.blockHead() ) blk = blk.cfg(0);
+        sb.p( "%-9.9s ".formatted( label( blk ) ) );
+    }
+    static void printLine( Node n, SB sb, Ary<Node> bns, int i, HashMap<Integer,Integer> ds, Ary<Node> ns ) {
+        printLine( n, sb );
+        if( i != -1 ) bns.del(i);
+        ds.remove(n._nid);
+        int idx = ns.find(n);
+        if( idx!=-1 ) ns.del(idx);
+    }
+
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/Field.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/Field.java
new file mode 100644
index 0000000..d4cf783
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/Field.java
@@ -0,0 +1,74 @@
+package com.compilerprogramming.ezlang.compiler.sontypes;
+
+import com.compilerprogramming.ezlang.compiler.SB;
+
+import java.util.ArrayList;
+
+/**
+ * Represents a field in a struct. This is not a Type in the type system.
+ */
+public class Field extends SONType {
+
+    // The pair {fieldName,type} uniquely identifies a field.
+
+    // Field name
+    public final String _fname;
+    // Type of the field
+    public final SONType _type;
+    // Unique memory alias, not sensibly part of a "type" but very convenient here.
+    public final int _alias;
+    // Field must be written to exactly once, no more, no less
+    public final boolean _final;
+
+    public Field(String fname, SONType type, int alias, boolean xfinal ) {
+        super(TFLD);
+        _fname = fname;
+        _type  = type;
+        _alias = alias;
+        _final = xfinal;
+    }
+    // Make with existing alias
+    public static Field make(String fname, SONType type, int alias, boolean xfinal ) {
+        return new Field(fname,type,alias,xfinal).intern();
+    }
+    public Field makeFrom( SONType type ) {
+        return type == _type ? this : new Field(_fname,type,_alias,_final).intern();
+    }
+    @Override public Field makeRO() { return _final ? this : make(_fname,_type.makeRO(),_alias,true);  }
+    @Override public boolean isFinal() { return _final && _type.isFinal(); }
+
+    public static final Field TEST = make("test", SONType.NIL,-2,false);
+    public static final Field TEST2= make("test", SONType.NIL,-2,true);
+    public static void gather(ArrayList<SONType> ts) { ts.add(TEST); ts.add(TEST2); }
+
+    @Override Field xmeet( SONType that ) {
+        Field fld = (Field)that; // Invariant
+        assert _fname.equals(fld._fname);
+        assert _alias==fld._alias;
+        return make(_fname,_type.meet(fld._type),_alias,_final | fld._final);
+    }
+
+    @Override
+    public Field dual() { return make(_fname,_type.dual(),_alias,!_final); }
+
+    @Override public Field glb() {
+        SONType glb = _type.glb();
+        return (glb==_type && _final) ? this : make(_fname,glb,_alias,true);
+    }
+
+    // Override in subclasses
+    int hash() { return _fname.hashCode() ^ _type.hashCode() ^ _alias ^ (_final ? 1024 : 0); }
+
+    boolean eq(SONType t) {
+        Field f = (Field)t;
+        return _fname.equals(f._fname) && _type==f._type && _alias==f._alias && _final==f._final;
+    }
+
+
+    @Override
+    public SB print( SB sb ) {
+        return _type.print(sb.p(_final?"":"!").p(_fname).p(":").p(_alias).p(" : "));
+    }
+
+    @Override public String str() { return _fname; }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONType.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONType.java
new file mode 100644
index 0000000..ba7eaac
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONType.java
@@ -0,0 +1,235 @@
+package com.compilerprogramming.ezlang.compiler.sontypes;
+
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.Utils;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ * These types are part of a Monotone Analysis Framework,
+ * @see <a href="https://www.cse.psu.edu/~gxt29/teaching/cse597s21/slides/08monotoneFramework.pdf">see for example this set of slides</a>.
+ * <p>
+ * The types form a lattice; @see <a href="https://en.wikipedia.org/wiki/Lattice_(order)">a symmetric complete bounded (ranked) lattice.</a>
+ * <p>
+ * This wild lattice theory will be needed later to allow us to easily beef up
+ * the analysis and optimization of the Simple compiler... but we don't need it
+ * now, just know that it is coming along in a later Chapter.
+ * <p>g
+ * One of the fun things here is that while the theory is deep and subtle, the
+ * actual implementation is darn near trivial and is generally really obvious
+ * what we're doing with it.  Right now, it's just simple integer math to do
+ * simple constant folding e.g. 1+2 == 3 stuff.
+ */
+
+public class SONType {
+    static final HashMap<SONType, SONType> INTERN = new HashMap<>();
+
+    // ----------------------------------------------------------
+    // Simple types are implemented fully here.  "Simple" means: the code and
+    // type hierarchy are simple, not that the Type is conceptually simple.
+    static final byte TBOT    = 0; // Bottom (ALL)
+    static final byte TTOP    = 1; // Top    (ANY)
+    static final byte TCTRL   = 2; // Ctrl flow bottom
+    static final byte TXCTRL  = 3; // Ctrl flow top (mini-lattice: any-xctrl-ctrl-all)
+    static final byte TNIL    = 4; // low null of all flavors
+    static final byte TXNIL   = 5; // high or choice null
+    static final byte TSIMPLE = 6; // End of the Simple Types
+
+    static final byte TPTR    = 7; // All nil-able scalar values
+    static final byte TINT    = 8; // All Integers; see TypeInteger
+    static final byte TFLT    = 9; // All Floats  ; see TypeFloat
+    static final byte TMEMPTR =10; // Memory pointer to a struct type
+    static final byte TFUNPTR =11; // Function pointer; unique signature and code address (just a bit)
+    static final byte TTUPLE  =12; // Tuples; finite collections of unrelated Types, kept in parallel
+    static final byte TMEM    =13; // All memory (alias 0) or A slice of memory - with specific alias
+    static final byte TSTRUCT =14; // Structs; tuples with named fields
+    static final byte TFLD    =15; // Named fields in structs
+    static final byte TRPC    =16; // Return Program Control (Return PC or RPC)
+
+    public final byte _type;
+
+    public boolean is_simple() { return _type < TSIMPLE; }
+    private static final String[] STRS = new String[]{"Bot","Top","Ctrl","~Ctrl","null","~nil"};
+    protected SONType(byte type) { _type = type; }
+
+    public static final SONType BOTTOM   = new SONType( TBOT   ).intern(); // ALL
+    public static final SONType TOP      = new SONType( TTOP   ).intern(); // ANY
+    public static final SONType CONTROL  = new SONType( TCTRL  ).intern(); // Ctrl
+    public static final SONType XCONTROL = new SONType( TXCTRL ).intern(); // ~Ctrl
+    public static final SONType NIL      = new SONType( TNIL   ).intern(); // low null of all flavors
+    public static final SONType XNIL     = new SONType( TXNIL  ).intern(); // high or choice null
+    public static SONType[] gather() {
+        ArrayList<SONType> ts = new ArrayList<>();
+        ts.add(BOTTOM);
+        ts.add(CONTROL);
+        ts.add(NIL);
+        ts.add(XNIL);
+        SONTypeNil.gather(ts);
+        SONTypePtr.gather(ts);
+        SONTypeInteger.gather(ts);
+        SONTypeFloat.gather(ts);
+        SONTypeMemPtr.gather(ts);
+        SONTypeFunPtr.gather(ts);
+        SONTypeMem.gather(ts);
+        Field.gather(ts);
+        SONTypeStruct.gather(ts);
+        SONTypeTuple.gather(ts);
+        SONTypeRPC.gather(ts);
+        int sz = ts.size();
+        for( int i = 0; i < sz; i++ )
+            ts.add(ts.get(i).dual());
+        return ts.toArray(new SONType[ts.size()]);
+    }
+
+    // Is high or on the lattice centerline.
+    public boolean isHigh       () { return _type==TTOP || _type==TXCTRL || _type==TXNIL; }
+    public boolean isHighOrConst() { return isHigh() || isConstant(); }
+
+    // Strict constant values, things on the lattice centerline.
+    // Excludes both high and low values
+    public boolean isConstant() { return _type==TNIL; }
+
+    // ----------------------------------------------------------
+
+    // Notes on Type interning: At the moment it is not easy to reset the
+    // interned types because we hold static references to several types and
+    // these are scattered around.  This means the INTERN cache will retain all
+    // types from every run of the Parser.  For this to work correctly types
+    // must be rigorous about defining when they are the same.  Also types need
+    // to be immutable once defined.  The rationale for interning is
+    // *correctness* with cyclic type definitions.  Simple structural recursive
+    // checks go exponential with merely sharing, but with cycles they will
+    // stack overflow and crash.  Interning means we do not need to have sharing
+    // checks with every type compare, only during interning.
+
+    // Factory method which interns "this"
+    public  <T extends SONType> T intern() {
+        T nnn = (T)INTERN.get(this);
+        if( nnn==null )
+            INTERN.put(nnn=(T)this,this);
+        return nnn;
+    }
+
+    private int _hash;          // Hash cache; not-zero when set.
+    @Override
+    public final int hashCode() {
+        if( _hash!=0 ) return _hash;
+        _hash = hash();
+        if( _hash==0 ) _hash = 0xDEADBEEF; // Bad hash from subclass; use some junk thing
+        return _hash;
+    }
+    // Override in subclasses
+    int hash() { return _type; }
+
+    @Override
+    public final boolean equals( Object o ) {
+        if( o==this ) return true;
+        if( !(o instanceof SONType t)) return false;
+        if( _type != t._type ) return false;
+        return eq(t);
+    }
+    // Overridden in subclasses; subclass can assume "this!=t" and java classes are same
+    boolean eq(SONType t) { return true; }
+
+
+    // ----------------------------------------------------------
+    public final SONType meet(SONType t) {
+        // Shortcut for the self case
+        if( t == this ) return this;
+        // Same-type is always safe in the subclasses
+        if( _type==t._type ) return xmeet(t);
+        // TypeNil vs TypeNil meet
+        if( this instanceof SONTypeNil ptr0 && t instanceof SONTypeNil ptr1 )
+            return ptr0.nmeet(ptr1);
+        // Reverse; xmeet 2nd arg is never "is_simple" and never equal to "this".
+        if(   is_simple() ) return this.xmeet(t   );
+        if( t.is_simple() ) return t   .xmeet(this);
+        return SONType.BOTTOM;     // Mixing 2 unrelated types
+    }
+
+    // Compute meet right now.  Overridden in subclasses.
+    // Handle cases where 'this.is_simple()' and unequal to 't'.
+    // Subclassed xmeet calls can assert that '!t.is_simple()'.
+    SONType xmeet(SONType t) {
+        assert is_simple(); // Should be overridden in subclass
+        // ANY meet anything is thing; thing meet ALL is ALL
+        if( _type==TBOT || t._type==TTOP ) return this;
+        if( _type==TTOP || t._type==TBOT ) return    t;
+
+        // RHS TypeNil vs NIL/XNIL
+        if( _type==  TNIL ) return t instanceof SONTypeNil ptr ? ptr.meet0() : (t._type==TXNIL ? SONTypePtr.PTR : BOTTOM);
+        if( _type== TXNIL ) return t instanceof SONTypeNil ptr ? ptr.meetX() : (t._type== TNIL ? SONTypePtr.PTR : BOTTOM);
+        // 'this' is only {TCTRL,TXCTRL}
+        // Other non-simple RHS things bottom out
+        if( !t.is_simple() ) return BOTTOM;
+        // If RHS is NIL/XNIL
+        if( t._type==TNIL || t._type==TXNIL ) return BOTTOM;
+        // Both are {TCTRL,TXCTRL} and unequal to each other
+        return _type==TCTRL || t._type==TCTRL ? CONTROL : XCONTROL;
+    }
+
+    public SONType dual() {
+        return switch( _type ) {
+        case TBOT  -> TOP;
+        case TTOP  -> BOTTOM;
+        case TCTRL ->XCONTROL;
+        case TXCTRL-> CONTROL;
+        case TNIL  ->XNIL;
+        case TXNIL -> NIL;
+        default -> throw Utils.TODO(); // Should not reach here
+        };
+    }
+
+    // ----------------------------------------------------------
+    // Our lattice is defined with a MEET and a DUAL.
+    // JOIN is dual of meet of both duals.
+    public final SONType join(SONType t) {
+        if( this==t ) return this;
+        return dual().meet(t.dual()).dual();
+    }
+
+    // True if this "isa" t; e.g. 17 isa TypeInteger.BOT
+    public boolean isa( SONType t ) { return meet(t)==t; }
+
+    // True if this "isa" t up to named structures
+    public boolean shallowISA( SONType t ) { return isa(t); }
+
+    public SONType nonZero() { return SONTypePtr.NPTR; }
+
+    // Make a zero version of this type, 0 for integers and null for pointers.
+    public SONType makeZero() { return SONType.NIL; }
+
+    /** Compute greatest lower bound in the lattice */
+    public SONType glb() { return SONType.BOTTOM; }
+
+    // Is forward-reference
+    public boolean isFRef() { return false; }
+
+    // All reachable struct Fields are final
+    public boolean isFinal() { return true; }
+
+    // Make all reachable struct Fields final
+    public SONType makeRO() { return this; }
+
+    // ----------------------------------------------------------
+
+    // Size in bits to hold an instance of this type.
+    // Sizes are expected to be between 1 and 64 bits.
+    // Size 0 means this either takes no space (such as a known-zero field)
+    // or isn't a scalar to be stored in memory.
+    public int log_size() { throw Utils.TODO(); }
+
+    // ----------------------------------------------------------
+    // Useful in the debugger, which calls toString everywhere.
+    // This is a more verbose dev-friendly print.
+    @Override
+    public final String toString() {
+        return str();
+    }
+
+    public SB  print(SB sb) { return sb.p(str()); }
+    public SB gprint(SB sb) { return print(sb); }
+
+    // This is used by error messages, and is a shorted print.
+    public String str() { return STRS[_type]; }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeFloat.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeFloat.java
new file mode 100644
index 0000000..c4d2dba
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeFloat.java
@@ -0,0 +1,94 @@
+package com.compilerprogramming.ezlang.compiler.sontypes;
+
+import com.compilerprogramming.ezlang.compiler.Utils;
+import java.util.ArrayList;
+
+/**
+ * Float Type
+ */
+public class SONTypeFloat extends SONType {
+
+    public final static SONTypeFloat ONE = constant(1.0);
+    public final static SONTypeFloat FZERO = constant(0.0);
+    public final static SONTypeFloat F32 = make(32, 0);
+    public final static SONTypeFloat F64 = make(64, 0);
+
+    // - high -64, high -32, con 0, low +32, low +64
+    public final byte _sz;
+
+    /**
+     * The constant value or 0
+     */
+    public final double _con;
+
+    private SONTypeFloat(byte sz, double con) {
+        super(TFLT);
+        _sz = sz;
+        _con = con;
+    }
+    private static SONTypeFloat make(int sz, double con) {
+        return new SONTypeFloat((byte)sz,con).intern();
+    }
+
+    public static SONTypeFloat constant(double con) { return make(0, con); }
+
+    public static void gather(ArrayList<SONType> ts) { ts.add(F64); ts.add(F32); ts.add(constant(3.141592653589793)); }
+
+    @Override public String str() {
+        return switch( _sz ) {
+        case -64 -> "~flt";
+        case -32 -> "~f32";
+        case   0 -> ""+_con+((float)_con==_con ? "f" : "");
+        case  32 ->  "f32";
+        case  64 ->  "flt";
+        default  -> throw Utils.TODO();
+        };
+    }
+    private boolean isF32() { return ((float)_con)==_con; }
+
+    @Override public boolean isHigh    () { return _sz< 0; }
+    @Override public boolean isConstant() { return _sz==0; }
+    @Override public int log_size() { return _sz==32 || _sz==-32 ? 2 : 3; }
+
+    public double value() { return _con; }
+
+    @Override
+    public SONTypeFloat xmeet(SONType other) {
+        SONTypeFloat f = (SONTypeFloat)other;
+        // Invariant from caller: 'this' != 'other' and same class (TypeFloat).
+        SONTypeFloat i = (SONTypeFloat)other; // Contract
+        // Larger size in i1, smaller in i0
+        SONTypeFloat i0 = _sz < i._sz ? this : i;
+        SONTypeFloat i1 = _sz < i._sz ? i : this;
+
+        if( i1._sz== 64 ) return F64;
+        if( i0._sz==-64 ) return i1;
+        if( i1._sz== 32 )
+            return i0._sz==0 && !i0.isF32() ? F64 : F32;
+        if( i1._sz!=  0 ) return i1;
+        // i1 is a constant
+        if( i0._sz==-32 )
+            return i1.isF32() ? i1 : F64;
+        // Since both are constants, and are never equals (contract) unequals
+        // constants fall to bottom
+        return i0.isF32() && i1.isF32() ? F32 : F64;
+    }
+
+    @Override
+    public SONType dual() {
+        return isConstant() ? this : make(-_sz,0); // Constants are a self-dual
+    }
+
+    @Override public SONType makeZero() { return FZERO; }
+    @Override public SONType glb() { return F64; }
+
+    @Override
+    int hash() { return (int)(Double.hashCode(_con) ^ _sz ^ (1<<17)); }
+    @Override
+    public boolean eq( SONType t ) {
+        SONTypeFloat i = (SONTypeFloat)t; // Contract
+        // Allow NaN to check for equality
+        return _sz==i._sz && Double.doubleToLongBits(_con)==Double.doubleToLongBits(i._con);
+    }
+
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeFunPtr.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeFunPtr.java
new file mode 100644
index 0000000..2d81a60
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeFunPtr.java
@@ -0,0 +1,106 @@
+package com.compilerprogramming.ezlang.compiler.sontypes;
+
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.Utils;
+import java.util.ArrayList;
+
+/**
+ * Represents a Pointer to a function.
+ * <p></p>
+ * Functions have argument types and a return type, making a function
+ * signature.  Within a signature there can be many instances of functions, and
+ * each function is labeled with a small integer constant.  A TFP can represent
+ * a single signature and a set of functions; the set might contain only 1
+ * function or the zeroth function (the null ptr).
+ * <p></p>
+ * Functions with different signatures cannot be mixed, and will result in a
+ * bottom function type, which can only be null-checked.
+ */
+public class SONTypeFunPtr extends SONTypeNil {
+    // A TypeFunPtr is Signature and a set of functions.
+    public final SONTypeTuple _sig;
+    public final SONType _ret;
+    // Cheesy easy implementation for a small set; 1 bit per unique function
+    // 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 SONTypeFunPtr(byte nil, SONTypeTuple sig, SONType ret, long fidxs) {
+        super(TFUNPTR,nil);
+        assert sig != null;
+        _sig = sig;
+        _ret = ret;
+        _fidxs = fidxs;
+    }
+
+    public static SONTypeFunPtr make(byte nil, SONTypeTuple sig, SONType ret, long fidxs ) { return new SONTypeFunPtr(nil,sig,ret,fidxs).intern(); }
+    public static SONTypeFunPtr make(boolean nil, SONTypeTuple sig, SONType ret ) { return make((byte)(nil ? 3 : 2),sig,ret,-1); }
+    @Override
+    SONTypeFunPtr makeFrom(byte nil ) { return  nil ==_nil   ? this : make(  nil,_sig,_ret,   _fidxs); }
+    public SONTypeFunPtr makeFrom(SONType ret ) { return     ret ==_ret   ? this : make( _nil,_sig, ret,   _fidxs); }
+    public SONTypeFunPtr makeFrom(int fidx ) { return make((byte)2, _sig,_ret,1L<<fidx ); }
+
+    public static SONTypeFunPtr BOT   = make((byte)3, SONTypeTuple.BOT, SONType.BOTTOM,-1);
+    public static SONTypeFunPtr TEST  = make((byte)2, SONTypeTuple.TEST, SONTypeInteger.BOT,1);
+    public static SONTypeFunPtr TEST0 = make((byte)3, SONTypeTuple.TEST, SONTypeInteger.BOT,3);
+    public static SONTypeFunPtr MAIN  = make((byte)3, SONTypeTuple.MAIN, SONTypeInteger.BOT,-1);
+    public static void gather(ArrayList<SONType> ts) { ts.add(TEST); ts.add(TEST0); ts.add(BOT); ts.add(MAIN); }
+
+    @Override
+    SONType xmeet(SONType t) {
+        SONTypeFunPtr that = (SONTypeFunPtr) t;
+        return SONTypeFunPtr.make(xmeet0(that),(SONTypeTuple)_sig.meet(that._sig), _ret.meet(that._ret), _fidxs | that._fidxs);
+    }
+
+    @Override
+    public SONTypeFunPtr dual() { return SONTypeFunPtr.make(dual0(), _sig.dual(), _ret.dual(), ~_fidxs); }
+
+    // RHS is NIL; do not deep-dual when crossing the centerline
+    @Override public SONType meet0() { return _nil==3 ? this : make((byte)3,_sig,_ret,_fidxs); }
+
+    @Override public SONTypeFunPtr glb() { return make((byte)3,_sig,_ret,-1L); }
+
+    @Override public boolean isHigh    () { return _nil <= 1 || (_nil==2 && _fidxs==0); }
+    @Override public boolean isConstant() { return (_nil==2 && Long.bitCount(_fidxs)==1) || (_nil==3 && _fidxs==0); }
+
+    @Override public int log_size() { return 2; } // (1<<2)==4-byte pointers
+
+    public SONType arg(int i) { return _sig._types[i]; }
+    public long fidxs() { return _fidxs; }
+    public SONType ret() { return _ret; }
+    public int nargs() { return _sig._types.length; }
+    public int fidx() { assert Long.bitCount(_fidxs)==1; return Long.numberOfTrailingZeros(_fidxs); }
+
+    @Override
+    int hash() { return Utils.fold(_sig.hashCode() ^ _ret.hashCode() ^ _fidxs ^ super.hash()); }
+
+    @Override
+    boolean eq(SONType t) {
+        SONTypeFunPtr ptr = (SONTypeFunPtr)t; // Invariant
+        return _sig == ptr._sig  && _ret == ptr._ret && _fidxs == ptr._fidxs && super.eq(ptr);
+    }
+
+    @Override public String str() { return print(new SB()).toString(); }
+    @Override public SB  print(SB sb) { return _print(sb,false,true); }
+    public SB print(SB sb, boolean n) { return _print(sb,false,n); }
+    @Override public SB gprint(SB sb) { return _print(sb,true ,true); }
+    private static SB _print(SB sb, boolean g, SONType t) { return g ? t.gprint(sb) : t.print(sb); }
+    private SB _print(SB sb, boolean g, boolean n) {
+        sb.p(x()).p("{ ");
+        if( _sig._types!=null )
+            for( SONType 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); }
+
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeInteger.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeInteger.java
new file mode 100644
index 0000000..5030b57
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeInteger.java
@@ -0,0 +1,127 @@
+package com.compilerprogramming.ezlang.compiler.sontypes;
+
+import com.compilerprogramming.ezlang.compiler.Utils;
+import java.util.ArrayList;
+
+/**
+ * Integer Type
+ */
+public class SONTypeInteger extends SONType {
+
+    public final static SONTypeInteger ZERO= make(0,0);
+    public final static SONTypeInteger FALSE=ZERO;
+    public final static SONTypeInteger TRUE= make(1,1);
+
+    public final static SONTypeInteger I1  = make(-1,0);
+    public final static SONTypeInteger I8  = make(-128,127);
+    public final static SONTypeInteger I16 = make(-32768,32767);
+    public final static SONTypeInteger I32 = make(-1L<<31,(1L<<31)-1);
+    public final static SONTypeInteger BOT = make(Long.MIN_VALUE,Long.MAX_VALUE);
+    public final static SONTypeInteger TOP = BOT.dual();
+
+    public final static SONTypeInteger U1  = make(0,1);
+    public final static SONTypeInteger BOOL= U1;
+    public final static SONTypeInteger U8  = make(0,255);
+    public final static SONTypeInteger U16 = make(0,65535);
+    public final static SONTypeInteger U32 = make(0,(1L<<32)-1);
+
+    /**
+     * Describes an integer *range* - everything from min to max; both min and
+     * max are inclusive.  If min==max, this is a constant.
+     * <br>
+     * If min <= max, this is a  below center (towards bottom).
+     * If min >  max, this is an above center (towards top).
+     */
+    public final long _min, _max;
+
+    private SONTypeInteger(long min, long max) {
+        super(TINT);
+        _min = min;
+        _max = max;
+    }
+
+    // Strict non-zero contract
+    public static SONTypeInteger make(long lo, long hi) { return new SONTypeInteger(lo,hi).intern(); }
+
+    public static SONTypeInteger constant(long con) { return make(con, con); }
+
+    public static void gather(ArrayList<SONType> ts) { ts.add(I32); ts.add(BOT); ts.add(U1); ts.add(I1); ts.add(U8); }
+
+    @Override public String str() {
+        if( isConstant() ) return ""+_min;
+        long lo = _min, hi = _max;
+        String x = "";
+        if( hi < lo ) {
+            lo = _max;  hi = _min;
+            x = "~";
+        }
+        return x+_str(lo,hi);
+    }
+    private static String _str(long lo, long hi) {
+        if( lo==Long.MIN_VALUE && hi==Long.MAX_VALUE ) return "int";
+        if( lo==       0 && hi==         1 ) return "bool";
+        if( lo==      -1 && hi==         0 ) return "i1";
+        if( lo==    -128 && hi==       127 ) return "i8";
+        if( lo==  -32768 && hi==     32767 ) return "i16";
+        if( lo== -1L<<31 && hi==(1L<<31)-1 ) return "i32";
+        if( lo==       0 && hi==      255  ) return "u8";
+        if( lo==       0 && hi==     65535 ) return "u16";
+        if( lo==       0 && hi==(1L<<32)-1 ) return "u32";
+        return "["+lo+"-"+hi+"]";
+    }
+
+    @Override public boolean isHigh    () { return _min >  _max; }
+    @Override public boolean isConstant() { return _min == _max; }
+
+    @Override public int log_size() {
+        if( this==I8  || this==U8 || this==BOOL ) return 0; // 1<<0 == 1 bytes
+        if( this==I16 || this==U16              ) return 1; // 1<<1 == 2 bytes
+        if( this==I32 || this==U32              ) return 2; // 1<<2 == 4 bytes
+        if( this==BOT                           ) return 3; // 1<<3 == 8 bytes
+        if( isHighOrConst() ) return 0;
+        throw Utils.TODO();
+    }
+
+    public long value() { assert isConstant(); return _min; }
+
+    // AND-mask of forced zeros.  e.g. unsigned types will return their mask;
+    // u8 will return 0xFF.  But also a range of 16-18 (0x10-0x12) will return
+    // 0x13 - no value in the range {16,17,18} will allow bit 0x04 to be set.
+    public long mask() {
+        if( isHigh() ) return 0;
+        if( isConstant() ) return _min;
+        // Those bit positions which differ min to max
+        long x = _min ^ _max;
+        // Highest '1' bit in the differ set.  Since the range is from min to
+        // max, all values below here are possible.
+        long ff1 = Long.highestOneBit(x);
+        // Make a all-1's mask from ff1, and set over the same bits (either min
+        // or max is ok).
+        long mask = _min | (ff1-1) | ff1;
+        return mask;
+    }
+
+    @Override
+    public SONType xmeet(SONType other) {
+        // Invariant from caller: 'this' != 'other' and same class (TypeInteger)
+        SONTypeInteger i = (SONTypeInteger)other; // Contract
+        return make(Math.min(_min,i._min), Math.max(_max,i._max));
+    }
+
+    @Override public SONTypeInteger dual() { return make(_max,_min); }
+
+    @Override public SONTypeInteger nonZero() {
+        if( isHigh() ) return this;
+        if( this==ZERO ) return null;                  // No sane answer
+        if( _min==0 ) return make(1,Math.max(_max,1)); // specifically good on BOOL
+        if( _max==0 ) return make(_min,-1);
+        return this;
+    }
+    @Override public SONType makeZero() { return ZERO; }
+    @Override public SONTypeInteger glb() { return BOT; }
+    @Override int hash() { return Utils.fold(_min) * Utils.fold(_max); }
+    @Override public boolean eq( SONType t ) {
+        SONTypeInteger i = (SONTypeInteger)t; // Contract
+        return _min==i._min && _max==i._max;
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeMem.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeMem.java
new file mode 100644
index 0000000..aed4569
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeMem.java
@@ -0,0 +1,72 @@
+package com.compilerprogramming.ezlang.compiler.sontypes;
+
+import com.compilerprogramming.ezlang.compiler.SB;
+
+import java.util.ArrayList;
+
+
+/**
+ * Represents a slice of memory corresponding to a set of aliases
+ */
+public class SONTypeMem extends SONType {
+
+    // Which slice of memory?
+    //  0 means TOP, no slice.
+    //  0 means BOT, all memory.
+    //  N means slice#N.
+    public final int _alias;
+    public final SONType _t;       // Memory contents, some scalar type
+
+    private SONTypeMem(int alias, SONType t) {
+        super(TMEM);
+        assert alias!=0 || (t== SONType.TOP || t== SONType.BOTTOM);
+        _alias = alias;
+        _t = t;
+    }
+
+    public static SONTypeMem make(int alias, SONType t) { return new SONTypeMem(alias,t).intern(); }
+    public static final SONTypeMem TOP = make(0, SONType.TOP   );
+    public static final SONTypeMem BOT = make(0, SONType.BOTTOM);
+
+    public static void gather(ArrayList<SONType> ts) { ts.add(make(1, SONType.NIL)); ts.add(make(1, SONTypeInteger.ZERO)); ts.add(BOT); }
+
+    @Override
+    SONTypeMem xmeet(SONType t) {
+        SONTypeMem that = (SONTypeMem) t; // Invariant: TypeMem and unequal
+        if( this==TOP ) return that;
+        if( that==TOP ) return this;
+        if( this==BOT ) return BOT;
+        if( that==BOT ) return BOT;
+        int alias = _alias==that._alias ? _alias : 0;
+        SONType mt = _t.meet(that._t);
+        return make(alias,mt);
+    }
+
+    @Override
+    public SONType dual() {
+        return make(_alias,_t.dual());
+    }
+
+    @Override public boolean isHigh() { return _t.isHigh(); }
+    @Override public SONType glb() { return make(_alias,_t.glb()); }
+
+    @Override int hash() { return 9876543 + _alias + _t.hashCode(); }
+
+    @Override boolean eq(SONType t) {
+        SONTypeMem that = (SONTypeMem) t; // Invariant
+        return _alias == that._alias && _t == that._t;
+    }
+
+    @Override public SB print(SB sb) {
+        sb.p("#");
+        if( _alias==0 ) return sb.p(_t._type==TTOP ? "TOP" : "BOT");
+        return _t.print(sb.p(_alias).p(":"));
+    }
+    @Override public SB gprint(SB sb) {
+        sb.p("#");
+        if( _alias==0 ) return sb.p(_t._type==TTOP ? "TOP" : "BOT");
+        return _t.gprint(sb.p(_alias).p(":"));
+    }
+
+    @Override public String str() { return print(new SB()).toString(); }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeMemPtr.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeMemPtr.java
new file mode 100644
index 0000000..7b0f1c8
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeMemPtr.java
@@ -0,0 +1,107 @@
+package com.compilerprogramming.ezlang.compiler.sontypes;
+
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.Utils;
+import java.util.ArrayList;
+
+/**
+ * Represents a Pointer to memory.
+ *
+ * Null is generic pointer to non-existent memory.
+ * *void is a non-Null pointer to all possible refs, both structs and arrays.
+ * Pointers can be to specific struct and array types, or a union with Null.
+ * The distinguished *$BOT ptr represents union of *void and Null.
+ * The distinguished *$TOP ptr represents the dual of *$BOT.
+ */
+public class SONTypeMemPtr extends SONTypeNil {
+    // A TypeMemPtr is pair (obj,nil)
+    // where obj is a TypeStruct, possibly TypeStruct.BOT/TOP
+    // where nil is an explicit null is allowed or not
+
+    // Examples:
+    // (Person,false) - a not-nil Person
+    // (Person,true ) - a Person or a nil
+    // (BOT   ,false) - a not-nil void* (unspecified struct)
+    // (TOP   ,true ) - a nil
+
+    public final SONTypeStruct _obj;
+
+    public SONTypeMemPtr(byte nil, SONTypeStruct obj) {
+        super(TMEMPTR,nil);
+        assert obj!=null;
+        _obj = obj;
+    }
+    static SONTypeMemPtr make(byte nil, SONTypeStruct obj) { return new SONTypeMemPtr(nil, obj).intern(); }
+    public static SONTypeMemPtr makeNullable(SONTypeStruct obj) { return make((byte)3, obj); }
+    public static SONTypeMemPtr make(SONTypeStruct obj) { return new SONTypeMemPtr((byte)2, obj).intern(); }
+    public SONTypeMemPtr makeFrom(SONTypeStruct obj) { return obj==_obj ? this : make(_nil, obj); }
+    public SONTypeMemPtr makeNullable() { return makeFrom((byte)3); }
+    @Override
+    SONTypeMemPtr makeFrom(byte nil) { return nil==_nil ? this : make(nil,_obj); }
+    @Override public SONTypeMemPtr makeRO() { return makeFrom(_obj.makeRO()); }
+    @Override public boolean isFinal() { return _obj.isFinal(); }
+
+    // An abstract pointer, pointing to either a Struct or an Array.
+    // Can also be null or not, so 4 choices {TOP,BOT} x {nil,not}
+    public static SONTypeMemPtr BOT = make((byte)3, SONTypeStruct.BOT);
+    public static SONTypeMemPtr TOP = BOT.dual();
+    public static SONTypeMemPtr NOTBOT = make((byte)2, SONTypeStruct.BOT);
+
+    public static SONTypeMemPtr TEST= make((byte)2, SONTypeStruct.TEST);
+    public static void gather(ArrayList<SONType> ts) { ts.add(NOTBOT); ts.add(BOT); ts.add(TEST); }
+
+    @Override
+    public SONTypeNil xmeet(SONType t) {
+        SONTypeMemPtr that = (SONTypeMemPtr) t;
+        return SONTypeMemPtr.make(xmeet0(that), (SONTypeStruct)_obj.meet(that._obj));
+    }
+
+    @Override
+    public SONTypeMemPtr dual() { return SONTypeMemPtr.make( dual0(), _obj.dual()); }
+
+    // RHS is NIL; do not deep-dual when crossing the centerline
+    @Override
+    SONType meet0() { return _nil==3 ? this : make((byte)3,_obj); }
+
+
+    // True if this "isa" t up to named structures
+    @Override public boolean shallowISA( SONType t ) {
+        if( !(t instanceof SONTypeMemPtr that) ) return false;
+        if( this==that ) return true;
+        if( xmeet0(that)!=that._nil ) return false;
+        if( _obj==that._obj ) return true;
+        if( _obj._name.equals(that._obj._name) )
+            return true;        // Shallow, do not follow matching names, just assume ok
+        throw Utils.TODO(); // return _obj.shallowISA(that._obj);
+    }
+
+    @Override public SONTypeMemPtr glb() { return make((byte)3,_obj.glb()); }
+    // Is forward-reference
+    @Override public boolean isFRef() { return _obj.isFRef(); }
+
+    @Override public int log_size() { return 2; } // (1<<2)==4-byte pointers
+
+    @Override int hash() { return _obj.hashCode() ^ super.hash(); }
+
+    @Override boolean eq(SONType t) {
+        SONTypeMemPtr ptr = (SONTypeMemPtr)t; // Invariant
+        return _obj == ptr._obj  && super.eq(ptr);
+    }
+
+    @Override public String str() {
+        if( this== NOTBOT) return "*void";
+        if( this==    BOT) return "*void?";
+        return x()+"*"+_obj.str()+q();
+    }
+
+    @Override public SB print(SB sb) {
+        if( this== NOTBOT) return sb.p("*void");
+        if( this==    BOT) return sb.p("*void?");
+        return _obj.print(sb.p(x()).p("*")).p(q());
+    }
+    @Override public SB gprint(SB sb) {
+        if( this== NOTBOT) return sb.p("*void");
+        if( this==    BOT) return sb.p("*void?");
+        return _obj.gprint(sb.p(x()).p("*")).p(q());
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeNil.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeNil.java
new file mode 100644
index 0000000..3193c0c
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeNil.java
@@ -0,0 +1,60 @@
+package com.compilerprogramming.ezlang.compiler.sontypes;
+
+import java.util.ArrayList;
+
+/**
+ * Nil-able Scalar types
+ */
+public abstract class SONTypeNil extends SONType {
+    // 0 = high-subclass choice nil
+    // 1 = high-subclass no nil
+    // 2 = low -subclass no nil
+    // 3 = low -subclass also nil
+    final byte _nil;
+
+    SONTypeNil(byte t, byte nil ) { super(t); _nil = nil; }
+
+    public static void gather(ArrayList<SONType> ts) { }
+
+    abstract SONTypeNil makeFrom(byte nil);
+
+    byte xmeet0(SONTypeNil that) { return (byte)Math.max(_nil,that._nil); }
+
+    byte dual0() { return (byte)(3-_nil); }
+
+    // RHS is NIL
+    abstract SONType meet0();
+
+    // RHS is XNIL
+    SONType meetX() {
+        return _nil==0 ? XNIL : (_nil<=2 ? SONTypePtr.NPTR : SONTypePtr.PTR);
+    }
+
+    SONType nmeet(SONTypeNil tn) {
+        // Invariants: both are TypeNil subclasses and unequal classes.
+        // If this is TypePtr, we went to TypePtr.nmeet and not here.
+        // If that is TypePtr, this is not (invariant); reverse and go again.
+        if( tn instanceof SONTypePtr ts ) return ts.nmeet(this);
+
+        // Two mismatched TypeNil, no Scalar.
+        if( _nil==0 && tn._nil==0 ) return XNIL;
+        if( _nil<=2 && tn._nil<=2 ) return SONTypePtr.NPTR;
+        return SONTypePtr.PTR;
+    }
+
+    @Override public boolean isHigh       () { return _nil <= 1; }
+    @Override public boolean isConstant   () { return false; }
+    @Override public boolean isHighOrConst() { return isHigh() || isConstant(); }
+
+    @Override public SONType glb() { return SONType.NIL; }
+
+    public boolean notNull() { return _nil==1 || _nil==2; }
+    public boolean nullable() { return _nil==3; }
+
+    final String q() { return _nil == 1 || _nil == 2 ? "" : "?"; }
+    final String x() { return isHigh() ? "~" : ""; }
+
+    int hash() { return _nil<<17; }
+
+    boolean eq(SONTypeNil ptr) { return _nil == ptr._nil; }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypePtr.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypePtr.java
new file mode 100644
index 0000000..dda8dd2
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypePtr.java
@@ -0,0 +1,56 @@
+package com.compilerprogramming.ezlang.compiler.sontypes;
+
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.Utils;
+import java.util.ArrayList;
+
+/**
+ * Represents a Scalar; a single register-sized value.
+ */
+public class SONTypePtr extends SONTypeNil {
+
+    private SONTypePtr(byte nil) { super(TPTR,nil);  }
+
+    // An abstract pointer, pointing to either a Struct or an Array.
+    // Can also be null or not, so 4 choices {TOP,BOT} x {nil,not}
+    public static SONTypePtr XPTR = new SONTypePtr((byte)0).intern();
+    public static SONTypePtr XNPTR= new SONTypePtr((byte)1).intern();
+    public static SONTypePtr NPTR = new SONTypePtr((byte)2).intern();
+    public static SONTypePtr PTR  = new SONTypePtr((byte)3).intern();
+    private static final SONTypePtr[] PTRS = new SONTypePtr[]{XPTR,XNPTR,NPTR,PTR};
+
+    public static void gather(ArrayList<SONType> ts) { ts.add(PTR); ts.add(NPTR); }
+
+    SONTypeNil makeFrom(byte nil) { throw Utils.TODO(); }
+
+    @Override public SONTypeNil xmeet(SONType t) {
+        SONTypePtr that = (SONTypePtr) t;
+        return PTRS[xmeet0(that)];
+    }
+
+    @Override public SONTypePtr dual() { return PTRS[dual0()]; }
+
+    // High scalar loses, low scalar wins
+    @Override
+    SONTypeNil nmeet(SONTypeNil tn) {
+        if( _nil==0 ) return tn; // High scalar loses
+        if( _nil==1 ) return tn.makeFrom(xmeet0(tn)); // High scalar loses
+        if( _nil==2 ) return tn._nil==3 ? PTR : NPTR; // Low scalar wins
+        return PTR; // this
+    }
+
+
+    // RHS is  NIL
+    @Override
+    SONType meet0() { return isHigh() ? NIL : PTR; }
+    // RHS is XNIL
+    // 0->xscalar, 1->nscalar, 2->nscalar, 3->scalar
+    @Override
+    SONType meetX() { return _nil==0 ? XNIL : (_nil==3 ? PTR : NPTR); }
+
+    @Override public SONTypePtr glb() { return PTR; }
+
+    private static final String[] STRS = new String[]{"~ptr","~nptr","nptr","ptr"};
+    @Override public String str() { return STRS[_nil]; }
+    @Override public SB print(SB sb) { return sb.p(str()); }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeRPC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeRPC.java
new file mode 100644
index 0000000..82f0e2f
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeRPC.java
@@ -0,0 +1,97 @@
+package com.compilerprogramming.ezlang.compiler.sontypes;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+
+/**
+ *  Return Program Control or Return PC or RPC
+ */
+public class SONTypeRPC extends SONType {
+
+    // A set of CallEndNode IDs (or StopNode); commonly just one.
+    // Basically a sparse bit set
+    final HashSet<Integer> _rpcs;
+
+    // If true, invert the meaning of the bits
+    final boolean _any;
+
+    private SONTypeRPC(boolean any, HashSet<Integer> rpcs) {
+        super(TRPC);
+        _any = any;
+        _rpcs = rpcs;
+    }
+    private static SONTypeRPC make(boolean any, HashSet<Integer> rpcs) {
+        return new SONTypeRPC(any,rpcs).intern();
+    }
+
+    public static SONTypeRPC constant(int cend) {
+        HashSet<Integer> rpcs = new HashSet<>();
+        rpcs.add(cend);
+        return make(false,rpcs);
+    }
+
+    public  static final SONTypeRPC BOT = make(true,new HashSet<>());
+    private static final SONTypeRPC TEST2 = constant(2);
+    private static final SONTypeRPC TEST3 = constant(2);
+
+    public static void gather(ArrayList<SONType> ts) { ts.add(BOT); ts.add(TEST2); ts.add(TEST3); }
+
+    @Override public String str() {
+        if( _rpcs.isEmpty() )
+            return _any ? "$[ALL]" : "$[]";
+        if( _rpcs.size()==1 )
+            for( Integer rpc : _rpcs )
+                return _any ? "$[-"+rpc+"]" : "$["+rpc+"]";
+        return "$["+(_any ? "-" : "")+_rpcs+"]";
+    }
+
+    @Override public boolean isConstant() {
+        return !_any && _rpcs.size()==1;
+    }
+
+    @Override
+    public SONTypeRPC xmeet(SONType other) {
+        SONTypeRPC rpc = (SONTypeRPC)other;
+        // If the two sets are equal, the _any must be unequal (invariant),
+        // so they cancel and all bits are set.
+        if( _rpcs.equals(rpc._rpcs) )
+            return BOT;
+        // Classic union of bit sets (which might be infinite).
+        HashSet<Integer> lhs = _rpcs, rhs = rpc._rpcs;
+        // Smaller on left
+        if( lhs.size() > rhs.size() ) { lhs = rpc._rpcs; rhs = _rpcs; }
+
+        HashSet<Integer> rpcs = new HashSet<>();
+        boolean any = true;
+        // If both sets are infinite, intersect.
+        if( _any && rpc._any ) {
+            for( Integer i : lhs )  if( rhs.contains(i) )  rpcs.add(i);
+
+        } else if( !_any && !rpc._any ) {
+            // if neither set is infinite, union.
+            rpcs.addAll( lhs );
+            rpcs.addAll( rhs );
+            any = false;
+        } else {
+            // If one is infinite, subtract the other from it.
+            HashSet<Integer> sub = _any ? rpc._rpcs : _rpcs;
+            HashSet<Integer> inf = _any ? _rpcs : rpc._rpcs;
+            for( Integer i : inf )
+                if( inf.contains(i) && !sub.contains(i) )
+                    rpcs.add(i);
+        }
+        return make(any,rpcs);
+    }
+
+    @Override
+    public SONType dual() { return make(!_any,_rpcs); }
+
+    @Override
+    int hash() { return _rpcs.hashCode() ^ (_any ? -1 : 0) ; }
+    @Override
+    public boolean eq( SONType t ) {
+        SONTypeRPC rpc = (SONTypeRPC)t; // Contract
+        return _any==rpc._any && _rpcs.equals(rpc._rpcs);
+    }
+
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeStruct.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeStruct.java
new file mode 100644
index 0000000..f1f15ea
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeStruct.java
@@ -0,0 +1,259 @@
+package com.compilerprogramming.ezlang.compiler.sontypes;
+
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.Utils;
+import java.util.ArrayList;
+
+/**
+ * Represents a struct type.
+ */
+public class SONTypeStruct extends SONType {
+
+    // A Struct has a name and a set of fields; the fields themselves have
+    // names, types and aliases.  The name has no semantic meaning, but is
+    // useful for debugging.  Briefly during parsing a Struct can be a
+    // forward-ref; in this case the _fields array is null.
+
+    // Its illegal to attempt to load a field from a forward-ref struct.
+
+    // Example: "int rez = new S.x; struct S { int x; } return rez;" // Error, S not defined
+    // Rewrite: "struct S { int x; } int rez = new S.x; return rez;" // Ok, no forward ref
+    //
+    // During the normal optimization run, struct types "bottom out" at further
+    // struct references, so we don't have to handle e.g. cyclic types.  The
+    // "bottom out" is again the forward-ref struct.
+    public final String _name;
+    public Field[] _fields;
+
+    public SONTypeStruct(String name, Field[] fields) {
+        super(TSTRUCT);
+        _name = name;
+        _fields = fields;
+    }
+
+    // All fields directly listed
+    public static SONTypeStruct make(String name, Field... fields) { return new SONTypeStruct(name, fields).intern(); }
+    public static final SONTypeStruct TOP = make("$TOP",new Field[0]);
+    public static final SONTypeStruct BOT = make("$BOT",new Field[0]);
+    public static final SONTypeStruct TEST = make("test",new Field[]{Field.TEST});
+    // Forward-ref version
+    public static SONTypeStruct makeFRef(String name) { return make(name, (Field[])null); }
+    // Make a read-only version
+    @Override public SONTypeStruct makeRO() {
+        if( isFinal() ) return this;
+        Field[] flds = new Field[_fields.length];
+        for( int i=0; i<flds.length; i++ )
+            flds[i] = _fields[i].makeRO();
+        return make(_name,flds);
+    }
+
+    // Array
+    public static SONTypeStruct makeAry(SONTypeInteger len, int lenAlias, SONType body, int bodyAlias) {
+        assert body instanceof SONTypeInteger || body instanceof SONTypeFloat || (body instanceof SONTypeNil tn && tn.nullable());
+        assert len.isa(SONTypeInteger.U32);
+        return make("[" + body.str() + "]",
+                    Field.make("#" ,len , lenAlias,true ),
+                    Field.make("[]",body,bodyAlias,false));
+    }
+    // Array
+    public static SONTypeStruct makeArray(SONTypeInteger len, int lenAlias, SONType body, int bodyAlias) {
+        assert body instanceof SONTypeInteger || body instanceof SONTypeFloat || (body instanceof SONTypeNil tn && tn.nullable());
+        assert len.isa(SONTypeInteger.U32);
+        return new SONTypeStruct("[" + body.str() + "]",
+                new Field[] {
+                new Field("#" ,len , lenAlias,true ),
+                new Field("[]",body,bodyAlias,false) });
+    }
+
+    // A pair of self-cyclic types
+    private static final SONTypeStruct S1F = makeFRef("S1");
+    private static final SONTypeStruct S2F = makeFRef("S2");
+    public  static final SONTypeStruct S1  = make("S1", Field.make("a", SONTypeInteger.BOT, -1, false), Field.make("s2", SONTypeMemPtr.make((byte)2,S2F),-2, false) );
+    private static final SONTypeStruct S2  = make("S2", Field.make("b", SONTypeFloat.F64, -3, false), Field.make("s1", SONTypeMemPtr.make((byte)2,S1F),-4, false) );
+
+    private static final SONTypeStruct ARY = makeAry(SONTypeInteger.U32,-1, SONTypeInteger.BOT,-2);
+
+    public static void gather(ArrayList<SONType> ts) { ts.add(TEST); ts.add(BOT); ts.add(S1); ts.add(S2); ts.add(ARY); }
+
+    // Find field index by name
+    public int find(String fname) {
+        for( int i=0; i<_fields.length; i++ )
+            if( _fields[i]._fname.equals(fname) )
+                return i;
+        return -1;
+    }
+    public Field field(String fname) {
+        int idx = find(fname);
+        return idx== -1 ? null : _fields[idx];
+    }
+
+    // Find field index by alias
+    public int findAlias( int alias ) {
+        for( int i=0; i<_fields.length; i++ )
+            if( _fields[i]._alias==alias )
+                return i;
+        return -1;
+    }
+
+
+    @Override
+    SONType xmeet(SONType t) {
+        SONTypeStruct that = (SONTypeStruct) t;
+        if( this==TOP ) return that;
+        if( that==TOP ) return this;
+        if( this==BOT ) return BOT;
+        if( that==BOT ) return BOT;
+
+        // Within the same compilation unit, struct names are unique.  If the
+        // names differ, its different structs.  Across many compilation units,
+        // structs with the same name but different field layouts can be
+        // interned... which begs the question:
+        // "What is the meet of structs from two different compilation units?"
+        // And the answer is: "don't ask".
+        if( !_name.equals(that._name) )
+            return BOT;         // It's a struct; that's about all we know
+        if( this._fields==null ) return that;
+        if( that._fields==null ) return this;
+        if( _fields.length != that._fields.length ) return BOT;
+        // Just do field meets
+        Field[] flds = new Field[_fields.length];
+        for( int i=0; i<_fields.length; i++ ) {
+            Field f0 = _fields[i], f1 = that._fields[i];
+            if( !f0._fname.equals(f1._fname) || f0._alias != f1._alias )
+                return BOT;
+            flds[i] = (Field)f0.meet(f1);
+        }
+        return make(_name,flds);
+    }
+
+    @Override
+    public SONTypeStruct dual() {
+        if( this==TOP ) return BOT;
+        if( this==BOT ) return TOP;
+        if( _fields == null ) return this;
+        Field[] flds = new Field[_fields.length];
+        for( int i=0; i<_fields.length; i++ )
+            flds[i] = _fields[i].dual();
+        return make(_name,flds);
+    }
+
+    // Keeps the same struct, but lower-bounds all fields.
+    @Override public SONTypeStruct glb() {
+        if( _glb() ) return this;
+        // Need to glb each field
+        Field[] flds = new Field[_fields.length];
+        for( int i=0; i<_fields.length; i++ )
+            flds[i] = _fields[i].glb();
+        return make(_name,flds);
+    }
+    private boolean _glb() {
+        if( _fields!=null )
+          for( Field f : _fields )
+              if( f.glb() != f )
+                 return false;
+        return true;
+    }
+
+    // Is forward-reference
+    @Override public boolean isFRef() { return _fields==null; }
+    // All fields are final
+    @Override public boolean isFinal() {
+        if( _fields==null ) return true;
+        for( Field fld : _fields )
+            if( !fld.isFinal() )
+                return false;
+        return true;
+    }
+
+    @Override
+    boolean eq(SONType t) {
+        SONTypeStruct ts = (SONTypeStruct)t; // Invariant
+        if( !_name.equals(ts._name) )
+            return false;
+//        if( _fields == ts._fields ) return true;
+//        if( _fields==null || ts._fields==null ) return false;
+//        if( _fields.length!=ts._fields.length )
+//            return false;
+//        for( int i = 0; i < _fields.length; i++ )
+//            if( _fields[i] != ts._fields[i] )
+//                return false;
+        return true;
+    }
+
+    @Override
+    int hash() {
+        long hash = _name.hashCode();
+//        if( _fields != null )
+//            for( Field f : _fields )
+//                hash = Utils.rot(hash,13) ^ f.hashCode();
+//        return Utils.fold(hash);
+        return (int) hash;
+    }
+
+    @Override
+    public SB print(SB sb) {
+        sb.p(_name);
+        if( _fields == null ) return sb; // Forward reference struct, just print the name
+        if( isAry() ) return sb; // Skip printing generic array fields
+        sb.p(" {");
+        for( Field f : _fields )
+            f._type.print(sb).p(f._final ? " " : " !").p(f._fname).p("; ");
+        return sb.p("}");
+    }
+    @Override public SB gprint( SB sb ) { return sb.p(_name); }
+
+    @Override public String str() { return _name; }
+
+
+    public boolean isAry() { return _fields.length==2 && _fields[1]._fname.equals("[]"); }
+
+    public int aryBase() {
+        assert isAry();
+        if( _offs==null ) _offs = offsets();
+        return _offs[1];
+    }
+    public int aryScale() {
+        assert isAry();
+        return _fields[1]._type.log_size();
+    }
+
+
+    // Field offsets as packed byte offsets
+    private int[] _offs;  // Lazily computed and not part of the type semantics
+    public int offset(int idx) {
+        if( _offs==null ) _offs = offsets();
+        return _offs[idx];
+    }
+    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 )
+            cnts[f._type.log_size()]++; // Log size is 0(byte), 1(i16/u16), 2(i32/f32), 3(i64/dbl)
+        int off = 0, idx = 0; // Base common struct fields go here, e.g. Mark/Klass
+        // Compute offsets to the start of each power-of-2 aligned fields.
+        int[] offs = new int[4];
+        for( int i=3; i>=0; i-- ) {
+            offs[i] = off;
+            off += cnts[i]<<i;
+        }
+        // Assign offsets to all fields.
+        // Really a hidden radix sort.
+        _offs = new int[_fields.length+1];
+        for( Field f : _fields ) {
+            int log = f._type.log_size();
+            _offs[idx++] = offs[log]; // Field offset
+            offs[log] += 1<<log;      // Next field offset at same alignment
+            cnts[log]--;              // Count down, should be all zero at end
+        }
+        _offs[_fields.length] = (off+7)& ~7; // Round out to max alignment
+        return _offs;
+    }
+
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeTuple.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeTuple.java
new file mode 100644
index 0000000..4ea6539
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeTuple.java
@@ -0,0 +1,99 @@
+package com.compilerprogramming.ezlang.compiler.sontypes;
+
+import com.compilerprogramming.ezlang.compiler.SB;
+import java.util.ArrayList;
+
+public class SONTypeTuple extends SONType {
+
+    public final SONType[] _types;
+
+    public SONTypeTuple(SONType[] types) { super(TTUPLE); _types = types; }
+    public  static SONTypeTuple make(SONType... types) { return new SONTypeTuple(types).intern(); }
+
+    public static final SONTypeTuple BOT = new SONTypeTuple(new SONType[0]).intern();
+    public static final SONTypeTuple TOP = new SONTypeTuple(null).intern();
+
+    public static final SONTypeTuple TEST = make(SONTypeInteger.BOT, SONTypeMemPtr.TEST);
+    public static final SONTypeTuple START= make(SONType.CONTROL, SONTypeMem.TOP, SONTypeInteger.BOT);
+    public static final SONTypeTuple MAIN = make(SONTypeInteger.BOT);
+    public static final SONTypeTuple RET  = make(SONType.CONTROL, SONTypeMem.BOT, SONType.BOTTOM);
+
+    public static final SONTypeTuple IF_BOTH    = make(new SONType[]{SONType. CONTROL, SONType. CONTROL});
+    public static final SONTypeTuple IF_NEITHER = make(new SONType[]{SONType.XCONTROL, SONType.XCONTROL});
+    public static final SONTypeTuple IF_TRUE    = make(new SONType[]{SONType. CONTROL, SONType.XCONTROL});
+    public static final SONTypeTuple IF_FALSE   = make(new SONType[]{SONType.XCONTROL, SONType. CONTROL});
+
+    public  static void gather(ArrayList<SONType> ts) { ts.add(BOT); ts.add(TEST); ts.add(START); ts.add(MAIN); ts.add(IF_TRUE); }
+
+    @Override
+    SONType xmeet(SONType other) {
+        SONTypeTuple tt = (SONTypeTuple)other;     // contract from xmeet
+        if( this==TOP ) return other;
+        if( tt  ==TOP ) return this ;
+        if( _types.length != tt._types.length )
+            return BOT;
+        SONType[] ts = new SONType[_types.length];
+        for( int i=0; i<_types.length; i++ )
+            ts[i] = _types[i].meet(tt._types[i]);
+        return make(ts);
+    }
+
+    @Override public SONTypeTuple dual() {
+        if( this==TOP ) return BOT;
+        if( this==BOT ) return TOP;
+        SONType[] ts = new SONType[_types.length];
+        for( int i=0; i<_types.length; i++ )
+            ts[i] = _types[i].dual();
+        return make(ts);
+    }
+
+    @Override
+    public SONType glb() {
+        SONType[] ts = new SONType[_types.length];
+        for( int i=0; i<_types.length; i++ )
+            ts[i] = _types[i].glb();
+        return make(ts);
+    }
+
+    public SONType ret() { assert _types.length==3; return _types[2]; }
+
+    @Override public String str() { return print(new SB()).toString(); }
+
+    @Override public SB print(SB sb) {
+        if( this==TOP ) return sb.p("[TOP]");
+        if( this==BOT ) return sb.p("[BOT]");
+        sb.p("[  ");
+        for( SONType t : _types )
+            t.print(sb).p(", ");
+        return sb.unchar(2).p("]");
+    }
+    @Override public SB gprint(SB sb) {
+        if( this==TOP ) return sb.p("[TOP]");
+        if( this==BOT ) return sb.p("[BOT]");
+        sb.p("[  ");
+        for( SONType t : _types )
+            t.gprint(sb).p(", ");
+        return sb.unchar(2).p("]");
+    }
+
+    @Override
+    int hash() {
+        int sum = 0;
+        if( _types!=null ) for( SONType type : _types ) sum ^= type.hashCode();
+        return sum;
+    }
+
+    @Override
+    boolean eq( SONType t ) {
+        SONTypeTuple tt = (SONTypeTuple)t; // Contract
+        if( _types==null && tt._types==null ) return true;
+        if( _types==null || tt._types==null ) return false;
+        if( _types.length != tt._types.length ) return false;
+        for( int i=0; i<_types.length; i++ )
+            if( _types[i]!=tt._types[i] )
+                return false;
+        return true;
+    }
+
+
+}
diff --git a/seaofnodes/src/test/java/com/compilerprogramming/ezlang/compiler/Main.java b/seaofnodes/src/test/java/com/compilerprogramming/ezlang/compiler/Main.java
new file mode 100644
index 0000000..8b8400f
--- /dev/null
+++ b/seaofnodes/src/test/java/com/compilerprogramming/ezlang/compiler/Main.java
@@ -0,0 +1,19 @@
+package com.compilerprogramming.ezlang.compiler;
+
+import com.compilerprogramming.ezlang.compiler.codegen.CodeGen;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+public class Main {
+    public static final String PORTS = "com.compilerprogramming.ezlang.compiler.nodes.cpus";
+    // 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]));
+        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/seaofnodes/src/test/java/com/compilerprogramming/ezlang/compiler/Simple.java b/seaofnodes/src/test/java/com/compilerprogramming/ezlang/compiler/Simple.java
new file mode 100644
index 0000000..eb5c5d2
--- /dev/null
+++ b/seaofnodes/src/test/java/com/compilerprogramming/ezlang/compiler/Simple.java
@@ -0,0 +1,304 @@
+package com.compilerprogramming.ezlang.compiler;
+
+import com.compilerprogramming.ezlang.compiler.codegen.CodeGen;
+import com.compilerprogramming.ezlang.compiler.print.*;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.io.InputStreamReader;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.util.stream.Collectors;
+
+public class Simple {
+    public static final String PORTS = "com.seaofnodes.simple.node.cpus";
+
+    static final int DUMP_AFTER_PARSE        = 1<<0;
+    static final int DUMP_AFTER_OPTO         = 1<<1;
+    static final int DUMP_AFTER_TYPE_CHECK   = 1<<2;
+    static final int DUMP_AFTER_INSTR_SELECT = 1<<3;
+    static final int DUMP_AFTER_GCM          = 1<<4;
+    static final int DUMP_AFTER_LOCAL_SCHED  = 1<<5;
+    static final int DUMP_AFTER_REG_ALLOC    = 1<<6;
+    static final int DUMP_AFTER_ENCODE       = 1<<7;
+
+    static final int DUMP_FINAL              = 1<<16;
+
+    static final int DUMP_DOT                = 1<<29;
+    static final int DUMP_PASS_NAME          = 1<<30;
+    static final int DUMP_AFTER_ALL          = 0xff | DUMP_PASS_NAME;
+    static final int DUMP_DOT_AFTER_ALL      = 0xff | DUMP_DOT;
+
+    static final int DUMP_DISASSEMBLE        = 1<<31;
+
+    static String system_cpu = null;
+    static String system_abi = null;
+
+    static void usage() {
+        System.out.println(
+"""
+simple <input-file> [options] [--eval|--run ...]
+Options:
+  --dump                     - dump final intermediate representation
+  --dump-after-parse         - dump intermediate representation after parse
+  --dump-after-opto          - dump intermediate representation after opto pass
+  --dump-after-type-check    - dump intermediate representation after type check pass
+  --dump-after-instr-select  - dump intermediate representation after instrution selection pass
+  --dump-after-gcm           - dump intermediate representation after GCM pass
+  --dump-after-local-sched   - dump intermediate representation after local scheduling pass
+  --dump-after-reg-alloc     - dump intermediate representation after register allocation pass
+  --dump-after-encode        - dump intermediate representation after encoding pass
+  --dump-after-all           - dump intermediate representation after all passes
+  --dot                      - dump grapical representation of intermediate code into *.dot file(s)
+  -S                         - dump generated assembler code
+  --eval ...                 - evaluate the compiled code in emulator
+  --run ...                  - run the compiled code natively
+  --dump-size                - print the size of generated code
+  --dump-time                - print compilation and execution times
+  --cpu <cpu-name>           - use specific CPU (x86_64_v2, riscv, arm)
+  --abi <abi-name>           - use speific ABI variant (SystemV)
+  --target                   - print native CPU and ABI
+  --version
+  --help
+"""
+);
+        System.exit(0);
+    }
+
+    static void bad_usage() {
+        System.err.println("ERROR: Invalid usage (use --help)");
+        System.exit(1);
+    }
+
+    static void target() {
+        if (system_cpu == null || system_abi == null) {
+            System.err.println("ERROR: Unknown target");
+            System.exit(1);
+        }
+        System.out.print(system_cpu);
+        System.out.print("-");
+        System.out.println(system_abi);
+        System.exit(0);
+    }
+
+    static void dump(CodeGen code, int dump, int pass) {
+        if ((dump & pass) != 0) {
+            if ((dump & DUMP_DOT) != 0) {
+                String fn = switch (pass) {
+                    case DUMP_AFTER_PARSE        -> "01-parse.dot";
+                    case DUMP_AFTER_OPTO         -> "02-opto.dot";
+                    case DUMP_AFTER_TYPE_CHECK   -> "03-type_check.dot";
+                    case DUMP_AFTER_INSTR_SELECT -> "04-instr_select.dot";
+                    case DUMP_AFTER_GCM          -> "05-gcm.dot";
+                    case DUMP_AFTER_LOCAL_SCHED  -> "06-local_sched.dot";
+                    case DUMP_AFTER_REG_ALLOC    -> "07-reg_allos.dot";
+                    case DUMP_AFTER_ENCODE       -> "08-local_sched.dot";
+                    case DUMP_FINAL              -> "09-final.dot";
+                    default                      -> throw Utils.TODO();
+                };
+
+                try {
+                    Files.writeString(Path.of(fn),
+                        new GraphVisualizer().generateDotOutput(code._stop, null, null));
+                } catch(IOException e) {
+                    System.err.println("ERROR: Cannot write DOT file");
+                    System.exit(1);
+                }
+            } else {
+                if ((dump & DUMP_PASS_NAME) != 0) {
+                    System.err.println(switch (pass) {
+                        case DUMP_AFTER_PARSE        -> "After Parse:";
+                        case DUMP_AFTER_OPTO         -> "After OPTO:";
+                        case DUMP_AFTER_TYPE_CHECK   -> "After Type Check:";
+                        case DUMP_AFTER_INSTR_SELECT -> "After Instruction Selection:";
+                        case DUMP_AFTER_GCM          -> "After GCM:";
+                        case DUMP_AFTER_LOCAL_SCHED  -> "After Local Scheduling:";
+                        case DUMP_AFTER_REG_ALLOC    -> "After Register Allocation:";
+                        case DUMP_AFTER_ENCODE       -> "After Instruction Encoding:";
+                        case DUMP_FINAL              -> "Final:";
+                        default                      -> throw Utils.TODO();
+                    });
+                }
+
+                System.err.println(IRPrinter.prettyPrint(code._stop, 9999));
+            }
+        }
+    }
+
+    static void print_compilation_times(CodeGen code) {
+        double t, total = 0;
+
+        total += t = code._tParse / 1e3;
+        System.out.println(String.format("Parsing Time:               %.3f sec", t));
+        total += t = code._tOpto / 1e3;
+        System.out.println(String.format("Optimization Time:          %.3f sec", t));
+        total += t = code._tTypeCheck / 1e3;
+        System.out.println(String.format("Type Checking Time:         %.3f sec", t));
+        total += t = code._tInsSel / 1e3;
+        System.out.println(String.format("Instruction Selection Time: %.3f sec", t));
+        total += t = code._tGCM / 1e3;
+        System.out.println(String.format("GCM Time:                   %.3f sec", t));
+        total += t = code._tLocal / 1e3;
+        System.out.println(String.format("Local Scheduling Time:      %.3f sec", t));
+        total += t = code._tRegAlloc / 1e3;
+        System.out.println(String.format("Register Allocation Time:   %.3f sec", t));
+        total += t = code._tEncode / 1e3;
+        System.out.println(String.format("Instruction Encoding Time:  %.3f sec", t));
+        System.out.println(String.format("TOTAL COMPILATION TIME:     %.3f sec", total));
+    }
+
+    static String getInput() {
+        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
+        return reader.lines().collect(Collectors.joining("\n"));
+    }
+
+    public static void main(String[] args) throws Exception {
+        String input_filename = null;
+        boolean use_stdin = false;
+        boolean do_eval = false;
+        boolean do_run = false;
+        boolean do_codegen = false;
+        boolean do_print_size = false;
+        boolean do_print_time = false;
+        int dump = 0;
+        int first_arg = 0;
+        String src = null;
+        String cpu = null;
+        String abi = null;
+
+        // TODO: autodetect
+        system_cpu = "x86_64_v2";
+        system_abi = "SystemV";
+
+        // Parse command line
+loop:   for (int i = 0; i < args.length; i++) {
+            String arg = args[i];
+            if (arg.charAt(0) == '-') {
+                switch (arg) {
+                    case "-":                         use_stdin = true; break;
+                    case "--dump":                    dump |= DUMP_FINAL; break;
+                    case "--dump-after-parse":        dump |= DUMP_AFTER_PARSE; break;
+                    case "--dump-after-opto":         dump |= DUMP_AFTER_OPTO; break;
+                    case "--dump-after-type-check":   dump |= DUMP_AFTER_TYPE_CHECK; break;
+                    case "--dump-after-instr-select": dump |= DUMP_AFTER_INSTR_SELECT; break;
+                    case "--dump-after-gcm":          dump |= DUMP_AFTER_GCM; break;
+                    case "--dump-after-local-sched":  dump |= DUMP_AFTER_LOCAL_SCHED; break;
+                    case "--dump-after-reg-alloc":    dump |= DUMP_AFTER_REG_ALLOC; break;
+                    case "--dump-after-encode":       dump |= DUMP_AFTER_ENCODE; break;
+                    case "--dump-after-all":          dump |= DUMP_AFTER_ALL; break;
+                    case "--dot":                     dump |= DUMP_DOT; break;
+                    case "-S":                        dump |= DUMP_DISASSEMBLE; break;
+                    case "--eval":                    do_eval = true; first_arg = i + 1; break loop;
+                    case "--run":                     do_run = true; first_arg = i + 1; break loop;
+                    case "--dump-size":               do_print_size = true; break;
+                    case "--dump-time":               do_print_time = true; break;
+                    case "--cpu":                     if (cpu != null
+                                                       || i + 1 >= args.length
+                                                       || args[i + 1].charAt(0) == '-') bad_usage();
+                                                      cpu = args[i + 1];
+                                                      i++;
+                                                      break;
+                    case "--abi":                     if (abi != null
+                                                       || i + 1 >= args.length
+                                                       || args[i + 1].charAt(0) == '-') bad_usage();
+                                                      abi = args[i + 1];
+                                                      i++;
+                                                      break;
+                    case "--target":                  target();
+                    case "--version":                 throw Utils.TODO();
+                    case "--help":                    usage();
+                    default:                          bad_usage();
+                }
+            } else {
+                if (input_filename != null || use_stdin) bad_usage();
+                input_filename = arg;
+            }
+        }
+
+        if (input_filename == null && !use_stdin) {
+            System.err.println("ERROR: no input file' (use --help)");
+            System.exit(1);
+        }
+
+        if (do_run || (dump & DUMP_DISASSEMBLE) != 0 || do_print_size) {
+            if (cpu == null) cpu = system_cpu;
+            if (abi == null) abi = system_abi;
+            if (cpu == null || abi == null) {
+                System.err.println("ERROR: Cannot compile for unknown target");
+                System.exit(1);
+            }
+            do_codegen = true;
+        }
+
+        // Read input file
+        try {
+            src = use_stdin ? getInput() : Files.readString(Path.of(input_filename));
+        } catch(IOException e) {
+            System.err.println("ERROR: Cannot read input file");
+            System.exit(1);
+        }
+
+        // Compilation pipeline
+        CodeGen code = new CodeGen(src);
+
+        code.parse();
+        dump(code, dump, DUMP_AFTER_PARSE);
+
+        code.opto();
+        dump(code, dump, DUMP_AFTER_OPTO);
+
+        code.typeCheck();
+        dump(code, dump, DUMP_AFTER_TYPE_CHECK);
+
+        if (do_codegen) {
+            code.instSelect(PORTS, cpu, abi);
+            dump(code, dump, DUMP_AFTER_INSTR_SELECT);
+        }
+
+        code.GCM();
+        dump(code, dump, DUMP_AFTER_GCM);
+
+        code.localSched();
+        dump(code, dump, DUMP_AFTER_LOCAL_SCHED);
+
+        if (do_codegen) {
+            code.regAlloc();
+            dump(code, dump, DUMP_AFTER_REG_ALLOC);
+
+            code.encode();
+            dump(code, dump, DUMP_AFTER_ENCODE);
+        }
+
+        dump(code, dump, DUMP_FINAL);
+
+        if (do_codegen && (dump & DUMP_DISASSEMBLE) != 0) {
+            System.out.println(code.asm());
+        }
+
+        if (do_print_size) {
+             System.out.println(String.format("Code Size: %d", code._encoding._bits.size()));
+        }
+
+        if (do_print_time) {
+            print_compilation_times(code);
+        }
+
+//        if (do_eval) {
+//            // TODO: Support for evaluation of functions with different argument numbers and types
+//            long t = System.currentTimeMillis();
+//            long arg = (first_arg < args.length) ? Integer.valueOf(args[first_arg]) : 0;
+//            System.out.println(Eval2.eval(code, arg, 100000));
+//            if (do_print_time) {
+//                System.out.println(String.format("EXECUTION TIME:             %.3f sec",
+//                    (System.currentTimeMillis() - t) / 1e3));
+//            }
+//        } else
+        if (do_run) {
+            if (cpu != system_cpu || abi != system_abi) {
+                System.err.println("ERROR: cannot run code on not native target");
+                System.exit(1);
+            }
+            throw Utils.TODO();
+        }
+    }
+}
diff --git a/seaofnodes/src/test/java/com/compilerprogramming/ezlang/compiler/TestSONTypes.java b/seaofnodes/src/test/java/com/compilerprogramming/ezlang/compiler/TestSONTypes.java
new file mode 100644
index 0000000..6584c71
--- /dev/null
+++ b/seaofnodes/src/test/java/com/compilerprogramming/ezlang/compiler/TestSONTypes.java
@@ -0,0 +1,221 @@
+package com.compilerprogramming.ezlang.compiler;
+
+import com.compilerprogramming.ezlang.compiler.codegen.CodeGen;
+import org.junit.Test;
+
+import static com.compilerprogramming.ezlang.compiler.Main.PORTS;
+import static org.junit.Assert.assertEquals;
+
+public class TestSONTypes {
+
+    String compileSrc(String src) {
+        var compiler = new CodeGen(src);
+        compiler.parse();
+        return null;
+    }
+
+    @Test
+    public void test1() {
+        String src = """
+struct T { var i: Int }
+""";
+        compileSrc(src);
+    }
+
+    @Test
+    public void test2() {
+        String src = """
+struct T { var i: S }
+struct S { var i: Int }
+""";
+        compileSrc(src);
+    }
+
+    @Test
+    public void test3() {
+        String src = """
+struct T { var next: T? }
+""";
+        compileSrc(src);
+    }
+
+    @Test
+    public void test4() {
+        String src = """
+struct T { var arr: [Int] }
+""";
+        compileSrc(src);
+    }
+
+    @Test
+    public void test5() {
+        String src = """
+func foo() {}
+""";
+        compileSrc(src);
+    }
+
+    @Test
+    public void test6() {
+        String src = """
+func foo(a: Int) {}
+""";
+        compileSrc(src);
+    }
+
+
+    @Test
+    public void test7() {
+        String src = """
+func foo(a: Int, b: Int)->Int {}
+""";
+        compileSrc(src);
+    }
+
+    @Test
+    public void test8() {
+        String src = """
+func foo(a: Int, b: Int)->[Int] {}
+""";
+        compileSrc(src);
+    }
+
+    @Test
+    public void test9() {
+        String src = """
+func foo(a: Int, b: Int)->[Int]? {}
+""";
+        compileSrc(src);
+    }
+
+    @Test
+    public void test10() {
+        String src = """
+func foo(a: Int, b: Int)->Int { 
+  return a+b;
+}
+""";
+        compileSrc(src);
+    }
+
+    @Test
+    public void test11() {
+        String src = """
+func foo()->[Int] { 
+  return new [Int]{};
+}
+""";
+        compileSrc(src);
+    }
+
+    @Test
+    public void test12() {
+        String src = """
+func foo()->[Int] { 
+  return new [Int]{1};
+}
+""";
+        compileSrc(src);
+    }
+
+    @Test
+    public void test13() {
+        String src = """
+func foo()->[Int] { 
+  return new [Int]{42,84};
+}
+""";
+        compileSrc(src);
+    }
+
+    @Test
+    public void test14() {
+        String src = """
+struct T { var i: Int }
+func foo()->T { 
+  return new T{i=1};
+}
+""";
+        compileSrc(src);
+    }
+
+    @Test
+    public void test15() {
+        String src = """
+struct T { var i: Int; var j: Int }
+func foo()->T { 
+  return new T{i=23, j=32};
+}
+""";
+        compileSrc(src);
+    }
+
+    @Test
+    public void test16() {
+        String src = """
+func foo()->Int { 
+  return new [Int]{42,84}[1];
+}
+""";
+        compileSrc(src);
+    }
+
+    @Test
+    public void test17() {
+        String src = """
+struct T { var i: Int; var j: Int }
+func foo()->Int { 
+  return new T{i=23, j=32}.j;
+}
+""";
+        compileSrc(src);
+    }
+
+    @Test
+    public void test18() {
+        String src = """
+func bar()->Int { return 1 }
+func foo()->Int { 
+  return bar();
+}
+""";
+        compileSrc(src);
+    }
+
+    @Test
+    public void test19() {
+        String src = """
+func foo()->Int { 
+  var x = 1
+  return x
+}
+""";
+        compileSrc(src);
+    }
+
+        static void testCPU( String src, String cpu, String os, int spills, String stop ) {
+        CodeGen code = new CodeGen(src);
+        code.parse().opto().typeCheck().instSelect(PORTS,cpu,os).GCM().localSched().regAlloc().encode();
+        int delta = spills>>3;
+        if( delta==0 ) delta = 1;
+        assertEquals("Expect spills:",spills,code._regAlloc._spillScaled,delta);
+        if( stop != null )
+            assertEquals(stop, code._stop.toString());
+        System.out.println(code.asm());
+    }
+
+    private static void testAllCPUs( String src, int spills, String stop ) {
+        testCPU(src,"x86_64_v2", "SystemV",spills,stop);
+        testCPU(src,"riscv"    , "SystemV",spills,stop);
+        testCPU(src,"arm"      , "SystemV",spills,stop);
+    }
+
+    @Test
+    public void test101() {
+        testAllCPUs("""
+                func main()->Int {
+                    return 42
+                }
+                """, 0, null);
+    }
+}
diff --git a/types/src/main/java/com/compilerprogramming/ezlang/types/Type.java b/types/src/main/java/com/compilerprogramming/ezlang/types/Type.java
index 1233a64..05cbca6 100644
--- a/types/src/main/java/com/compilerprogramming/ezlang/types/Type.java
+++ b/types/src/main/java/com/compilerprogramming/ezlang/types/Type.java
@@ -140,6 +140,7 @@ public int getFieldIndex(String name) {
             return  fieldNames.indexOf(name);
         }
         public int numFields() { return fieldNames.size(); }
+        public String getFieldName(int index) { return fieldNames.get(index); }
         public void complete() { pending = false; }
     }
 

From d09041afe11c921e464026436f55b69ebe2cee21 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Sat, 29 Mar 2025 11:35:31 +0000
Subject: [PATCH 045/119] #4 test case

---
 .../ezlang/semantic/TestSemaAssignTypes.java    | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/semantic/src/test/java/com/compilerprogramming/ezlang/semantic/TestSemaAssignTypes.java b/semantic/src/test/java/com/compilerprogramming/ezlang/semantic/TestSemaAssignTypes.java
index 91f6700..a317c12 100644
--- a/semantic/src/test/java/com/compilerprogramming/ezlang/semantic/TestSemaAssignTypes.java
+++ b/semantic/src/test/java/com/compilerprogramming/ezlang/semantic/TestSemaAssignTypes.java
@@ -5,6 +5,7 @@
 import com.compilerprogramming.ezlang.parser.Parser;
 import com.compilerprogramming.ezlang.types.TypeDictionary;
 import org.junit.Assert;
+import org.junit.Ignore;
 import org.junit.Test;
 
 public class TestSemaAssignTypes {
@@ -420,4 +421,20 @@ func foo()
 """;
         analyze(src, "foo", "func foo()");
     }
+
+    @Ignore
+    @Test(expected = CompilerException.class)
+    public void test23() {
+        String src = """
+    func bar(arg: Int)->Int {
+        if (arg)
+            return 42;
+        return 0;
+    }
+    func foo()->Int {
+        return bar();
+    }
+""";
+        analyze(src, "foo", "func foo()->Int");
+    }
 }

From 23ab1b10630ae85f869735c3ad4a21df5ed54eed Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Sat, 29 Mar 2025 11:36:31 +0000
Subject: [PATCH 046/119] Fixes and refactor

---
 .../ezlang/compiler/CompiledFunction.java           | 13 ++-----------
 .../com/compilerprogramming/ezlang/parser/AST.java  | 11 ++++++-----
 .../compilerprogramming/ezlang/parser/Parser.java   |  5 ++++-
 .../ezlang/compiler/CompiledFunction.java           | 13 ++-----------
 .../ezlang/compiler/Compiler.java                   | 11 ++++-------
 .../compilerprogramming/ezlang/compiler/Simple.java |  2 +-
 .../ezlang/semantic/SemaAssignTypes.java            |  4 ++--
 .../ezlang/compiler/CompiledFunction.java           | 13 ++-----------
 8 files changed, 23 insertions(+), 49 deletions(-)

diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
index 5d237e0..19a4818 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
@@ -175,20 +175,11 @@ private void compileStatement(AST.Stmt statement) {
     }
 
     private void compileAssign(AST.AssignStmt assignStmt) {
-        boolean indexedLhs = false;
-        if (!(assignStmt.lhs instanceof AST.NameExpr))
-            indexedLhs = compileExpr(assignStmt.lhs);
         boolean indexedRhs = compileExpr(assignStmt.rhs);
         if (indexedRhs)
             codeIndexedLoad();
-        if (indexedLhs)
-            codeIndexedStore();
-        else if (assignStmt.lhs instanceof AST.NameExpr symbolExpr) {
-            Symbol.VarSymbol varSymbol = (Symbol.VarSymbol) symbolExpr.symbol;
-            codeMove(pop(), new Operand.LocalRegisterOperand(registerPool.getReg(varSymbol.regNumber), varSymbol));
-        }
-        else
-            throw new CompilerException("Invalid assignment expression: " + assignStmt.lhs);
+        Symbol.VarSymbol varSymbol = (Symbol.VarSymbol) assignStmt.nameExpr.symbol;
+        codeMove(pop(), new Operand.LocalRegisterOperand(registerPool.getReg(varSymbol.regNumber), varSymbol));
     }
 
     private void compileExprStmt(AST.ExprStmt exprStmt) {
diff --git a/parser/src/main/java/com/compilerprogramming/ezlang/parser/AST.java b/parser/src/main/java/com/compilerprogramming/ezlang/parser/AST.java
index 1fbb4f7..81a0015 100644
--- a/parser/src/main/java/com/compilerprogramming/ezlang/parser/AST.java
+++ b/parser/src/main/java/com/compilerprogramming/ezlang/parser/AST.java
@@ -731,16 +731,17 @@ public void accept(ASTVisitor visitor) {
         }
     }
 
+    /* name = value */
     public static class AssignStmt extends Stmt {
-        public final Expr lhs;
+        public final NameExpr nameExpr;
         public final Expr rhs;
-        public AssignStmt(Expr lhs, Expr rhs) {
-            this.lhs = lhs;
+        public AssignStmt(NameExpr nameExpr, Expr rhs) {
+            this.nameExpr = nameExpr;
             this.rhs = rhs;
         }
         @Override
         public StringBuilder toStr(StringBuilder sb) {
-            lhs.toStr(sb);
+            nameExpr.toStr(sb);
             sb.append(" = ");
             rhs.toStr(sb);
             return sb;
@@ -751,7 +752,7 @@ public void accept(ASTVisitor visitor) {
             visitor = visitor.visit(this, true);
             if (visitor == null)
                 return;
-            lhs.accept(visitor);
+            nameExpr.accept(visitor);
             rhs.accept(visitor);
             visitor.visit(this, false);
         }
diff --git a/parser/src/main/java/com/compilerprogramming/ezlang/parser/Parser.java b/parser/src/main/java/com/compilerprogramming/ezlang/parser/Parser.java
index f7b4951..0d95fcb 100644
--- a/parser/src/main/java/com/compilerprogramming/ezlang/parser/Parser.java
+++ b/parser/src/main/java/com/compilerprogramming/ezlang/parser/Parser.java
@@ -253,8 +253,11 @@ private AST.Stmt parseAssign(Lexer lexer) {
             else if (lhs instanceof AST.GetFieldExpr getFieldExpr) {
                 return new AST.ExprStmt(new AST.SetFieldExpr(getFieldExpr.object, getFieldExpr.fieldName, rhs));
             }
+            else if (lhs instanceof AST.NameExpr nameExpr) {
+                return new AST.AssignStmt(nameExpr, rhs);
+            }
+            else throw new CompilerException("Expected a name, expr[] or expr.field");
         }
-        return new AST.AssignStmt(lhs, rhs);
     }
 
     private AST.Expr parseBool(Lexer lexer) {
diff --git a/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java b/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
index 0e25924..9433d5c 100644
--- a/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
+++ b/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
@@ -139,20 +139,11 @@ private void compileStatement(AST.Stmt statement) {
     }
 
     private void compileAssign(AST.AssignStmt assignStmt) {
-        boolean indexedLhs = false;
-        if (!(assignStmt.lhs instanceof AST.NameExpr))
-            indexedLhs = compileExpr(assignStmt.lhs);
         boolean indexedRhs = compileExpr(assignStmt.rhs);
         if (indexedRhs)
             codeIndexedLoad();
-        if (indexedLhs)
-            codeIndexedStore();
-        else if (assignStmt.lhs instanceof AST.NameExpr symbolExpr) {
-            Symbol.VarSymbol varSymbol = (Symbol.VarSymbol) symbolExpr.symbol;
-            code(new Instruction.Move(pop(), new Operand.LocalRegisterOperand(varSymbol.regNumber, varSymbol.name)));
-        }
-        else
-            throw new CompilerException("Invalid assignment expression: " + assignStmt.lhs);
+        Symbol.VarSymbol varSymbol = (Symbol.VarSymbol) assignStmt.nameExpr.symbol;
+        code(new Instruction.Move(pop(), new Operand.LocalRegisterOperand(varSymbol.regNumber, varSymbol.name)));
     }
 
     private void compileExprStmt(AST.ExprStmt exprStmt) {
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java
index 4798830..e19a453 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java
@@ -712,7 +712,9 @@ private Node compileStatement(AST.Stmt statement) {
             case AST.VarStmt letStmt -> {
                 return compileLet(letStmt);
             }
-            case AST.VarDeclStmt varDeclStmt -> {}
+            case AST.VarDeclStmt varDeclStmt -> {
+                return ZERO;
+            }
             case AST.IfElseStmt ifElseStmt -> {
                 return compileIf(ifElseStmt);
             }
@@ -736,7 +738,6 @@ private Node compileStatement(AST.Stmt statement) {
             }
             default -> throw new IllegalStateException("Unexpected value: " + statement);
         }
-        throw new CompilerException("Not yet implemented");
     }
 
     private ScopeNode jumpTo(ScopeNode toScope) {
@@ -847,7 +848,6 @@ private Node compileWhile(AST.WhileStmt whileStmt) {
 
         // At exit the false control is the current control, and
         // the scope is the exit scope after the exit test.
-        _xScopes.pop();
         _xScopes.push(exit);
         _scope = exit;
         return ZERO;
@@ -855,8 +855,6 @@ private Node compileWhile(AST.WhileStmt whileStmt) {
 
     private Node compileIf(AST.IfElseStmt ifElseStmt) {
         var pred = compileExpr(ifElseStmt.condition).keep();
-        pred.keep();
-
         // IfNode takes current control and predicate
         Node ifNode = new IfNode(ctrl(), pred).peephole();
         // Setup projection nodes
@@ -914,8 +912,7 @@ private Node compileExprStmt(AST.ExprStmt exprStmt) {
     }
 
     private Node compileAssign(AST.AssignStmt assignStmt) {
-        if (!(assignStmt.lhs instanceof AST.NameExpr nameExpr))
-            throw new CompilerException("Assignment requires a nameExpr");
+        var nameExpr = assignStmt.nameExpr;
         String name = nameExpr.name;
         if (!(nameExpr.symbol instanceof Symbol.VarSymbol varSymbol))
             throw new CompilerException("Assignment requires a variable name");
diff --git a/seaofnodes/src/test/java/com/compilerprogramming/ezlang/compiler/Simple.java b/seaofnodes/src/test/java/com/compilerprogramming/ezlang/compiler/Simple.java
index eb5c5d2..c649fbd 100644
--- a/seaofnodes/src/test/java/com/compilerprogramming/ezlang/compiler/Simple.java
+++ b/seaofnodes/src/test/java/com/compilerprogramming/ezlang/compiler/Simple.java
@@ -11,7 +11,7 @@
 import java.util.stream.Collectors;
 
 public class Simple {
-    public static final String PORTS = "com.seaofnodes.simple.node.cpus";
+    public static final String PORTS = "com.compilerprogramming.ezlang.compiler.nodes.cpus";
 
     static final int DUMP_AFTER_PARSE        = 1<<0;
     static final int DUMP_AFTER_OPTO         = 1<<1;
diff --git a/semantic/src/main/java/com/compilerprogramming/ezlang/semantic/SemaAssignTypes.java b/semantic/src/main/java/com/compilerprogramming/ezlang/semantic/SemaAssignTypes.java
index 25abf39..0fa930d 100644
--- a/semantic/src/main/java/com/compilerprogramming/ezlang/semantic/SemaAssignTypes.java
+++ b/semantic/src/main/java/com/compilerprogramming/ezlang/semantic/SemaAssignTypes.java
@@ -385,9 +385,9 @@ public ASTVisitor visit(AST.ExprStmt exprStmt, boolean enter) {
     @Override
     public ASTVisitor visit(AST.AssignStmt assignStmt, boolean enter) {
         if (!enter) {
-            validType(assignStmt.lhs.type, false);
+            validType(assignStmt.nameExpr.type, false);
             validType(assignStmt.rhs.type, true);
-            checkAssignmentCompatible(assignStmt.lhs.type, assignStmt.rhs.type);
+            checkAssignmentCompatible(assignStmt.nameExpr.type, assignStmt.rhs.type);
         }
         return this;
     }
diff --git a/stackvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java b/stackvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
index 7be5198..ed83aca 100644
--- a/stackvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
+++ b/stackvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
@@ -111,20 +111,11 @@ private void compileStatement(AST.Stmt statement) {
     }
 
     private void compileAssign(AST.AssignStmt assignStmt) {
-        boolean indexedLhs = false;
-        if (!(assignStmt.lhs instanceof AST.NameExpr))
-            indexedLhs = compileExpr(assignStmt.lhs);
         boolean indexedRhs = compileExpr(assignStmt.rhs);
         if (indexedRhs)
             codeIndexedLoad();
-        if (indexedLhs)
-            codeIndexedStore();
-        else if (assignStmt.lhs instanceof AST.NameExpr symbolExpr) {
-            Symbol.VarSymbol varSymbol = (Symbol.VarSymbol) symbolExpr.symbol;
-            code(new Instruction.Store(varSymbol.regNumber));
-        }
-        else
-            throw new CompilerException("Invalid assignment expression: " + assignStmt.lhs);
+        Symbol.VarSymbol varSymbol = (Symbol.VarSymbol) assignStmt.nameExpr.symbol;
+        code(new Instruction.Store(varSymbol.regNumber));
     }
 
     private void compileExprStmt(AST.ExprStmt exprStmt) {

From fd1ae26fbcea33f1a15129203819f7efd178dc39 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Sat, 29 Mar 2025 22:47:24 +0000
Subject: [PATCH 047/119] Bug fix

---
 .../ezlang/interpreter/Interpreter.java       |  8 +-
 .../ezlang/interpreter/Interpreter.java       |  8 +-
 .../ezlang/interpreter/TestInterpreter.java   | 88 +++++++++++++++++++
 3 files changed, 96 insertions(+), 8 deletions(-)

diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/interpreter/Interpreter.java b/optvm/src/main/java/com/compilerprogramming/ezlang/interpreter/Interpreter.java
index 66de388..c85fa24 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/interpreter/Interpreter.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/interpreter/Interpreter.java
@@ -108,13 +108,13 @@ else if (cbrInst.condition() instanceof Operand.ConstantOperand constantOperand)
                     int reg = baseReg;
                     for (Operand arg: callInst.args()) {
                         if (arg instanceof Operand.RegisterOperand param) {
-                            execStack.stack[base + reg] = execStack.stack[base + param.frameSlot()];
+                            execStack.stack[reg] = execStack.stack[base + param.frameSlot()];
                         }
                         else if (arg instanceof Operand.ConstantOperand constantOperand) {
-                            execStack.stack[base + reg] = new Value.IntegerValue(constantOperand.value);
+                            execStack.stack[reg] = new Value.IntegerValue(constantOperand.value);
                         }
                         else if (arg instanceof Operand.NullConstantOperand) {
-                            execStack.stack[base + reg] = new Value.NullValue();
+                            execStack.stack[reg] = new Value.NullValue();
                         }
                         reg += 1;
                     }
@@ -193,7 +193,7 @@ else if (binaryInst.right() instanceof Operand.RegisterOperand registerOperand)
                             case "<": value = x < y ? 1: 0; break;
                             case ">": value = x > y ? 1 : 0; break;
                             case "<=": value = x <= y ? 1 : 0; break;
-                            case ">=": value = x <= y ? 1 : 0; break;
+                            case ">=": value = x >= y ? 1 : 0; break;
                             default: throw new IllegalStateException();
                         }
                         execStack.stack[base + binaryInst.result().frameSlot()] = new Value.IntegerValue(value);
diff --git a/registervm/src/main/java/com/compilerprogramming/ezlang/interpreter/Interpreter.java b/registervm/src/main/java/com/compilerprogramming/ezlang/interpreter/Interpreter.java
index 7771700..919a82e 100644
--- a/registervm/src/main/java/com/compilerprogramming/ezlang/interpreter/Interpreter.java
+++ b/registervm/src/main/java/com/compilerprogramming/ezlang/interpreter/Interpreter.java
@@ -108,13 +108,13 @@ else if (cbrInst.condition() instanceof Operand.ConstantOperand constantOperand)
                     int reg = baseReg;
                     for (Operand arg: callInst.args()) {
                         if (arg instanceof Operand.RegisterOperand param) {
-                            execStack.stack[base + reg] = execStack.stack[base + param.frameSlot()];
+                            execStack.stack[reg] = execStack.stack[base + param.frameSlot()];
                         }
                         else if (arg instanceof Operand.ConstantOperand constantOperand) {
-                            execStack.stack[base + reg] = new Value.IntegerValue(constantOperand.value);
+                            execStack.stack[reg] = new Value.IntegerValue(constantOperand.value);
                         }
                         else if (arg instanceof Operand.NullConstantOperand) {
-                            execStack.stack[base + reg] = new Value.NullValue();
+                            execStack.stack[reg] = new Value.NullValue();
                         }
                         reg += 1;
                     }
@@ -193,7 +193,7 @@ else if (binaryInst.right() instanceof Operand.RegisterOperand registerOperand)
                             case "<": value = x < y ? 1: 0; break;
                             case ">": value = x > y ? 1 : 0; break;
                             case "<=": value = x <= y ? 1 : 0; break;
-                            case ">=": value = x <= y ? 1 : 0; break;
+                            case ">=": value = x >= y ? 1 : 0; break;
                             default: throw new IllegalStateException();
                         }
                         execStack.stack[base + binaryInst.result().frameSlot()] = new Value.IntegerValue(value);
diff --git a/registervm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java b/registervm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java
index fcb93e2..78e4d48 100644
--- a/registervm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java
+++ b/registervm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java
@@ -244,4 +244,92 @@ func foo()->Int
         Assert.assertTrue(value instanceof Value.IntegerValue integerValue &&
                 integerValue.value == 1);
     }
+
+        @Test
+    public void testMergeSort() {
+        String src = """
+// based on the top-down version from https://en.wikipedia.org/wiki/Merge_sort
+// via https://github.com/SeaOfNodes/Simple
+func merge_sort(a: [Int], b: [int], n: Int) 
+{
+    copy_array(a, 0, n, b)
+    split_merge(a, 0, n, b)
+}
+
+func split_merge(b: [Int], begin: Int, end: Int, a: [Int])
+{
+    if (end - begin <= 1)
+        return;
+    var middle = (end + begin) / 2
+    split_merge(a, begin, middle, b)
+    split_merge(a, middle, end, b)
+    merge(b, begin, middle, end, a)
+}
+
+func merge(b: [Int], begin: Int, middle: Int, end: Int, a: [Int])
+{
+    var i = begin
+    var j = middle
+    var k = begin
+    while (k < end) {
+        // && and ||
+        var cond = 0
+        if (i < middle) {
+            if (j >= end)          cond = 1;
+            else if (a[i] <= a[j]) cond = 1;
+        }
+        if (cond)
+        {
+            b[k] = a[i]
+            i = i + 1
+        }
+        else
+        {
+            b[k] = a[j]
+            j = j + 1
+        }
+        k = k + 1
+    }
+}
+
+func copy_array(a: [Int], begin: Int, end: Int, b: [Int])
+{
+    var k = begin
+    while (k < end)
+    {
+        b[k] = a[k]
+        k = k + 1
+    }
+}
+
+func eq(a: [Int], b: [Int], n: Int)->Int
+{
+    var result = 1
+    var i = 0
+    while (i < n)
+    {
+        if (a[i] != b[i])
+        {
+            result = 0
+            break
+        }
+        i = i + 1
+    } 
+    return result
+}
+
+func main()->Int
+{
+    var a = new [Int]{10,9,8,7,6,5,4,3,2,1}
+    var b = new [Int]{ 0,0,0,0,0,0,0,0,0,0}
+    var expect = new [Int]{1,2,3,4,5,6,7,8,9,10}
+    merge_sort(a, b, 10)
+    return eq(a,expect,10)
+}
+""";
+        var value = compileAndRun(src, "main");
+        Assert.assertNotNull(value);
+        Assert.assertTrue(value instanceof Value.IntegerValue integerValue &&
+                integerValue.value == 1);
+    }
 }

From 6381f9b86daa3393e706304e33ce7840d18bdabf Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Sun, 30 Mar 2025 10:39:36 +0100
Subject: [PATCH 048/119] Bug fix - incorrect frame size calculation

---
 .../ezlang/compiler/ChaitinGraphColoringRegisterAllocator.java  | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ChaitinGraphColoringRegisterAllocator.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ChaitinGraphColoringRegisterAllocator.java
index a2a4840..93183b9 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ChaitinGraphColoringRegisterAllocator.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ChaitinGraphColoringRegisterAllocator.java
@@ -45,7 +45,7 @@ public Map<Integer, Integer> assignRegisters(CompiledFunction function, int numR
      * Frame size = max number of registers needed to execute the function
      */
     private int computeFrameSize(Map<Integer, Integer> assignments) {
-        return assignments.values().stream().mapToInt(k->k).max().orElse(0);
+        return assignments.values().stream().mapToInt(k->k).max().orElse(-1)+1;
     }
 
     /**

From ddb5e2ce942b8fec198343616744920aefcdfd24 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Sun, 30 Mar 2025 18:39:01 +0100
Subject: [PATCH 049/119] #16 Do a liveness check before inserting a phi

---
 .../ezlang/compiler/CompiledFunction.java     |   2 +-
 .../ezlang/compiler/EnterSSA.java             |  17 +-
 .../ezlang/compiler/TestIncrementalSSA.java   | 149 +++++++
 .../ezlang/compiler/TestSSATransform.java     | 376 +++++++++++++-----
 .../ezlang/interpreter/TestInterpreter.java   |  88 ++++
 5 files changed, 520 insertions(+), 112 deletions(-)

diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
index 19a4818..48526e6 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
@@ -17,7 +17,7 @@ public class CompiledFunction {
     public BasicBlock currentBlock;
     private BasicBlock currentBreakTarget;
     private BasicBlock currentContinueTarget;
-    private Type.TypeFunction functionType;
+    public Type.TypeFunction functionType;
     public final RegisterPool registerPool;
     private final TypeDictionary typeDictionary;
 
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/EnterSSA.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/EnterSSA.java
index fc1d275..83e7c24 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/EnterSSA.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/EnterSSA.java
@@ -43,6 +43,7 @@ public EnterSSA(CompiledFunction bytecodeFunction, EnumSet<Options> options) {
         }
         this.blocks = domTree.blocks;   // the blocks are ordered reverse post order
         findNonLocalNames();
+        new Liveness(bytecodeFunction); // EWe require liveness info to construct pruned ssa
         insertPhis();
         renameVars();
         bytecodeFunction.isSSA = true;
@@ -94,12 +95,16 @@ void insertPhis() {
                 while (b != null) {
                     visited.set(b.bid);
                     for (BasicBlock d: b.dominationFrontier) {
-                        if (d == function.exit) // The exit block does not need any phis as it has no instructions
-                            continue;
-                        // insert phi for x in d
-                        d.insertPhiFor(x);
-                        if (!visited.get(d.bid))
-                            worklist.push(d);
+                        // Perform a liveness check to avoid inserting
+                        // phi when variable is dead
+                        // Inserting dead phis causes problems during renaming
+                        // because there will not be a definition available
+                        if (d.liveIn.contains(x)) {
+                            // insert phi for x in d
+                            d.insertPhiFor(x);
+                            if (!visited.get(d.bid))
+                                worklist.push(d);
+                        }
                     }
                     b = worklist.pop();
                 }
diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestIncrementalSSA.java b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestIncrementalSSA.java
index 546425d..e4f9b93 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestIncrementalSSA.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestIncrementalSSA.java
@@ -520,5 +520,154 @@ func foo(x: Int,y: Int)->Int
 """, result);
     }
 
+    @Test
+    public void testSSA10() {
+        String src = """
+        func main()->Int {
+            var sum = 0
+            var i = 0
+            var j = 0
+            while (i < 5) {
+                j = 0
+                while (j < 5) {
+                    if (j % 2 == 0) 
+                        sum = sum + j
+                    j = j + 1    
+                }
+                i = i + 1    
+            }
+            return sum
+        }
+""";
+        String result = compileSrc(src);
+        Assert.assertEquals("""
+func main()->Int
+Reg #0 sum 0
+Reg #1 i 1
+Reg #2 j 2
+Reg #3 %t3 3
+Reg #4 i_1 1
+Reg #5 j_1 2
+Reg #6 %t6 6
+Reg #7 j_2 2
+Reg #8 %t8 8
+Reg #9 %t9 9
+Reg #10 %t10 10
+Reg #11 sum_1 0
+Reg #12 sum_2 0
+Reg #13 %t13 13
+Reg #14 j_3 2
+Reg #15 j_4 2
+Reg #16 sum_3 0
+Reg #17 sum_4 0
+Reg #18 %t18 18
+Reg #19 i_2 1
+Reg #20 i_3 1
+Reg #21 i_4 1
+L0:
+    sum = 0
+    i = 0
+    j = 0
+    goto  L2
+L2:
+    sum_3 = phi(sum, sum_1)
+    i_1 = phi(i, i_4)
+    %t3 = i_1<5
+    if %t3 goto L3 else goto L4
+L3:
+    j_1 = 0
+    goto  L5
+L5:
+    sum_1 = phi(sum_3, sum_4)
+    j_2 = phi(j_1, j_4)
+    %t6 = j_2<5
+    if %t6 goto L6 else goto L7
+L6:
+    %t8 = j_2%2
+    %t9 = %t8==0
+    if %t9 goto L8 else goto L9
+L8:
+    %t10 = sum_1+j_2
+    sum_2 = %t10
+    goto  L9
+L9:
+    sum_4 = phi(sum_1, sum_2)
+    %t13 = j_2+1
+    j_4 = %t13
+    goto  L5
+L7:
+    %t18 = i_1+1
+    i_4 = %t18
+    goto  L2
+L4:
+    ret sum_3
+    goto  L1
+L1:
+""", result);
+    }
+
+    @Test
+    public void testSSA17() {
+        String src = """
+func merge(begin: Int, middle: Int, end: Int)
+{
+    if (begin < end) {
+        var cond = 0
+        if (begin < middle) {
+            if (begin >= end)          cond = 1;
+        }
+        if (cond)
+        {
+            cond = 0
+        }
+    }
+}
+""";
+        String result = compileSrc(src);
+        Assert.assertEquals("""
+func merge(begin: Int,middle: Int,end: Int)
+Reg #0 begin 0
+Reg #1 middle 1
+Reg #2 end 2
+Reg #3 cond 3
+Reg #4 %t4 4
+Reg #5 %t5 5
+Reg #6 %t6 6
+Reg #7 cond_1 3
+Reg #8 cond_2 3
+Reg #9 cond_3 3
+Reg #10 cond_4 3
+L0:
+    arg begin
+    arg middle
+    arg end
+    %t4 = begin<end
+    if %t4 goto L2 else goto L3
+L2:
+    cond = 0
+    %t5 = begin<middle
+    if %t5 goto L4 else goto L5
+L4:
+    %t6 = begin>=end
+    if %t6 goto L6 else goto L7
+L6:
+    cond_1 = 1
+    goto  L7
+L7:
+    cond_3 = phi(cond, cond_1)
+    goto  L5
+L5:
+    cond_2 = phi(cond, cond_3)
+    if cond_2 goto L8 else goto L9
+L8:
+    cond_4 = 0
+    goto  L9
+L9:
+    goto  L3
+L3:
+    goto  L1
+L1:
+""", result);
+    }
 
 }
\ No newline at end of file
diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
index a605c93..c8b5d6a 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
@@ -462,10 +462,6 @@ func example14_66(p: Int, q: Int, r: Int, s: Int, t: Int) {
 L18:
     goto  L4
 L4:
-    l_10 = phi(l_1, l_9)
-    k_5 = phi(k_1, k_4)
-    j_4 = phi(j_1, j_3)
-    i_3 = phi(i_1, i_2)
     goto  L1
 L1:
 L19:
@@ -497,10 +493,6 @@ func example14_66(p: Int, q: Int, r: Int, s: Int, t: Int) {
     i_1 = i_0
     goto  L2
 L2:
-    l_10 = l_1
-    k_5 = k_1
-    j_4 = j_1
-    i_3 = i_1
     if 1 goto L3 else goto L4
 L3:
     if p_0 goto L5 else goto L6
@@ -549,10 +541,6 @@ func example14_66(p: Int, q: Int, r: Int, s: Int, t: Int) {
     %t18_0 = !t_0
     if %t18_0 goto L18 else goto L19
 L18:
-    l_10 = l_9
-    k_5 = k_4
-    j_4 = j_3
-    i_3 = i_2
     goto  L4
 L4:
     goto  L1
@@ -803,7 +791,6 @@ func foo()->Int {
     }
 
     @Test
-    @Ignore
     public void testInit() {
         // see issue #16
         String src = """
@@ -818,7 +805,81 @@ func foo(x: Int) {
                 }
                 """;
         String result = compileSrc(src);
-        System.out.println(result);
+        Assert.assertEquals("""
+func foo
+Before SSA
+==========
+L0:
+    arg x
+    goto  L2
+L2:
+    %t2 = x>0
+    if %t2 goto L3 else goto L4
+L3:
+    z = 5
+    %t3 = x==1
+    if %t3 goto L5 else goto L6
+L5:
+    %t4 = z+1
+    z = %t4
+    goto  L6
+L6:
+    %t5 = x-1
+    x = %t5
+    goto  L2
+L4:
+    goto  L1
+L1:
+After SSA
+=========
+L0:
+    arg x_0
+    goto  L2
+L2:
+    x_1 = phi(x_0, x_2)
+    %t2_0 = x_1>0
+    if %t2_0 goto L3 else goto L4
+L3:
+    z_0 = 5
+    %t3_0 = x_1==1
+    if %t3_0 goto L5 else goto L6
+L5:
+    %t4_0 = z_0+1
+    z_1 = %t4_0
+    goto  L6
+L6:
+    %t5_0 = x_1-1
+    x_2 = %t5_0
+    goto  L2
+L4:
+    goto  L1
+L1:
+After exiting SSA
+=================
+L0:
+    arg x_0
+    x_1 = x_0
+    goto  L2
+L2:
+    %t2_0 = x_1>0
+    if %t2_0 goto L3 else goto L4
+L3:
+    z_0 = 5
+    %t3_0 = x_1==1
+    if %t3_0 goto L5 else goto L6
+L5:
+    %t4_0 = z_0+1
+    z_1 = %t4_0
+    goto  L6
+L6:
+    %t5_0 = x_1-1
+    x_2 = %t5_0
+    x_1 = x_2
+    goto  L2
+L4:
+    goto  L1
+L1:
+""", result);
     }
 
     // http://users.csc.calpoly.edu/~akeen/courses/csc431/handouts/references/ssa_example.pdf
@@ -1897,24 +1958,23 @@ func main()->Int {
     j_0 = 0
     goto  L2
 L2:
-    j_1 = phi(j_0, j_2)
     i_1 = phi(i_0, i_3)
     b_1 = phi(b_0, b_2)
     a_1 = phi(a_0, a_2)
     %t4_0 = i_1<3
     if %t4_0 goto L3 else goto L4
 L3:
-    j_2 = 0
+    j_1 = 0
     goto  L5
 L5:
     i_2 = phi(i_1, i_4)
     a_2 = phi(a_1, a_3)
-    %t5_0 = j_2<2
+    %t5_0 = j_1<2
     if %t5_0 goto L6 else goto L7
 L6:
     %t6_0 = a_2+1
     a_3 = %t6_0
-    %t7_0 = j_2+1
+    %t7_0 = j_1+1
     i_4 = %t7_0
     goto  L5
 L7:
@@ -1935,7 +1995,6 @@ func main()->Int {
     b_0 = 0
     i_0 = 0
     j_0 = 0
-    j_1 = j_0
     i_1 = i_0
     b_1 = b_0
     a_1 = a_0
@@ -1944,17 +2003,17 @@ func main()->Int {
     %t4_0 = i_1<3
     if %t4_0 goto L3 else goto L4
 L3:
-    j_2 = 0
+    j_1 = 0
     i_2 = i_1
     a_2 = a_1
     goto  L5
 L5:
-    %t5_0 = j_2<2
+    %t5_0 = j_1<2
     if %t5_0 goto L6 else goto L7
 L6:
     %t6_0 = a_2+1
     a_3 = %t6_0
-    %t7_0 = j_2+1
+    %t7_0 = j_1+1
     i_4 = %t7_0
     i_2 = i_4
     a_2 = a_3
@@ -1964,7 +2023,6 @@ func main()->Int {
     b_2 = %t8_0
     %t9_0 = i_2+1
     i_3 = %t9_0
-    j_1 = j_2
     i_1 = i_3
     b_1 = b_2
     a_1 = a_2
@@ -2043,31 +2101,30 @@ func main()->Int {
     j_0 = 0
     goto  L2
 L2:
-    j_1 = phi(j_0, j_3)
     i_1 = phi(i_0, i_2)
     sum_1 = phi(sum_0, sum_2)
     %t3_0 = i_1<5
     if %t3_0 goto L3 else goto L4
 L3:
-    j_2 = 0
+    j_1 = 0
     goto  L5
 L5:
-    j_3 = phi(j_2, j_4)
+    j_2 = phi(j_1, j_3)
     sum_2 = phi(sum_1, sum_4)
-    %t4_0 = j_3<5
+    %t4_0 = j_2<5
     if %t4_0 goto L6 else goto L7
 L6:
-    %t5_0 = j_3%2
+    %t5_0 = j_2%2
     %t6_0 = %t5_0==0
     if %t6_0 goto L8 else goto L9
 L8:
-    %t7_0 = sum_2+j_3
+    %t7_0 = sum_2+j_2
     sum_3 = %t7_0
     goto  L9
 L9:
     sum_4 = phi(sum_2, sum_3)
-    %t8_0 = j_3+1
-    j_4 = %t8_0
+    %t8_0 = j_2+1
+    j_3 = %t8_0
     goto  L5
 L7:
     %t9_0 = i_1+1
@@ -2083,7 +2140,6 @@ func main()->Int {
     sum_0 = 0
     i_0 = 0
     j_0 = 0
-    j_1 = j_0
     i_1 = i_0
     sum_1 = sum_0
     goto  L2
@@ -2091,33 +2147,32 @@ func main()->Int {
     %t3_0 = i_1<5
     if %t3_0 goto L3 else goto L4
 L3:
-    j_2 = 0
-    j_3 = j_2
+    j_1 = 0
+    j_2 = j_1
     sum_2 = sum_1
     goto  L5
 L5:
-    %t4_0 = j_3<5
+    %t4_0 = j_2<5
     if %t4_0 goto L6 else goto L7
 L6:
-    %t5_0 = j_3%2
+    %t5_0 = j_2%2
     %t6_0 = %t5_0==0
     sum_4 = sum_2
     if %t6_0 goto L8 else goto L9
 L8:
-    %t7_0 = sum_2+j_3
+    %t7_0 = sum_2+j_2
     sum_3 = %t7_0
     sum_4 = sum_3
     goto  L9
 L9:
-    %t8_0 = j_3+1
-    j_4 = %t8_0
-    j_3 = j_4
+    %t8_0 = j_2+1
+    j_3 = %t8_0
+    j_2 = j_3
     sum_2 = sum_4
     goto  L5
 L7:
     %t9_0 = i_1+1
     i_2 = %t9_0
-    j_1 = j_3
     i_1 = i_2
     sum_1 = sum_2
     goto  L2
@@ -2205,34 +2260,33 @@ else if (i > j)
     j_0 = 0
     goto  L2
 L2:
-    j_1 = phi(j_0, j_3)
     i_1 = phi(i_0, i_2)
     a_1 = phi(a_0, a_2)
     %t3_0 = i_1<3
     if %t3_0 goto L3 else goto L4
 L3:
-    j_2 = 0
+    j_1 = 0
     goto  L5
 L5:
-    j_3 = phi(j_2, j_4)
+    j_2 = phi(j_1, j_3)
     a_2 = phi(a_1, a_6)
-    %t4_0 = j_3<3
+    %t4_0 = j_2<3
     if %t4_0 goto L6 else goto L7
 L6:
-    %t5_0 = i_1==j_3
+    %t5_0 = i_1==j_2
     if %t5_0 goto L8 else goto L9
 L8:
     %t6_0 = a_4+i_1
-    %t7_0 = %t6_0+j_3
+    %t7_0 = %t6_0+j_2
     a_5 = %t7_0
     goto  L10
 L10:
     a_6 = phi(a_5, a_4)
-    %t10_0 = j_3+1
-    j_4 = %t10_0
+    %t10_0 = j_2+1
+    j_3 = %t10_0
     goto  L5
 L9:
-    %t8_0 = i_1>j_3
+    %t8_0 = i_1>j_2
     if %t8_0 goto L11 else goto L12
 L11:
     %t9_0 = a_2-1
@@ -2255,7 +2309,6 @@ else if (i > j)
     a_0 = 0
     i_0 = 0
     j_0 = 0
-    j_1 = j_0
     i_1 = i_0
     a_1 = a_0
     goto  L2
@@ -2263,30 +2316,30 @@ else if (i > j)
     %t3_0 = i_1<3
     if %t3_0 goto L3 else goto L4
 L3:
-    j_2 = 0
-    j_3 = j_2
+    j_1 = 0
+    j_2 = j_1
     a_2 = a_1
     goto  L5
 L5:
-    %t4_0 = j_3<3
+    %t4_0 = j_2<3
     if %t4_0 goto L6 else goto L7
 L6:
-    %t5_0 = i_1==j_3
+    %t5_0 = i_1==j_2
     if %t5_0 goto L8 else goto L9
 L8:
     %t6_0 = a_4+i_1
-    %t7_0 = %t6_0+j_3
+    %t7_0 = %t6_0+j_2
     a_5 = %t7_0
     a_6 = a_5
     goto  L10
 L10:
-    %t10_0 = j_3+1
-    j_4 = %t10_0
-    j_3 = j_4
+    %t10_0 = j_2+1
+    j_3 = %t10_0
+    j_2 = j_3
     a_2 = a_6
     goto  L5
 L9:
-    %t8_0 = i_1>j_3
+    %t8_0 = i_1>j_2
     a_4 = a_2
     if %t8_0 goto L11 else goto L12
 L11:
@@ -2300,7 +2353,6 @@ else if (i > j)
 L7:
     %t11_0 = i_1+1
     i_2 = %t11_0
-    j_1 = j_3
     i_1 = i_2
     a_1 = a_2
     goto  L2
@@ -2389,21 +2441,20 @@ func main()->Int {
     j_0 = 0
     goto  L2
 L2:
-    j_1 = phi(j_0, j_3)
     i_1 = phi(i_0, i_2)
     count_1 = phi(count_0, count_2)
     %t3_0 = i_1<5
     if %t3_0 goto L3 else goto L4
 L3:
-    j_2 = 0
+    j_1 = 0
     goto  L5
 L5:
-    j_3 = phi(j_2, j_5, j_4)
+    j_2 = phi(j_1, j_4, j_3)
     count_2 = phi(count_1, count_2, count_3)
-    %t4_0 = j_3<5
+    %t4_0 = j_2<5
     if %t4_0 goto L6 else goto L7
 L6:
-    %t5_0 = i_1+j_3
+    %t5_0 = i_1+j_2
     %t6_0 = %t5_0>5
     if %t6_0 goto L8 else goto L9
 L8:
@@ -2413,17 +2464,17 @@ func main()->Int {
     i_2 = %t11_0
     goto  L2
 L9:
-    %t7_0 = i_1==j_3
+    %t7_0 = i_1==j_2
     if %t7_0 goto L10 else goto L11
 L10:
-    %t8_0 = j_3+1
-    j_5 = %t8_0
+    %t8_0 = j_2+1
+    j_4 = %t8_0
     goto  L5
 L11:
     %t9_0 = count_2+1
     count_3 = %t9_0
-    %t10_0 = j_3+1
-    j_4 = %t10_0
+    %t10_0 = j_2+1
+    j_3 = %t10_0
     goto  L5
 L4:
     ret count_1
@@ -2435,7 +2486,6 @@ func main()->Int {
     count_0 = 0
     i_0 = 0
     j_0 = 0
-    j_1 = j_0
     i_1 = i_0
     count_1 = count_0
     goto  L2
@@ -2443,16 +2493,16 @@ func main()->Int {
     %t3_0 = i_1<5
     if %t3_0 goto L3 else goto L4
 L3:
-    j_2 = 0
-    j_3 = j_2
+    j_1 = 0
+    j_2 = j_1
     count_2 = count_1
     goto  L5
 L5:
-    count_2_35 = count_2
-    %t4_0 = j_3<5
+    count_2_34 = count_2
+    %t4_0 = j_2<5
     if %t4_0 goto L6 else goto L7
 L6:
-    %t5_0 = i_1+j_3
+    %t5_0 = i_1+j_2
     %t6_0 = %t5_0>5
     if %t6_0 goto L8 else goto L9
 L8:
@@ -2460,26 +2510,25 @@ func main()->Int {
 L7:
     %t11_0 = i_1+1
     i_2 = %t11_0
-    j_1 = j_3
     i_1 = i_2
     count_1 = count_2
     goto  L2
 L9:
-    %t7_0 = i_1==j_3
+    %t7_0 = i_1==j_2
     if %t7_0 goto L10 else goto L11
 L10:
-    %t8_0 = j_3+1
-    j_5 = %t8_0
-    j_3 = j_5
-    count_2_34 = count_2
-    count_2 = count_2_34
+    %t8_0 = j_2+1
+    j_4 = %t8_0
+    j_2 = j_4
+    count_2_33 = count_2
+    count_2 = count_2_33
     goto  L5
 L11:
     %t9_0 = count_2+1
     count_3 = %t9_0
-    %t10_0 = j_3+1
-    j_4 = %t10_0
-    j_3 = j_4
+    %t10_0 = j_2+1
+    j_3 = %t10_0
+    j_2 = j_3
     count_2 = count_3
     goto  L5
 L4:
@@ -2562,33 +2611,32 @@ func main()->Int {
     j_0 = 0
     goto  L2
 L2:
-    j_1 = phi(j_0, j_3)
     i_1 = phi(i_0, i_2)
     innerSum_1 = phi(innerSum_0, innerSum_2)
     outerSum_1 = phi(outerSum_0, outerSum_2)
     %t4_0 = i_1<4
     if %t4_0 goto L3 else goto L4
 L3:
-    j_2 = 0
+    j_1 = 0
     goto  L5
 L5:
-    j_3 = phi(j_2, j_4)
+    j_2 = phi(j_1, j_3)
     innerSum_2 = phi(innerSum_1, innerSum_4)
-    %t5_0 = j_3<4
+    %t5_0 = j_2<4
     if %t5_0 goto L6 else goto L7
 L6:
-    %t6_0 = i_1+j_3
+    %t6_0 = i_1+j_2
     %t7_0 = %t6_0%2
     %t8_0 = %t7_0==0
     if %t8_0 goto L8 else goto L9
 L8:
-    %t9_0 = innerSum_2+j_3
+    %t9_0 = innerSum_2+j_2
     innerSum_3 = %t9_0
     goto  L9
 L9:
     innerSum_4 = phi(innerSum_2, innerSum_3)
-    %t10_0 = j_3+1
-    j_4 = %t10_0
+    %t10_0 = j_2+1
+    j_3 = %t10_0
     goto  L5
 L7:
     %t11_0 = outerSum_1+innerSum_2
@@ -2607,7 +2655,6 @@ func main()->Int {
     innerSum_0 = 0
     i_0 = 0
     j_0 = 0
-    j_1 = j_0
     i_1 = i_0
     innerSum_1 = innerSum_0
     outerSum_1 = outerSum_0
@@ -2616,28 +2663,28 @@ func main()->Int {
     %t4_0 = i_1<4
     if %t4_0 goto L3 else goto L4
 L3:
-    j_2 = 0
-    j_3 = j_2
+    j_1 = 0
+    j_2 = j_1
     innerSum_2 = innerSum_1
     goto  L5
 L5:
-    %t5_0 = j_3<4
+    %t5_0 = j_2<4
     if %t5_0 goto L6 else goto L7
 L6:
-    %t6_0 = i_1+j_3
+    %t6_0 = i_1+j_2
     %t7_0 = %t6_0%2
     %t8_0 = %t7_0==0
     innerSum_4 = innerSum_2
     if %t8_0 goto L8 else goto L9
 L8:
-    %t9_0 = innerSum_2+j_3
+    %t9_0 = innerSum_2+j_2
     innerSum_3 = %t9_0
     innerSum_4 = innerSum_3
     goto  L9
 L9:
-    %t10_0 = j_3+1
-    j_4 = %t10_0
-    j_3 = j_4
+    %t10_0 = j_2+1
+    j_3 = %t10_0
+    j_2 = j_3
     innerSum_2 = innerSum_4
     goto  L5
 L7:
@@ -2645,7 +2692,6 @@ func main()->Int {
     outerSum_2 = %t11_0
     %t12_0 = i_1+1
     i_2 = %t12_0
-    j_1 = j_3
     i_1 = i_2
     innerSum_1 = innerSum_2
     outerSum_1 = outerSum_2
@@ -2807,6 +2853,126 @@ func foo()->Int
     %t7_0 = 0
     %t7_2 = %t7_0
     goto  L4
+""", result);
+    }
+
+
+    @Test
+    public void testSSA17() {
+        String src = """
+func merge(begin: Int, middle: Int, end: Int)
+{
+    if (begin < end) {
+        var cond = 0
+        if (begin < middle) {
+            if (begin >= end)          cond = 1;
+        }
+        if (cond)
+        {
+            cond = 0
+        }
+    }
+}
+""";
+        String result = compileSrc(src);
+        Assert.assertEquals("""
+func merge
+Before SSA
+==========
+L0:
+    arg begin
+    arg middle
+    arg end
+    %t4 = begin<end
+    if %t4 goto L2 else goto L3
+L2:
+    cond = 0
+    %t5 = begin<middle
+    if %t5 goto L4 else goto L5
+L4:
+    %t6 = begin>=end
+    if %t6 goto L6 else goto L7
+L6:
+    cond = 1
+    goto  L7
+L7:
+    goto  L5
+L5:
+    if cond goto L8 else goto L9
+L8:
+    cond = 0
+    goto  L9
+L9:
+    goto  L3
+L3:
+    goto  L1
+L1:
+After SSA
+=========
+L0:
+    arg begin_0
+    arg middle_0
+    arg end_0
+    %t4_0 = begin_0<end_0
+    if %t4_0 goto L2 else goto L3
+L2:
+    cond_0 = 0
+    %t5_0 = begin_0<middle_0
+    if %t5_0 goto L4 else goto L5
+L4:
+    %t6_0 = begin_0>=end_0
+    if %t6_0 goto L6 else goto L7
+L6:
+    cond_1 = 1
+    goto  L7
+L7:
+    cond_2 = phi(cond_0, cond_1)
+    goto  L5
+L5:
+    cond_3 = phi(cond_0, cond_2)
+    if cond_3 goto L8 else goto L9
+L8:
+    cond_4 = 0
+    goto  L9
+L9:
+    goto  L3
+L3:
+    goto  L1
+L1:
+After exiting SSA
+=================
+L0:
+    arg begin_0
+    arg middle_0
+    arg end_0
+    %t4_0 = begin_0<end_0
+    if %t4_0 goto L2 else goto L3
+L2:
+    cond_0 = 0
+    %t5_0 = begin_0<middle_0
+    cond_3 = cond_0
+    if %t5_0 goto L4 else goto L5
+L4:
+    %t6_0 = begin_0>=end_0
+    cond_2 = cond_0
+    if %t6_0 goto L6 else goto L7
+L6:
+    cond_1 = 1
+    cond_2 = cond_1
+    goto  L7
+L7:
+    cond_3 = cond_2
+    goto  L5
+L5:
+    if cond_3 goto L8 else goto L9
+L8:
+    cond_4 = 0
+    goto  L9
+L9:
+    goto  L3
+L3:
+    goto  L1
+L1:
 """, result);
     }
 }
\ No newline at end of file
diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java b/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java
index acb583c..a00d48d 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java
@@ -608,4 +608,92 @@ func foo()->Int
                 integerValue.value == 1);
     }
 
+    @Test
+    public void testMergeSort() {
+        String src = """
+// based on the top-down version from https://en.wikipedia.org/wiki/Merge_sort
+// via https://github.com/SeaOfNodes/Simple
+func merge_sort(a: [Int], b: [int], n: Int) 
+{
+    copy_array(a, 0, n, b)
+    split_merge(a, 0, n, b)
+}
+
+func split_merge(b: [Int], begin: Int, end: Int, a: [Int])
+{
+    if (end - begin <= 1)
+        return;
+    var middle = (end + begin) / 2
+    split_merge(a, begin, middle, b)
+    split_merge(a, middle, end, b)
+    merge(b, begin, middle, end, a)
+}
+
+func merge(b: [Int], begin: Int, middle: Int, end: Int, a: [Int])
+{
+    var i = begin
+    var j = middle
+    var k = begin
+    while (k < end) {
+        // && and ||
+        var cond = 0
+        if (i < middle) {
+            if (j >= end)          cond = 1;
+            else if (a[i] <= a[j]) cond = 1;
+        }
+        if (cond)
+        {
+            b[k] = a[i]
+            i = i + 1
+        }
+        else
+        {
+            b[k] = a[j]
+            j = j + 1
+        }
+        k = k + 1
+    }
+}
+
+func copy_array(a: [Int], begin: Int, end: Int, b: [Int])
+{
+    var k = begin
+    while (k < end)
+    {
+        b[k] = a[k]
+        k = k + 1
+    }
+}
+
+func eq(a: [Int], b: [Int], n: Int)->Int
+{
+    var result = 1
+    var i = 0
+    while (i < n)
+    {
+        if (a[i] != b[i])
+        {
+            result = 0
+            break
+        }
+        i = i + 1
+    } 
+    return result
+}
+
+func main()->Int
+{
+    var a = new [Int]{10,9,8,7,6,5,4,3,2,1}
+    var b = new [Int]{ 0,0,0,0,0,0,0,0,0,0}
+    var expect = new [Int]{1,2,3,4,5,6,7,8,9,10}
+    merge_sort(a, b, 10)
+    return eq(a,expect,10)
+}
+""";
+        var value = compileAndRun(src, "main", Options.OPT);
+        Assert.assertNotNull(value);
+        Assert.assertTrue(value instanceof Value.IntegerValue integerValue &&
+                integerValue.value == 1);
+    }
+
 }

From ad202670b8f030fea165be3fe75314602f008a79 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Sun, 30 Mar 2025 21:51:21 +0100
Subject: [PATCH 050/119] Bug fixes

---
 .../ezlang/interpreter/TestInterpreter.java                 | 2 +-
 .../ezlang/interpreter/TestInterpreter.java                 | 2 +-
 .../com/compilerprogramming/ezlang/compiler/Compiler.java   | 6 +++++-
 3 files changed, 7 insertions(+), 3 deletions(-)

diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java b/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java
index a00d48d..161cf38 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java
@@ -613,7 +613,7 @@ public void testMergeSort() {
         String src = """
 // based on the top-down version from https://en.wikipedia.org/wiki/Merge_sort
 // via https://github.com/SeaOfNodes/Simple
-func merge_sort(a: [Int], b: [int], n: Int) 
+func merge_sort(a: [Int], b: [Int], n: Int) 
 {
     copy_array(a, 0, n, b)
     split_merge(a, 0, n, b)
diff --git a/registervm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java b/registervm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java
index 78e4d48..49f8450 100644
--- a/registervm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java
+++ b/registervm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java
@@ -250,7 +250,7 @@ public void testMergeSort() {
         String src = """
 // based on the top-down version from https://en.wikipedia.org/wiki/Merge_sort
 // via https://github.com/SeaOfNodes/Simple
-func merge_sort(a: [Int], b: [int], n: Int) 
+func merge_sort(a: [Int], b: [Int], n: Int) 
 {
     copy_array(a, 0, n, b)
     split_merge(a, 0, n, b)
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java
index e19a453..dd90516 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java
@@ -941,7 +941,11 @@ private Node compileLet(AST.VarStmt letStmt) {
     }
 
     private Node compileReturn(AST.ReturnStmt returnStmt) {
-        var expr = compileExpr(returnStmt.expr);
+        Node expr;
+        if (returnStmt.expr != null)
+            expr = compileExpr(returnStmt.expr);
+        else
+            expr = ZERO;
         // Need default memory, since it can be lazy, need to force
         // a non-lazy Phi
         _fun.addReturn(ctrl(), _scope.mem().merge(), expr);

From d4bec63421c1cc604182e1600ff0955f8d622a89 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Sun, 30 Mar 2025 23:16:07 +0100
Subject: [PATCH 051/119] Doc updates

---
 README.md       | 5 +++--
 optvm/README.md | 8 ++++++--
 2 files changed, 9 insertions(+), 4 deletions(-)

diff --git a/README.md b/README.md
index d9783b2..058345e 100644
--- a/README.md
+++ b/README.md
@@ -26,8 +26,9 @@ The project is under development and subject to change. At this point in time, w
 * [semantic](./semantic/README.md) - semantic analyzer
 * [stackvm](./stackvm/README.md) - a compiler that generates IR for a stack based virtual machine
 * [registervm](./registervm/README.md) - a compiler that generates a so called three-address IR and an interpreter that can execute the IR
-* [optvm](./optvm/README.md) - an optimizing compiler (WIP) that supports SSA.
-* [seaofnodes](./seaofnodes/README.md) - a compiler that will generate Sea of Nodes IR, using SoN backend from [Simple](https://github.com/SeaOfNodes/Simple).
+* [optvm](./optvm/README.md) - WIP optimizing compiler with SSA transformation, constant propagation, graph coloring register allocation 
+  targeting an abstract machine.
+* [seaofnodes](./seaofnodes/README.md) - WIP compiler that generates Sea of Nodes IR, using SoN backend from [Simple](https://github.com/SeaOfNodes/Simple).
 
 ## How can you contribute?
 
diff --git a/optvm/README.md b/optvm/README.md
index 218b4f7..60c6eed 100644
--- a/optvm/README.md
+++ b/optvm/README.md
@@ -33,10 +33,14 @@ a physical machine. Therefore, all our optimization passes will work on the inst
   each register has a unique integer ID and these ids are allocated in a sequential manner.
 * [Liveness](src/main/java/com/compilerprogramming/ezlang/compiler/Liveness.java) - Liveness calculator, works for both SSA and non-SSA forms. Computes
   liveness data per basic block - mainly live-out. Note that the interference graph builder starts here and computes instruction level liveness as necessary.
-* [EnterSSA](src/main/java/com/compilerprogramming/ezlang/compiler/EnterSSA.java) - Transforms into SSA, using algorithm by Preston Briggs.
+* [EnterSSA](src/main/java/com/compilerprogramming/ezlang/compiler/EnterSSA.java) - Transforms into SSA, using [algorithm by Preston Briggs](https://dl.acm.org/doi/10.5555/295545.295551). This is one of available mechanisms to transform the IR into SSA. 
+  The other alternative is to generate SSA IR directly from the AST, using [Braun's method](https://dl.acm.org/doi/10.1007/978-3-642-37051-9_6) - this option is integrated into the
+  [compiler]((src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java)) and enabled using an option.
 * [ExitSSA](src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA.java) - Exits SSA form, using algorithm by Preston Briggs.
 * [SparseConditionalConstantPropagation](src/main/java/com/compilerprogramming/ezlang/compiler/SparseConditionalConstantPropagation.java) - Conditional Constant Propagation on SSA form (SCCP)
-* [SSAEdges](src/main/java/com/compilerprogramming/ezlang/compiler/SSAEdges.java) - SSAEdges are def-use chains used by SCCP algorithm 
+* [ConstantComparisonPropagation](src/main/java/com/compilerprogramming/ezlang/compiler/ConstantComparisonPropagation.java) - Detects equals and not equals against constants within conditionals,
+   and inserts variables with appropriately specialized type within the dominated blocks, so that a second pass of SCCP can further optimize code.
+* [SSAEdges](src/main/java/com/compilerprogramming/ezlang/compiler/SSAEdges.java) - SSAEdges are def-use chains used by SCCP algorithm, and also generated during incremental SSA construction using Braun's method.
 * [LoopFinder](src/main/java/com/compilerprogramming/ezlang/compiler/LoopFinder.java) - Discovers loops. (Not used yet)
 * [LoopNest](src/main/java/com/compilerprogramming/ezlang/compiler/LoopNest.java) - Representation of loop nesting. (Not used yet)
 * [InterferenceGraph](src/main/java/com/compilerprogramming/ezlang/compiler/InterferenceGraph.java) - Representation of an Interference Graph

From b4e4dcbb52e014f6d94d8dd46a0c600e577a71ec Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <dibyendu@redukti.com>
Date: Fri, 4 Apr 2025 20:31:51 +0100
Subject: [PATCH 052/119] Add compiling of boolean expressions to stack IR

---
 .../ezlang/compiler/CompiledFunction.java     | 34 ++++++++++++++++++-
 .../ezlang/compiler/TestCompiler.java         | 30 ++++++++++++++++
 2 files changed, 63 insertions(+), 1 deletion(-)

diff --git a/stackvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java b/stackvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
index ed83aca..4d384c1 100644
--- a/stackvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
+++ b/stackvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
@@ -343,7 +343,39 @@ private boolean compileSymbolExpr(AST.NameExpr symbolExpr) {
         return false;
     }
 
+    private boolean codeBoolean(AST.BinaryExpr binaryExpr) {
+        boolean isAnd = binaryExpr.op.str.equals("&&");
+        BasicBlock l1 = createBlock();
+        BasicBlock l2 = createBlock();
+        BasicBlock l3 = createBlock();
+        boolean indexed = compileExpr(binaryExpr.expr1);
+        if (indexed)
+            codeIndexedLoad();
+        if (isAnd) {
+            code(new Instruction.ConditionalBranch(currentBlock, l1, l2));
+        } else {
+            code(new Instruction.ConditionalBranch(currentBlock, l2, l1));
+        }
+        startBlock(l1);
+        indexed = compileExpr(binaryExpr.expr2);
+        if (indexed)
+            codeIndexedLoad();
+        jumpTo(l3);
+        startBlock(l2);
+        // Below we must write to the same temp
+        code(new Instruction.PushConst(isAnd ? 0 : 1));
+        jumpTo(l3);
+        startBlock(l3);
+        // leaves result on stack
+        return false;
+    }
+
     private boolean compileBinaryExpr(AST.BinaryExpr binaryExpr) {
+        String binOp = binaryExpr.op.str;
+        if (binOp.equals("&&") ||
+                binOp.equals("||")) {
+            return codeBoolean(binaryExpr);
+        }
         int opCode = 0;
         boolean indexed = compileExpr(binaryExpr.expr1);
         if (indexed)
@@ -351,7 +383,7 @@ private boolean compileBinaryExpr(AST.BinaryExpr binaryExpr) {
         indexed = compileExpr(binaryExpr.expr2);
         if (indexed)
             codeIndexedLoad();
-        switch (binaryExpr.op.str) {
+        switch (binOp) {
             case "+" -> opCode = Instruction.ADD_I;
             case "-" -> opCode = Instruction.SUB_I;
             case "*" -> opCode = Instruction.MUL_I;
diff --git a/stackvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestCompiler.java b/stackvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestCompiler.java
index 6393090..2d828ee 100644
--- a/stackvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestCompiler.java
+++ b/stackvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestCompiler.java
@@ -634,4 +634,34 @@ public void testFunction28() {
 L1:
 """, result);
     }
+
+    @Test
+    public void testFunction104() {
+        String src = """
+                func foo()->Int
+                {
+                    return 1 == 1 && 2 == 2
+                }
+                """;
+        String result = compileSrc(src);
+        Assert.assertEquals("""
+L0:
+	pushi 1
+	pushi 1
+	eq
+	cbr L2 L3
+L2:
+	pushi 2
+	pushi 2
+	eq
+	jump L4
+L4:
+	jump L1
+L1:
+L3:
+	pushi 0
+	jump L4
+""", result);
+    }
+
 }

From febdbb4c87a3321b3e2dc629db620e5db618f607 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Sun, 6 Apr 2025 14:16:02 +0100
Subject: [PATCH 053/119] Various fixes:

1. We do need lexical scopes - so the idea that we could just create unique names for all vars doesn't work because then it means that all vars are available in scopes where they are not supposed to be, leading to creation of phi's that reference undefined vars.

2. The type conversion from EeZee to SON Types had many bugs.

3. The definition of ARY in TypeStruct meant that int[] ended up with incorrect aliases.

Additional fixes.
---
 .../ezlang/compiler/Compiler.java             | 92 +++++++++----------
 .../compiler/sontypes/SONTypeFunPtr.java      |  6 +-
 .../compiler/sontypes/SONTypeMemPtr.java      |  6 +-
 .../compiler/sontypes/SONTypeStruct.java      | 16 ++--
 .../compiler/sontypes/SONTypeTuple.java       |  4 +-
 seaofnodes/src/test/cases/fib/fib.ez          | 18 ++++
 seaofnodes/src/test/cases/fib/fib.ir          | 46 ++++++++++
 seaofnodes/src/test/cases/fib/fib.simple      | 15 +++
 seaofnodes/src/test/cases/fib/fib.simple_ir   | 46 ++++++++++
 seaofnodes/src/test/cases/hello/hello.ez      |  4 +
 seaofnodes/src/test/cases/mergsort/sort.ez    | 78 ++++++++++++++++
 .../ezlang/compiler/TestSONTypes.java         | 23 ++---
 12 files changed, 281 insertions(+), 73 deletions(-)
 create mode 100644 seaofnodes/src/test/cases/fib/fib.ez
 create mode 100644 seaofnodes/src/test/cases/fib/fib.ir
 create mode 100644 seaofnodes/src/test/cases/fib/fib.simple
 create mode 100644 seaofnodes/src/test/cases/fib/fib.simple_ir
 create mode 100644 seaofnodes/src/test/cases/hello/hello.ez
 create mode 100644 seaofnodes/src/test/cases/mergsort/sort.ez

diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java
index dd90516..b93dda1 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java
@@ -90,7 +90,12 @@ public TypeDictionary createAST(String src) {
 
 
     private void populateDefaultTypes(Map<String,SONType> types) {
+        // Pre-create int, [int] and *[int] types
         types.put(typeDictionary.INT.name(), SONTypeInteger.BOT);
+        var intArrayType = SONTypeStruct.makeAry(SONTypeInteger.U32, _code.getALIAS(), SONTypeInteger.BOT, _code.getALIAS());
+        var ptrIntArrayType = SONTypeMemPtr.make(intArrayType);
+        types.put("[" + typeDictionary.INT.name() + "]", ptrIntArrayType);
+        // Also get the types created by default
         for (SONType t: SONType.gather()) {
             types.put(t.str(), t);
         }
@@ -145,18 +150,23 @@ private void createSONStructType(Map<String, SONType> structTypes, String typeNa
             Type type = typeStruct.getField(name); // FIXME
             fs.push(new Field(name,getSONType(structTypes,type),_code.getALIAS(),false));
         }
+        // A Struct type may have been created before because of
+        // reference from itself; in which case we need to update that
         SONType fref = structTypes.get(typeName);
         if (fref != null) {
-            if (fref instanceof SONTypeStruct ts) {
+            if (fref instanceof SONTypeMemPtr ptr &&
+                ptr._obj instanceof SONTypeStruct ts) {
+                assert ts._fields.length == 0;
+                // Add the fields to the existing type
                 ts._fields = fs.asAry();
             }
-            else throw new CompilerException("");
+            else throw new CompilerException("Expected struct type " + typeName + " but got " + fref);
         }
         else {
-            structTypes.put(typeName, new SONTypeStruct(typeName, fs.asAry()));
+            var ts = SONTypeStruct.make(typeName, fs.asAry());
+            var ptr = SONTypeMemPtr.make((byte)2,ts);
+            structTypes.put(typeName,ptr);
         }
-//        SONTypeStruct ts = SONTypeStruct.make(typeName, fs.asAry());
-//        TYPES.put(typeName, SONTypeMemPtr.make(ts));
     }
 
     private String getSONTypeName(Type type) {
@@ -164,7 +174,7 @@ private String getSONTypeName(Type type) {
             return typeFunction.name;
         }
         else if (type instanceof Type.TypeArray typeArray) {
-            return typeArray.name();
+            return "*[" + getSONTypeName(typeArray.getElementType()) + "]";
         }
         else if (type instanceof Type.TypeStruct typeStruct) {
             return "*" + typeStruct.name;
@@ -183,36 +193,25 @@ else if (type instanceof Type.TypeNullable typeNullable) {
     }
 
     private SONType getSONType(Map<String, SONType> structTypes, Type type) {
-        String tname = getSONTypeName(type);
-        SONType t = structTypes.get(tname);
+        SONType t = structTypes.get(type.name());
         if (t != null) return t;
         if (type instanceof Type.TypeStruct) {
-            SONType existing = structTypes.get(type.name);
-            SONTypeStruct ts;
-            if (existing instanceof SONTypeStruct tsExisting)
-                ts = tsExisting;
-            else {
-                ts = new SONTypeStruct(type.name, new Field[0]);
-                structTypes.put(ts.str(), ts);
-            }
-            SONTypeMemPtr ptr = new SONTypeMemPtr((byte)2,ts);
-            if (type.name().equals(ts.str())) {
-                structTypes.put(ptr.str(), ptr);
-                return ptr;
-            }
-            else throw new CompilerException("Unexpected error");
+            // For struct types in EeZee language a reference
+            // to T means *T in SoN
+            // Create SON struct type
+            SONTypeStruct ts = SONTypeStruct.make(type.name, new Field[0]);
+            // Now create *T
+            SONTypeMemPtr ptr = SONTypeMemPtr.make((byte)2,ts);
+            // EeZee T maps to SoN *T
+            structTypes.put(type.name(), ptr);
+            return ptr;
         }
         else if (type instanceof Type.TypeArray typeArray) {
+            // A reference to array in EeZee means
+            // *array in SoN
             SONType elementType = getSONType(structTypes,typeArray.getElementType());
-            SONTypeStruct ts = null;
-            SONType existing = structTypes.get(typeArray.name());
-            if (existing instanceof SONTypeStruct tsExisting)
-                ts = tsExisting;
-            else {
-                ts = SONTypeStruct.makeArray(SONTypeInteger.U32, _code.getALIAS(), elementType, _code.getALIAS());
-                structTypes.put(ts.str(), ts);
-            }
-            SONTypeMemPtr ptr = new SONTypeMemPtr((byte)2,ts);
+            SONTypeStruct ts = SONTypeStruct.makeArray(SONTypeInteger.U32, _code.getALIAS(), elementType, _code.getALIAS());
+            SONTypeMemPtr ptr = SONTypeMemPtr.make((byte)2,ts);
             structTypes.put(typeArray.name(), ptr); // Array type name is not same as ptr str()
             return ptr;
         }
@@ -223,11 +222,11 @@ else if (type instanceof Type.TypeNullable typeNullable) {
                 if (ptr1.nullable())
                     ptr = ptr1;
                 else
-                    ptr = new SONTypeMemPtr((byte)3,ptr1._obj);
+                    ptr = SONTypeMemPtr.make((byte)3,ptr1._obj);
             }
             else
-                ptr = new SONTypeMemPtr((byte)2,(SONTypeStruct) baseType);
-            structTypes.put(ptr.str(), ptr);
+                ptr = SONTypeMemPtr.make((byte)2,(SONTypeStruct) baseType);
+            structTypes.put(typeNullable.name(), ptr);
             return ptr;
         }
         else if (type instanceof Type.TypeVoid) {
@@ -241,13 +240,13 @@ else if (type instanceof Type.TypeVoid) {
 
     // TODO because first two slots are MEM and RPC
     private int REGNUM = 2;
-    private void setVarIds(Scope scope, ScopeNode scopeNode, FunNode fun) {
+    private void defineScopedVars(Scope scope, ScopeNode scopeNode, FunNode fun) {
         for (Symbol symbol: scope.getLocalSymbols()) {
             if (symbol instanceof Symbol.VarSymbol varSymbol) {
                 varSymbol.regNumber = REGNUM++;
-                String sonTypeName = getSONTypeName(varSymbol.type);
-                SONType sonType = TYPES.get(sonTypeName);
-                if (sonType == null) throw new CompilerException("Unknown SON Type "+sonTypeName);
+                SONType sonType = TYPES.get(varSymbol.type.name());
+                if (sonType == null)
+                    throw new CompilerException("Unknown SON Type "+varSymbol.type.name());
                 Node init = null;
 
                 if (varSymbol instanceof Symbol.ParameterSymbol) {
@@ -256,9 +255,6 @@ private void setVarIds(Scope scope, ScopeNode scopeNode, FunNode fun) {
                 scopeNode.define(makeVarName(varSymbol), sonType, false, init);
             }
         }
-        for (Scope childScope: scope.children) {
-            setVarIds(childScope, scopeNode, fun);
-        }
     }
 
     private void generateFunction(Symbol.FunctionTypeSymbol functionTypeSymbol) {
@@ -338,7 +334,8 @@ private ReturnNode generateFunctionBody(Symbol.FunctionTypeSymbol functionTypeSy
         _scope.mem(mem);
         // All args, "as-if" called externally
         AST.FuncDecl funcDecl = (AST.FuncDecl) functionTypeSymbol.functionDecl;
-        setVarIds(funcDecl.scope,_scope,fun);
+        REGNUM = 2;
+        defineScopedVars(funcDecl.scope,_scope,fun);
 
         // Parse the body
         Node last = compileStatement(funcDecl.block);
@@ -556,12 +553,12 @@ private Node newStruct( SONTypeStruct obj, Node size) {
     private Node compileNewExpr(AST.NewExpr newExpr) {
         Type type = newExpr.type;
         if (type instanceof Type.TypeArray typeArray) {
-            SONTypeMemPtr tarray = (SONTypeMemPtr) TYPES.get(getSONTypeName(typeArray));
+            SONTypeMemPtr tarray = (SONTypeMemPtr) TYPES.get(typeArray.name());
             return newArray(tarray._obj,ZERO);
         }
         else if (type instanceof Type.TypeStruct typeStruct) {
-            SONTypeStruct tstruct = (SONTypeStruct) TYPES.get(typeStruct.name());
-            return newStruct(tstruct,con(tstruct.offset(tstruct._fields.length)));
+            SONTypeMemPtr tptr = (SONTypeMemPtr) TYPES.get(typeStruct.name());
+            return newStruct(tptr._obj,con(tptr._obj.offset(tptr._obj._fields.length)));
         }
         else
             throw new CompilerException("Unexpected type: " + type);
@@ -701,7 +698,7 @@ private Node compileCallExpr(AST.CallExpr callExpr) {
     }
 
     private String makeVarName(Symbol.VarSymbol varSymbol) {
-        return varSymbol.name + "$" + varSymbol.regNumber;
+        return varSymbol.name; // + "$" + varSymbol.regNumber;
     }
 
     private Node compileStatement(AST.Stmt statement) {
@@ -955,9 +952,12 @@ private Node compileReturn(AST.ReturnStmt returnStmt) {
 
     private Node compileBlock(AST.BlockStmt block) {
         Node last = ZERO;
+        _scope.push(ScopeNode.Kind.Block);
+        defineScopedVars(block.scope, _scope, _fun);
         for (AST.Stmt stmt: block.stmtList) {
             last = compileStatement(stmt);
         }
+        _scope.pop();
         return last;
     }
 
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeFunPtr.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeFunPtr.java
index 2d81a60..0b41493 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeFunPtr.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeFunPtr.java
@@ -41,10 +41,10 @@ public SONTypeFunPtr(byte nil, SONTypeTuple sig, SONType ret, long fidxs) {
     public SONTypeFunPtr makeFrom(int fidx ) { return make((byte)2, _sig,_ret,1L<<fidx ); }
 
     public static SONTypeFunPtr BOT   = make((byte)3, SONTypeTuple.BOT, SONType.BOTTOM,-1);
-    public static SONTypeFunPtr TEST  = make((byte)2, SONTypeTuple.TEST, SONTypeInteger.BOT,1);
-    public static SONTypeFunPtr TEST0 = make((byte)3, SONTypeTuple.TEST, SONTypeInteger.BOT,3);
+//    public static SONTypeFunPtr TEST  = make((byte)2, SONTypeTuple.TEST, SONTypeInteger.BOT,1);
+//    public static SONTypeFunPtr TEST0 = make((byte)3, SONTypeTuple.TEST, SONTypeInteger.BOT,3);
     public static SONTypeFunPtr MAIN  = make((byte)3, SONTypeTuple.MAIN, SONTypeInteger.BOT,-1);
-    public static void gather(ArrayList<SONType> ts) { ts.add(TEST); ts.add(TEST0); ts.add(BOT); ts.add(MAIN); }
+    public static void gather(ArrayList<SONType> ts) { /* ts.add(TEST); ts.add(TEST0); */ ts.add(BOT); ts.add(MAIN); }
 
     @Override
     SONType xmeet(SONType t) {
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeMemPtr.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeMemPtr.java
index 7b0f1c8..1d74de0 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeMemPtr.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeMemPtr.java
@@ -31,7 +31,7 @@ public SONTypeMemPtr(byte nil, SONTypeStruct obj) {
         assert obj!=null;
         _obj = obj;
     }
-    static SONTypeMemPtr make(byte nil, SONTypeStruct obj) { return new SONTypeMemPtr(nil, obj).intern(); }
+    public static SONTypeMemPtr make(byte nil, SONTypeStruct obj) { return new SONTypeMemPtr(nil, obj).intern(); }
     public static SONTypeMemPtr makeNullable(SONTypeStruct obj) { return make((byte)3, obj); }
     public static SONTypeMemPtr make(SONTypeStruct obj) { return new SONTypeMemPtr((byte)2, obj).intern(); }
     public SONTypeMemPtr makeFrom(SONTypeStruct obj) { return obj==_obj ? this : make(_nil, obj); }
@@ -47,8 +47,8 @@ public SONTypeMemPtr(byte nil, SONTypeStruct obj) {
     public static SONTypeMemPtr TOP = BOT.dual();
     public static SONTypeMemPtr NOTBOT = make((byte)2, SONTypeStruct.BOT);
 
-    public static SONTypeMemPtr TEST= make((byte)2, SONTypeStruct.TEST);
-    public static void gather(ArrayList<SONType> ts) { ts.add(NOTBOT); ts.add(BOT); ts.add(TEST); }
+    //public static SONTypeMemPtr TEST= make((byte)2, SONTypeStruct.TEST);
+    public static void gather(ArrayList<SONType> ts) { ts.add(NOTBOT); ts.add(BOT); /* ts.add(TEST); */ }
 
     @Override
     public SONTypeNil xmeet(SONType t) {
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeStruct.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeStruct.java
index f1f15ea..256ddfa 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeStruct.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeStruct.java
@@ -35,7 +35,7 @@ public SONTypeStruct(String name, Field[] fields) {
     public static SONTypeStruct make(String name, Field... fields) { return new SONTypeStruct(name, fields).intern(); }
     public static final SONTypeStruct TOP = make("$TOP",new Field[0]);
     public static final SONTypeStruct BOT = make("$BOT",new Field[0]);
-    public static final SONTypeStruct TEST = make("test",new Field[]{Field.TEST});
+    //public static final SONTypeStruct TEST = make("test",new Field[]{Field.TEST});
     // Forward-ref version
     public static SONTypeStruct makeFRef(String name) { return make(name, (Field[])null); }
     // Make a read-only version
@@ -66,14 +66,14 @@ public static SONTypeStruct makeArray(SONTypeInteger len, int lenAlias, SONType
     }
 
     // A pair of self-cyclic types
-    private static final SONTypeStruct S1F = makeFRef("S1");
-    private static final SONTypeStruct S2F = makeFRef("S2");
-    public  static final SONTypeStruct S1  = make("S1", Field.make("a", SONTypeInteger.BOT, -1, false), Field.make("s2", SONTypeMemPtr.make((byte)2,S2F),-2, false) );
-    private static final SONTypeStruct S2  = make("S2", Field.make("b", SONTypeFloat.F64, -3, false), Field.make("s1", SONTypeMemPtr.make((byte)2,S1F),-4, false) );
+//    private static final SONTypeStruct S1F = makeFRef("S1");
+//    private static final SONTypeStruct S2F = makeFRef("S2");
+//    public  static final SONTypeStruct S1  = make("S1", Field.make("a", SONTypeInteger.BOT, -1, false), Field.make("s2", SONTypeMemPtr.make((byte)2,S2F),-2, false) );
+//    private static final SONTypeStruct S2  = make("S2", Field.make("b", SONTypeFloat.F64, -3, false), Field.make("s1", SONTypeMemPtr.make((byte)2,S1F),-4, false) );
+//
+//    private static final SONTypeStruct ARY = makeAry(SONTypeInteger.U32,-1, SONTypeInteger.BOT,-2);
 
-    private static final SONTypeStruct ARY = makeAry(SONTypeInteger.U32,-1, SONTypeInteger.BOT,-2);
-
-    public static void gather(ArrayList<SONType> ts) { ts.add(TEST); ts.add(BOT); ts.add(S1); ts.add(S2); ts.add(ARY); }
+    public static void gather(ArrayList<SONType> ts) { /* ts.add(TEST); ts.add(BOT); ts.add(S1); ts.add(S2); ts.add(ARY); */ }
 
     // Find field index by name
     public int find(String fname) {
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeTuple.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeTuple.java
index 4ea6539..f3acceb 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeTuple.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeTuple.java
@@ -13,7 +13,7 @@ public class SONTypeTuple extends SONType {
     public static final SONTypeTuple BOT = new SONTypeTuple(new SONType[0]).intern();
     public static final SONTypeTuple TOP = new SONTypeTuple(null).intern();
 
-    public static final SONTypeTuple TEST = make(SONTypeInteger.BOT, SONTypeMemPtr.TEST);
+    //public static final SONTypeTuple TEST = make(SONTypeInteger.BOT, SONTypeMemPtr.TEST);
     public static final SONTypeTuple START= make(SONType.CONTROL, SONTypeMem.TOP, SONTypeInteger.BOT);
     public static final SONTypeTuple MAIN = make(SONTypeInteger.BOT);
     public static final SONTypeTuple RET  = make(SONType.CONTROL, SONTypeMem.BOT, SONType.BOTTOM);
@@ -23,7 +23,7 @@ public class SONTypeTuple extends SONType {
     public static final SONTypeTuple IF_TRUE    = make(new SONType[]{SONType. CONTROL, SONType.XCONTROL});
     public static final SONTypeTuple IF_FALSE   = make(new SONType[]{SONType.XCONTROL, SONType. CONTROL});
 
-    public  static void gather(ArrayList<SONType> ts) { ts.add(BOT); ts.add(TEST); ts.add(START); ts.add(MAIN); ts.add(IF_TRUE); }
+    public  static void gather(ArrayList<SONType> ts) { ts.add(BOT); /* ts.add(TEST); */ ts.add(START); ts.add(MAIN); ts.add(IF_TRUE); }
 
     @Override
     SONType xmeet(SONType other) {
diff --git a/seaofnodes/src/test/cases/fib/fib.ez b/seaofnodes/src/test/cases/fib/fib.ez
new file mode 100644
index 0000000..a30b6d9
--- /dev/null
+++ b/seaofnodes/src/test/cases/fib/fib.ez
@@ -0,0 +1,18 @@
+func fib(n: Int)->Int {
+    var i: Int;
+    var temp=0;
+    var f1=1;
+    var f2=1;
+    i=n;
+    while( i>1 ){
+        temp = f1+f2;
+        f1=f2;
+        f2=temp;
+        i=i-1;
+    }
+    return f2;
+}
+
+func foo()->Int {
+    return fib(10);
+}
\ No newline at end of file
diff --git a/seaofnodes/src/test/cases/fib/fib.ir b/seaofnodes/src/test/cases/fib/fib.ir
new file mode 100644
index 0000000..b0821d1
--- /dev/null
+++ b/seaofnodes/src/test/cases/fib/fib.ir
@@ -0,0 +1,46 @@
+START:                            [[   ]]
+   1 Start   ____ ____ ____       [[     3    4    9   16   18    8    5   46  ]]  [  Ctrl, #TOP, int]
+
+L8:                               [[  START      ]]
+   8 fib     ____    1            [[    10   17   19   20   22  ]]  Ctrl
+  10 $rpc       8    9            [[    14       ]]  $[ALL]
+  17 $mem       8   16            [[    14       ]]  #BOT
+  19 n$2        8   18            [[    25       ]]  int
+  20 #1         8                 [[    37   39   26   41  ]]  1
+
+L46:                              [[  START      ]]
+  46 foo     ____    1            [[    48   55   56   57   59  ]]  Ctrl
+  48 $rpc      46    9            [[    52       ]]  $[ALL]
+  55 $mem      46   16            [[    59       ]]  #BOT
+  57 #10       46                 [[    59       ]]  10
+  56 #{ int    46                 [[    59       ]]  { int -> int #1}
+  59 Call      46   55   57   56  [[    60       ]]  Ctrl
+
+LOOP22:                           [[  L8        L29        ]]
+  22 Loop    ____    8   29       [[    39   25   37   26   28  ]]  Ctrl
+  39 Phi_f2$   22   20   38       [[    37   14   38  ]]  int
+  25 Phi_i$3   22   19   41       [[    26   41  ]]  int
+  37 Phi_f1$   22   20   39       [[    38       ]]  int
+  26 LT        22   20   25       [[    28       ]]  bool
+  28 If        22   26            [[    29   30  ]]  [  Ctrl, Ctrl]
+
+L60:                              [[  L46        ]]
+  60 CallEnd   59                 [[    61   63   64  ]]  [  Ctrl, #BOT, int]
+  63 $mem      60                 [[    52       ]]  #BOT
+  64 #2        60                 [[    52       ]]  int
+
+L30:                              [[  LOOP22     ]]
+  30 False     28                 [[    14       ]]  Ctrl
+  14 Return    30   17   39   10  [[     2       ]]  [  Ctrl, #BOT, int]
+
+L29:                              [[  LOOP22     ]]
+  29 True      28                 [[    38   41   22  ]]  Ctrl
+  41 Sub       29   25   20       [[    25       ]]  int
+  38 Add       29   39   37       [[    39       ]]  int
+
+L61:                              [[  L60        ]]
+  61 $ctrl     60                 [[    52       ]]  Ctrl
+  52 Return    61   63   64   48  [[     2       ]]  [  Ctrl, #BOT, int]
+
+L2:                               [[   ]]
+   2 Stop      14   52            [[             ]]  Bot
diff --git a/seaofnodes/src/test/cases/fib/fib.simple b/seaofnodes/src/test/cases/fib/fib.simple
new file mode 100644
index 0000000..5746da4
--- /dev/null
+++ b/seaofnodes/src/test/cases/fib/fib.simple
@@ -0,0 +1,15 @@
+val fib = {int n ->
+    int temp=0;
+    int f1=1;
+    int f2=1;
+    int i=n;
+    while( i>1 ){
+        temp = f1+f2;
+        f1=f2;
+        f2=temp;
+        i=i-1;
+    }
+    return f2;
+};
+
+fib(10);
\ No newline at end of file
diff --git a/seaofnodes/src/test/cases/fib/fib.simple_ir b/seaofnodes/src/test/cases/fib/fib.simple_ir
new file mode 100644
index 0000000..d45c9a8
--- /dev/null
+++ b/seaofnodes/src/test/cases/fib/fib.simple_ir
@@ -0,0 +1,46 @@
+START:                            [[   ]]
+   1 Start   ____ ____ ____       [[     4    5    9   16   18    8    6   20  ]]  [  Ctrl, #TOP, int]
+
+L8:                               [[  START      ]]
+   8 main    ____    1            [[    10   17   57   58   60  ]]  Ctrl
+  10 $rpc       8    9            [[    14       ]]  $[ALL]
+  17 $mem       8   16            [[    60       ]]  #BOT
+  58 #10        8                 [[    60       ]]  10
+  57 #{ int     8                 [[    60       ]]  { int -> int #1}
+  60 Call       8   17   58   57  [[    61       ]]  Ctrl
+
+L20:                              [[  START      ]]
+  20 fib     ____    1            [[    22   29   31   33   35  ]]  Ctrl
+  22 $rpc      20    9            [[    26       ]]  $[ALL]
+  29 $mem      20   16            [[    26       ]]  #BOT
+  31 n         20   18            [[    38       ]]  int
+  33 #1        20                 [[    50   52   39   54  ]]  1
+
+LOOP35:                           [[  L20       L42        ]]
+  35 Loop    ____   20   42       [[    52   38   50   39   41  ]]  Ctrl
+  52 Phi_f2    35   33   51       [[    50   26   51  ]]  int
+  38 Phi_i     35   31   54       [[    39   54  ]]  int
+  50 Phi_f1    35   33   52       [[    51       ]]  int
+  39 LT        35   33   38       [[    41       ]]  bool
+  41 If        35   39            [[    42   43  ]]  [  Ctrl, Ctrl]
+
+L61:                              [[  L8         ]]
+  61 CallEnd   60                 [[    63   65   66  ]]  [  Ctrl, #BOT, int]
+  65 $mem      61                 [[    14       ]]  #BOT
+  66 #2        61                 [[    14       ]]  int
+
+L63:                              [[  L61        ]]
+  63 $ctrl     61                 [[    14       ]]  Ctrl
+  14 Return    63   65   66   10  [[     2       ]]  [  Ctrl, #BOT, int]
+
+L43:                              [[  LOOP35     ]]
+  43 False     41                 [[    26       ]]  Ctrl
+  26 Return    43   29   52   22  [[     2       ]]  [  Ctrl, #BOT, int]
+
+L42:                              [[  LOOP35     ]]
+  42 True      41                 [[    51   54   35  ]]  Ctrl
+  54 Sub       42   38   33       [[    38       ]]  int
+  51 Add       42   52   50       [[    52       ]]  int
+
+L2:                               [[   ]]
+   2 Stop      14   26            [[             ]]  Bot
diff --git a/seaofnodes/src/test/cases/hello/hello.ez b/seaofnodes/src/test/cases/hello/hello.ez
new file mode 100644
index 0000000..4814721
--- /dev/null
+++ b/seaofnodes/src/test/cases/hello/hello.ez
@@ -0,0 +1,4 @@
+func main()->Int
+{
+    return 1
+}
\ No newline at end of file
diff --git a/seaofnodes/src/test/cases/mergsort/sort.ez b/seaofnodes/src/test/cases/mergsort/sort.ez
new file mode 100644
index 0000000..8ad506d
--- /dev/null
+++ b/seaofnodes/src/test/cases/mergsort/sort.ez
@@ -0,0 +1,78 @@
+// based on the top-down version from https://en.wikipedia.org/wiki/Merge_sort
+// via https://github.com/SeaOfNodes/Simple
+func merge_sort(a: [Int], b: [Int], n: Int)
+{
+    copy_array(a, 0, n, b)
+    split_merge(a, 0, n, b)
+}
+
+func split_merge(b: [Int], begin: Int, end: Int, a: [Int])
+{
+    if (end - begin <= 1)
+        return;
+    var middle = (end + begin) / 2
+    split_merge(a, begin, middle, b)
+    split_merge(a, middle, end, b)
+    merge(b, begin, middle, end, a)
+}
+
+func merge(b: [Int], begin: Int, middle: Int, end: Int, a: [Int])
+{
+    var i = begin
+    var j = middle
+    var k = begin
+    while (k < end) {
+        // && and ||
+        var cond = 0
+        if (i < middle) {
+            if (j >= end)          cond = 1;
+            else if (a[i] <= a[j]) cond = 1;
+        }
+        if (cond)
+        {
+            b[k] = a[i]
+            i = i + 1
+        }
+        else
+        {
+            b[k] = a[j]
+            j = j + 1
+        }
+        k = k + 1
+    }
+}
+
+func copy_array(a: [Int], begin: Int, end: Int, b: [Int])
+{
+    var k = begin
+    while (k < end)
+    {
+        b[k] = a[k]
+        k = k + 1
+    }
+}
+
+func eq(a: [Int], b: [Int], n: Int)->Int
+{
+    var result = 1
+    var i = 0
+    while (i < n)
+    {
+        if (a[i] != b[i])
+        {
+            result = 0
+            break
+        }
+        i = i + 1
+    }
+    return result
+}
+
+func main()->Int
+{
+    var a = new [Int]{10,9,8,7,6,5,4,3,2,1}
+    var b = new [Int]{ 0,0,0,0,0,0,0,0,0,0}
+    var expect = new [Int]{1,2,3,4,5,6,7,8,9,10}
+    merge_sort(a, b, 10)
+    return eq(a,expect,10)
+}
diff --git a/seaofnodes/src/test/java/com/compilerprogramming/ezlang/compiler/TestSONTypes.java b/seaofnodes/src/test/java/com/compilerprogramming/ezlang/compiler/TestSONTypes.java
index 6584c71..78c7e87 100644
--- a/seaofnodes/src/test/java/com/compilerprogramming/ezlang/compiler/TestSONTypes.java
+++ b/seaofnodes/src/test/java/com/compilerprogramming/ezlang/compiler/TestSONTypes.java
@@ -1,6 +1,7 @@
 package com.compilerprogramming.ezlang.compiler;
 
 import com.compilerprogramming.ezlang.compiler.codegen.CodeGen;
+import org.junit.Before;
 import org.junit.Test;
 
 import static com.compilerprogramming.ezlang.compiler.Main.PORTS;
@@ -25,7 +26,7 @@ public void test1() {
     @Test
     public void test2() {
         String src = """
-struct T { var i: S }
+struct T1 { var i: S }
 struct S { var i: Int }
 """;
         compileSrc(src);
@@ -34,7 +35,7 @@ public void test2() {
     @Test
     public void test3() {
         String src = """
-struct T { var next: T? }
+struct T2 { var next: T2? }
 """;
         compileSrc(src);
     }
@@ -42,7 +43,7 @@ public void test3() {
     @Test
     public void test4() {
         String src = """
-struct T { var arr: [Int] }
+struct T3 { var arr: [Int] }
 """;
         compileSrc(src);
     }
@@ -131,9 +132,9 @@ func foo()->[Int] {
     @Test
     public void test14() {
         String src = """
-struct T { var i: Int }
-func foo()->T { 
-  return new T{i=1};
+struct T4 { var i: Int }
+func foo()->T4 { 
+  return new T4{i=1};
 }
 """;
         compileSrc(src);
@@ -142,9 +143,9 @@ func foo()->T {
     @Test
     public void test15() {
         String src = """
-struct T { var i: Int; var j: Int }
-func foo()->T { 
-  return new T{i=23, j=32};
+struct T5 { var i: Int; var j: Int }
+func foo()->T5 { 
+  return new T5{i=23, j=32};
 }
 """;
         compileSrc(src);
@@ -163,9 +164,9 @@ func foo()->Int {
     @Test
     public void test17() {
         String src = """
-struct T { var i: Int; var j: Int }
+struct T5 { var i: Int; var j: Int }
 func foo()->Int { 
-  return new T{i=23, j=32}.j;
+  return new T5{i=23, j=32}.j;
 }
 """;
         compileSrc(src);

From a857461336517f15c9885fb6827994211cbdc34c Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Sun, 6 Apr 2025 16:03:41 +0100
Subject: [PATCH 054/119] Sync upstream Simple project to commit
 https://github.com/SeaOfNodes/Simple/commit/bb97129daa1504d67a8e3a76f16d6db15f7e5e7b

---
 .../ezlang/compiler/IterPeeps.java            |   2 +
 .../ezlang/compiler/codegen/Coalesce.java     |  20 +-
 .../ezlang/compiler/codegen/CodeGen.java      |  17 +-
 .../ezlang/compiler/codegen/ElfFile.java      | 167 ++++++------
 .../ezlang/compiler/codegen/Encoding.java     | 243 +++++++++---------
 .../compiler/codegen/GlobalCodeMotion.java    |  18 +-
 .../ezlang/compiler/codegen/IFG.java          |  62 +++--
 .../ezlang/compiler/codegen/LRG.java          |   5 +-
 .../ezlang/compiler/codegen/LinkMem.java      |  15 ++
 .../ezlang/compiler/codegen/Machine.java      |   2 +-
 .../ezlang/compiler/codegen/RegAlloc.java     | 173 ++++++++++---
 .../ezlang/compiler/codegen/RegMask.java      |  24 +-
 .../ezlang/compiler/codegen/RegMaskRW.java    |  31 +++
 .../ezlang/compiler/nodes/BoolNode.java       |  13 +
 .../ezlang/compiler/nodes/CFGNode.java        |   9 +-
 .../ezlang/compiler/nodes/CallNode.java       |   2 +
 .../ezlang/compiler/nodes/ConstantNode.java   |   4 +-
 .../ezlang/compiler/nodes/FRefNode.java       |   2 +-
 .../ezlang/compiler/nodes/IfNode.java         |  14 +
 .../ezlang/compiler/nodes/LoopNode.java       |  37 ++-
 .../ezlang/compiler/nodes/MemOpNode.java      |   2 +-
 .../ezlang/compiler/nodes/Node.java           |  34 +--
 .../compiler/nodes/cpus/arm/AddARM.java       |   2 +-
 .../compiler/nodes/cpus/arm/AddFARM.java      |   2 +-
 .../compiler/nodes/cpus/arm/AddIARM.java      |   2 +-
 .../compiler/nodes/cpus/arm/AndARM.java       |   2 +-
 .../compiler/nodes/cpus/arm/AndIARM.java      |   2 +-
 .../compiler/nodes/cpus/arm/AsrARM.java       |   2 +-
 .../compiler/nodes/cpus/arm/AsrIARM.java      |   2 +-
 .../compiler/nodes/cpus/arm/BranchARM.java    |  10 +-
 .../compiler/nodes/cpus/arm/CallARM.java      |   4 +-
 .../compiler/nodes/cpus/arm/CallRRARM.java    |   2 +-
 .../compiler/nodes/cpus/arm/CmpARM.java       |   2 +-
 .../compiler/nodes/cpus/arm/CmpIARM.java      |   6 +-
 .../compiler/nodes/cpus/arm/DivARM.java       |   2 +-
 .../compiler/nodes/cpus/arm/DivFARM.java      |   2 +-
 .../compiler/nodes/cpus/arm/FloatARM.java     |   6 +-
 .../compiler/nodes/cpus/arm/FunARM.java       |   2 +-
 .../compiler/nodes/cpus/arm/I2F8ARM.java      |   2 +-
 .../compiler/nodes/cpus/arm/IntARM.java       |   8 +-
 .../compiler/nodes/cpus/arm/LoadARM.java      |   4 +-
 .../compiler/nodes/cpus/arm/LslARM.java       |   2 +-
 .../compiler/nodes/cpus/arm/LslIARM.java      |   2 +-
 .../compiler/nodes/cpus/arm/LsrARM.java       |   2 +-
 .../compiler/nodes/cpus/arm/LsrIARM.java      |   2 +-
 .../compiler/nodes/cpus/arm/MulARM.java       |   2 +-
 .../compiler/nodes/cpus/arm/MulFARM.java      |   2 +-
 .../compiler/nodes/cpus/arm/NewARM.java       |   2 +-
 .../compiler/nodes/cpus/arm/NotARM.java       |   4 +-
 .../ezlang/compiler/nodes/cpus/arm/OrARM.java |   2 +-
 .../compiler/nodes/cpus/arm/OrIARM.java       |   2 +-
 .../compiler/nodes/cpus/arm/RetARM.java       |   4 +-
 .../compiler/nodes/cpus/arm/SetARM.java       |   3 +-
 .../compiler/nodes/cpus/arm/SplitARM.java     |  20 +-
 .../compiler/nodes/cpus/arm/StoreARM.java     |   4 +-
 .../compiler/nodes/cpus/arm/SubARM.java       |   2 +-
 .../compiler/nodes/cpus/arm/SubFARM.java      |   2 +-
 .../compiler/nodes/cpus/arm/SubIARM.java      |   2 +-
 .../compiler/nodes/cpus/arm/TFPARM.java       |   9 +-
 .../compiler/nodes/cpus/arm/UJmpARM.java      |   4 +-
 .../compiler/nodes/cpus/arm/XorARM.java       |   2 +-
 .../compiler/nodes/cpus/arm/XorIARM.java      |   2 +-
 .../ezlang/compiler/nodes/cpus/arm/arm.java   | 172 +++++++++++--
 .../compiler/nodes/cpus/riscv/AUIPC.java      |   8 +-
 .../compiler/nodes/cpus/riscv/AddIRISC.java   |   2 +-
 .../compiler/nodes/cpus/riscv/AndIRISC.java   |   4 +-
 .../compiler/nodes/cpus/riscv/AndRISC.java    |   4 +-
 .../compiler/nodes/cpus/riscv/BranchRISC.java |   5 +-
 .../compiler/nodes/cpus/riscv/CallRISC.java   |   4 +-
 .../compiler/nodes/cpus/riscv/CallRRISC.java  |   2 +-
 .../compiler/nodes/cpus/riscv/CastRISC.java   |   2 +-
 .../compiler/nodes/cpus/riscv/DivRISC.java    |   2 +-
 .../compiler/nodes/cpus/riscv/FltRISC.java    |  13 +-
 .../compiler/nodes/cpus/riscv/FunRISC.java    |   2 +-
 .../compiler/nodes/cpus/riscv/I2F8RISC.java   |   2 +-
 .../compiler/nodes/cpus/riscv/IntRISC.java    |   2 +-
 .../ezlang/compiler/nodes/cpus/riscv/LUI.java |   2 +-
 .../compiler/nodes/cpus/riscv/LoadRISC.java   |   9 +-
 .../compiler/nodes/cpus/riscv/MemOpRISC.java  |  34 ++-
 .../compiler/nodes/cpus/riscv/MulRISC.java    |   2 +-
 .../compiler/nodes/cpus/riscv/NJmpRISC.java   |  46 ++++
 .../compiler/nodes/cpus/riscv/NewRISC.java    |   5 +-
 .../compiler/nodes/cpus/riscv/NotRISC.java    |   2 +-
 .../compiler/nodes/cpus/riscv/OrIRISC.java    |   4 +-
 .../compiler/nodes/cpus/riscv/OrRISC.java     |   2 +-
 .../compiler/nodes/cpus/riscv/RetRISC.java    |   6 +-
 .../compiler/nodes/cpus/riscv/SetFRISC.java   |   2 +-
 .../compiler/nodes/cpus/riscv/SetIRISC.java   |   8 +-
 .../compiler/nodes/cpus/riscv/SetRISC.java    |  13 +-
 .../compiler/nodes/cpus/riscv/SllIRISC.java   |   4 +-
 .../compiler/nodes/cpus/riscv/SllRISC.java    |   2 +-
 .../compiler/nodes/cpus/riscv/SplitRISC.java  |  26 +-
 .../compiler/nodes/cpus/riscv/SraIRISC.java   |   4 +-
 .../compiler/nodes/cpus/riscv/SraRISC.java    |   4 +-
 .../compiler/nodes/cpus/riscv/SrlIRISC.java   |   4 +-
 .../compiler/nodes/cpus/riscv/SrlRISC.java    |   2 +-
 .../compiler/nodes/cpus/riscv/StoreRISC.java  |   7 +-
 .../compiler/nodes/cpus/riscv/SubRISC.java    |   2 +-
 .../compiler/nodes/cpus/riscv/TFPRISC.java    |  11 +-
 .../compiler/nodes/cpus/riscv/UJmpRISC.java   |   9 +-
 .../compiler/nodes/cpus/riscv/XorIRISC.java   |   4 +-
 .../compiler/nodes/cpus/riscv/XorRISC.java    |   2 +-
 .../compiler/nodes/cpus/riscv/riscv.java      |  55 ++--
 .../nodes/cpus/x86_64_v2/CmpIX86.java         |   2 +-
 .../nodes/cpus/x86_64_v2/CmpMemX86.java       |   4 +-
 .../compiler/nodes/cpus/x86_64_v2/CmpX86.java |  18 +-
 .../compiler/nodes/cpus/x86_64_v2/FltX86.java |   4 +-
 .../compiler/nodes/cpus/x86_64_v2/JmpX86.java |   7 +-
 .../nodes/cpus/x86_64_v2/LoadX86.java         |  64 ++---
 .../nodes/cpus/x86_64_v2/MemOpX86.java        |   2 +-
 .../compiler/nodes/cpus/x86_64_v2/RegX86.java |   3 +-
 .../nodes/cpus/x86_64_v2/SarIX86.java         |  35 ++-
 .../compiler/nodes/cpus/x86_64_v2/SetX86.java |   8 +-
 .../nodes/cpus/x86_64_v2/ShlIX86.java         |  36 ++-
 .../nodes/cpus/x86_64_v2/ShrIX86.java         |  31 ++-
 .../nodes/cpus/x86_64_v2/SplitX86.java        |  22 +-
 .../nodes/cpus/x86_64_v2/StoreX86.java        |  46 ++--
 .../nodes/cpus/x86_64_v2/x86_64_v2.java       | 155 +++++------
 .../ezlang/compiler/print/ASMPrinter.java     |  64 +++--
 .../ezlang/compiler/print/IRPrinter.java      |   1 +
 .../ezlang/compiler/sontypes/SONType.java     |   3 +-
 .../compiler/sontypes/SONTypeTuple.java       |  15 ++
 .../ezlang/compiler/Simple.java               |   2 +-
 .../ezlang/compiler/TestSONTypes.java         |   2 +-
 124 files changed, 1274 insertions(+), 781 deletions(-)
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/LinkMem.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/RegMaskRW.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/NJmpRISC.java

diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/IterPeeps.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/IterPeeps.java
index f58ae9f..c18aa90 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/IterPeeps.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/IterPeeps.java
@@ -49,6 +49,7 @@ public class IterPeeps {
 
     public IterPeeps( long seed ) { _work = new WorkList<>(seed); }
 
+    @SuppressWarnings("unchecked")
     public <N extends Node> N add( N n ) { return (N)_work.push(n); }
 
     public void addAll( Ary<Node> ary ) { _work.addAll(ary); }
@@ -126,6 +127,7 @@ private boolean progressOnList(CodeGen code) {
      * Classic WorkList, with a fast add/remove, dup removal, random pull.
      * The Node's nid is used to check membership in the worklist.
      */
+    @SuppressWarnings("unchecked")
     public static class WorkList<E extends Node> {
 
         private Node[] _es;
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/Coalesce.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/Coalesce.java
index a5b9183..47c75e6 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/Coalesce.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/Coalesce.java
@@ -10,20 +10,6 @@
 abstract public class Coalesce {
 
     public static boolean coalesce( int round, RegAlloc alloc ) {
-        // Convert the 2-D array of bits (a 1-D array of BitSets) into an
-        // adjacency matrix.
-        int maxlrg = alloc._LRGS.length;
-        for( int i=1; i<maxlrg; i++ ) {
-            BitSet ifg = IFG.IFG.atX(i);
-            if( ifg != null ) {
-                LRG lrg0 = alloc._LRGS[i];
-                for( int lrg = ifg.nextSetBit(0); lrg>=0; lrg=ifg.nextSetBit(lrg+1) ) {
-                    LRG lrg1 = alloc._LRGS[lrg];
-                    lrg0.addNeighbor(lrg1);
-                    lrg1.addNeighbor(lrg0);
-                }
-            }
-        }
 
         // Walk all the splits, looking for coalesce chances
         boolean progress = false;
@@ -37,7 +23,7 @@ public static boolean coalesce( int round, RegAlloc alloc ) {
                 if( v1 != v2 ) {
                     LRG ov1 = v1, ov2 = v2;
                     // Get the smaller neighbor count in v1
-                    if( (v1._adj==null ? 0 : v1._adj._len) > (v2._adj==null ? 0 : v2._adj._len) )
+                    if( v1.nadj() > v2.nadj() )
                         { v1 = v2; v2 = alloc.lrg(n); }
                     int v2len = v2._adj==null ? 0 : v2._adj._len;
                     // See that they do not conflict (coalescing would make a self-conflict)
@@ -58,9 +44,9 @@ public static boolean coalesce( int round, RegAlloc alloc ) {
                     // Most constrained mask
                     RegMask mask = v1._mask==v2._mask ? v1._mask : v1._mask.copy().and(v2._mask);
                     // Check for capacity
-                    if( (v2._adj==null ? 0 : v2._adj._len) >= mask.size() ) {
+                    if( v2.nadj() >= mask.size() ) {
                         // Fails capacity, will not be trivial colorable
-                        v2._adj.setLen(v2len);
+                        if( v2._adj!=null ) v2._adj.setLen(v2len);
                         continue;
                     }
 
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/CodeGen.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/CodeGen.java
index 522cfd3..22e05c3 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/CodeGen.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/CodeGen.java
@@ -11,7 +11,9 @@
 import java.util.HashMap;
 import java.util.IdentityHashMap;
 
+@SuppressWarnings("unchecked")
 public class CodeGen {
+    public static final String PORTS = "com.compilerprogramming.ezlang.compiler.nodes.cpus";
     // Last created CodeGen as a global; used all over to avoid passing about a
     // "context".
     public static CodeGen CODE;
@@ -187,7 +189,8 @@ public CodeGen typeCheck() {
 
     // Convert to target hardware nodes
     public int _tInsSel;
-    public CodeGen instSelect( String base, String cpu, String callingConv ) {
+    public CodeGen instSelect( String cpu, String callingConv ) { return instSelect(cpu,callingConv,PORTS); }
+    public CodeGen instSelect( String cpu, String callingConv, String base ) {
         assert _phase.ordinal() <= Phase.TypeCheck.ordinal();
         _phase = Phase.InstSelect;
 
@@ -313,16 +316,21 @@ public String reg(Node n) {
     // ---------------------------
     // Encoding
     public int _tEncode;
+    public boolean _JIT;
     public Encoding _encoding;
-    public CodeGen encode() {
+    public CodeGen encode(boolean jit) {
         assert _phase == Phase.RegAlloc;
         _phase = Phase.Encoding;
         long t0 = System.currentTimeMillis();
         _encoding = new Encoding(this);
+        _JIT = jit;
         _encoding.encode();
         _tEncode = (int)(System.currentTimeMillis() - t0);
         return this;
     }
+    public CodeGen encode() {
+        return encode(false);
+    }
     // Encoded binary, no relocation info
     public byte[] binary() { return _encoding.bits(); }
 
@@ -331,12 +339,13 @@ public CodeGen encode() {
     public CodeGen exportELF(String fname) throws IOException {
         assert _phase == Phase.Encoding;
         _phase = Phase.Export;
-        ElfFile obj = new ElfFile(this);
-        obj.export(fname);
+        if( fname == null ) new LinkMem(this).link(); // In memory patching
+        else new ElfFile(this).export(fname); // External ELF file
         return this;
     }
 
     // ---------------------------
+    public boolean _asmLittle=true;
     SB asm(SB sb) { return ASMPrinter.print(sb,this); }
     public String asm() { return asm(new SB()).toString(); }
 
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/ElfFile.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/ElfFile.java
index eee43a1..490b921 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/ElfFile.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/ElfFile.java
@@ -4,14 +4,12 @@
 import com.compilerprogramming.ezlang.compiler.nodes.*;
 import com.compilerprogramming.ezlang.compiler.sontypes.*;
 
-import java.util.Map;
-import java.util.HashMap;
+import java.io.*;
+
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
-import java.io.IOException;
-import java.io.FileOutputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.BufferedOutputStream;
+import java.util.HashMap;
+import java.util.Map;
 
 public class ElfFile {
 
@@ -69,6 +67,10 @@ public static class Symbol {
         // top 4bits are the "bind", bottom 4 are "type"
         int _info;
 
+        // bind 1011
+        // 00001111
+        // 10110000
+
         Symbol(String name, int parent, int bind, int type) {
             _name = name;
             _parent = parent;
@@ -78,6 +80,7 @@ public static class Symbol {
         void writeHeader(ByteBuffer out) {
             out.putInt(_name_pos);         // name
             out.put((byte) _info);         // info
+            // default visibility
             out.put((byte) 0);             // other
             out.putShort((short) _parent); // shndx
             out.putLong(_value);           // value
@@ -117,7 +120,7 @@ void writeHeader(ByteBuffer out) {
             out.putLong(size());       // size
             out.putInt(_link);         // link
             out.putInt(_info);         // info
-            out.putLong(1);            // addralign
+            out.putLong(16);           // addralign
             if (_type == 2) {
                 out.putLong(SYMBOL_SIZE);// entsize
             } else if (_type == 4) {
@@ -140,7 +143,7 @@ public static class SymbolSection extends Section {
         }
 
         @Override
-            void writeHeader(ByteBuffer out) {
+        void writeHeader(ByteBuffer out) {
             // points to string table section
             _link = 1;
 
@@ -159,23 +162,22 @@ void push(Symbol s) {
         }
 
         @Override
-            void write(ByteBuffer out) {
+        void write(ByteBuffer out) {
+            // Index 0 both designates the first entry in the table and serves as the undefined symbol index
             for( int i = 0; i < SYMBOL_SIZE/4; i++ ) {
                 out.putInt(0);
             }
-            int num=1;
+            // index is already set
             for( Symbol s : _loc ) {
-                s._index = num++;
                 s.writeHeader(out);
             }
             for( Symbol s : _symbols ) {
-                s._index = num++;
                 s.writeHeader(out);
             }
         }
 
         @Override
-            int size() {
+        int size() {
             return (1 + _symbols.len() + _loc.len()) * SYMBOL_SIZE;
         }
     }
@@ -210,54 +212,21 @@ private void pushSection(Section s) {
         s._index = _sections._len;
     }
 
+    /* creates function and stores where it starts*/
     private final HashMap<SONTypeFunPtr,Symbol> _funcs = new HashMap<>();
     private void encodeFunctions(SymbolSection symbols, DataSection text) {
-        int func_start = 0;
         for( int i=0; i<_code._cfg._len; i++ ) {
-            Node bb = _code._cfg.at(i);
-            if( bb instanceof FunNode f ) {
-                // skip until the function ends
-                while( !(_code._cfg.at(i) instanceof ReturnNode) ) {
-                    i++;
-                }
-
-                Node r = _code._cfg.at(i);
-                int end = _code._encoding._opStart[r._nid] + _code._encoding._opLen[r._nid];
-
-                Symbol func = new Symbol(f._name, text._index, SYM_BIND_GLOBAL, SYM_TYPE_FUNC);
-                func._size = end - func_start;
-                func._value = func_start;
-                symbols.push(func);
-                _funcs.put(f.sig(), func);
-
-                // next function starts where the last one ends
-                func_start = end;
-            }
-        }
-    }
-
-    public final HashMap<SONType,Symbol> _bigCons = new HashMap<>();
-    private void encodeConstants(SymbolSection symbols, DataSection rdata) {
-        int cnt = 0;
-        for (Map.Entry<Node,SONType> e : _code._encoding._bigCons.entrySet()) {
-            if (_bigCons.get(e.getValue()) != null) {
-                continue;
-            }
-
-            Symbol glob = new Symbol("GLOB$"+cnt, rdata._index, SYM_BIND_GLOBAL, SYM_TYPE_FUNC);
-            glob._value = rdata._contents.size();
-            symbols.push(glob);
-
-            SONType t = e.getValue();
-            if ( t instanceof SONTypeFloat tf ) {
-                write8(rdata._contents, Double.doubleToLongBits(tf._con));
-            } else {
-                throw Utils.TODO();
-            }
-
-            glob._size = rdata._contents.size() - glob._value;
-            _bigCons.put(e.getValue(), glob);
-            cnt++;
+            if( !(_code._cfg.at(i) instanceof FunNode fun) ) continue;
+            // skip until the function ends
+            while( !(_code._cfg.at(i) instanceof ReturnNode ret) )
+                i++;
+            int end = _code._encoding._opStart[ret._nid] + _code._encoding._opLen[ret._nid];
+
+            Symbol func = new Symbol(fun._name, text._index, SYM_BIND_GLOBAL, SYM_TYPE_FUNC);
+            func._value = _code._encoding._opStart[fun._nid];
+            func._size = end - func._value;
+            symbols.push(func);
+            _funcs.put(fun.sig(), func);
         }
     }
 
@@ -271,67 +240,90 @@ public void export(String fname) throws IOException {
         SymbolSection symbols = new SymbolSection(".symtab", 2 /* SHT_STRTAB */);
         pushSection(symbols);
 
+        // zero flag by default
         // we've already constructed this entire section in the encoding phase
         DataSection text = new DataSection(".text", 1 /* SHT_PROGBITS */, _code._encoding._bits);
         text._flags = SHF_ALLOC | SHF_WRITE | SHF_EXECINSTR;
         pushSection(text);
 
-        DataSection rdata = new DataSection(".rodata", 1 /* SHT_PROGBITS */);
+        // Build and write constant pool
+        Encoding.BAOS cpool = new Encoding.BAOS();
+        Encoding enc = _code._encoding;
+        enc.writeConstantPool(cpool,false);
+        DataSection rdata = new DataSection(".rodata", 1 /* SHT_PROGBITS */, cpool);
         rdata._flags = SHF_ALLOC;
         pushSection(rdata);
 
         // populate function symbols
         encodeFunctions(symbols, text);
-        // populate big constants
-        // encodeConstants(symbols, rdata);
+
+        int idx = 1;
+        for( Section s : _sections ) {
+            Symbol sym = new Symbol(s._name, idx++, SYM_BIND_LOCAL, SYM_TYPE_SECTION);
+            // we can reuse the same name pos from the actual section
+            sym._name_pos = s._name_pos;
+            sym._size = s.size();
+            symbols.push(sym);
+        }
+
+        // calculate local index
+        int num = 1;
+        for( Symbol s : symbols._loc )
+            s._index = num++;
+        // extra space for .rela.text
+        int start_global = num+1; // Add one to skip the final .rela.text local symbol
+        for( Symbol a: symbols._symbols )
+            a._index = start_global++;
+        int bigConIdx = start_global;
+        start_global += enc._bigCons.size();
 
         // create .text relocations
         DataSection text_rela = new DataSection(".rela.text", 4 /* SHT_RELA */);
-        for( Node n : _code._encoding._externals.keySet()) {
+        for( Node n : enc._externals.keySet()) {
             int nid    = n._nid;
-            String extern = _code._encoding._externals.get(n);
-            int sym_id = _funcs.get(extern)._index;
-            int offset = _code._encoding._opStart[nid] + _code._encoding._opLen[nid] - 4;
+            String extern = enc._externals.get(n);
+
+            Symbol sym = new Symbol(extern, 0, SYM_BIND_GLOBAL, SYM_TYPE_NOTYPE);
+            sym._index = start_global++;
+            symbols.push(sym);
+
+            int offset = enc._opStart[nid] + enc._opLen[nid] - 4;
 
             // u64 offset
             write8(text_rela._contents, offset);
             // u64 info
-            write8(text_rela._contents, ((long)sym_id << 32L) | 4L /* PLT32 */);
+            write8(text_rela._contents, ((long)sym._index << 32L) | 4L /* PLT32 */);
             // i64 addend
             write8(text_rela._contents, -4);
         }
-        // relocations to constants
-        if (false) for (Map.Entry<Node,SONType> e : _code._encoding._bigCons.entrySet()) {
-            int nid    = e.getKey()._nid;
-            int sym_id = _bigCons.get(e.getValue())._index;
-            int offset = _code._encoding._opStart[nid] + _code._encoding._opLen[nid] - 4;
 
-            // u64 offset
-            write8(text_rela._contents, offset);
-            // u64 info
-            write8(text_rela._contents, ((long)sym_id << 32L) | 1L /* PC32 */);
-            // i64 addend
+        // Write relocations for the constant pool
+        for( Encoding.Relo relo : enc._bigCons.values() ) {
+            Symbol glob = new Symbol("GLOB$"+bigConIdx, rdata._index, SYM_BIND_GLOBAL, SYM_TYPE_FUNC);
+            glob._value = relo._target;
+            glob._size = 1 << relo._t.log_size();
+            glob._index = bigConIdx++;
+            symbols.push(glob);
+            write8(text_rela._contents, relo._opStart+relo._off);
+            write8(text_rela._contents, ((long)glob._index << 32L) | relo._elf );
             write8(text_rela._contents, -4);
         }
+
         text_rela._flags = SHF_INFO_LINK;
         text_rela._link = 2;
         text_rela._info = text._index;
         pushSection(text_rela);
 
+        Symbol sym = new Symbol(text_rela._name, num++, SYM_BIND_LOCAL, SYM_TYPE_SECTION);
+        sym._name_pos = text_rela._name_pos;
+        sym._size = text_rela.size();
+        symbols.push(sym);
+
         // populate string table
         for( Section s : _sections ) { s._name_pos = writeCString(strtab, s._name); }
         for( Symbol s : symbols._symbols ) { s._name_pos = writeCString(strtab, s._name); }
         for( Symbol s : symbols._loc ) { s._name_pos = writeCString(strtab, s._name); }
 
-        int idx = 1;
-        for( Section s : _sections ) {
-            Symbol sym = new Symbol(s._name, idx++, SYM_BIND_LOCAL, SYM_TYPE_SECTION);
-            // we can reuse the same name pos from the actual section
-            sym._name_pos = s._name_pos;
-            sym._size = s.size();
-            symbols.push(sym);
-        }
-
         int size = 64; // ELF header
         // size of all section data
         for( Section s : _sections ) {
@@ -379,7 +371,10 @@ public void export(String fname) throws IOException {
         }
         assert out.position() == size;
 
-        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(fname));
+        File file = new File(fname);
+        if( file.getParentFile()!=null )
+          file.getParentFile().mkdirs();
+        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file));
         bos.write(out.array());
         bos.close();
     }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/Encoding.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/Encoding.java
index a30447d..6696e1c 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/Encoding.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/Encoding.java
@@ -44,6 +44,20 @@ public static class BAOS extends ByteArrayOutputStream {
     public int [] _opStart;     // Start  of opcodes, by _nid
     public byte[] _opLen;       // Length of opcodes, by _nid
 
+    // Big Constant relocation info.
+    public static class Relo {
+        public final Node _op;
+        public final SONType _t;          // Constant type
+        public final byte _off;        // Offset from start of opcode
+        public final byte _elf;        // ELF relocation type, e.g. 2/PC32
+        public int _target;      // Where constant is finally placede
+        public int _opStart;     // Opcode start
+        Relo( Node op, SONType t, byte off, byte elf ) {
+            _op=op;  _t=t;  _off=off; _elf=elf;
+        }
+    }
+    public final HashMap<Node,Relo> _bigCons = new HashMap<>();
+
     Encoding( CodeGen code ) { _code = code; }
 
     // Shortcut to the defining register
@@ -51,26 +65,17 @@ public short reg(Node n) {
         return _code._regAlloc.regnum(n);
     }
 
-    public Encoding add1( int op ) { _bits.write(op); return this; }
-
-    public void add2( int op ) {
-        _bits.write(op    );
-        _bits.write(op>> 8);
-    }
-    // Little endian write of a 32b opcode
-    public void add4( int op ) {
-        _bits.write(op    );
-        _bits.write(op>> 8);
-        _bits.write(op>>16);
-        _bits.write(op>>24);
+    public int read1(int idx) {
+        byte[] buf = _bits.buf();
+        return (buf[idx]&0xFF);
     }
-    public void add8( long i64 ) {
-        add4((int) i64     );
-        add4((int)(i64>>32));
+    public int read2(int idx) {
+        byte[] buf = _bits.buf();
+        return (buf[idx]&0xFF) | (buf[idx+1]&0xFF) <<8;
     }
     public int read4(int idx) {
         byte[] buf = _bits.buf();
-        return buf[idx] | (buf[idx+1]&0xFF) <<8 | (buf[idx+2]&0xFF)<<16 | (buf[idx+3]&0xFF)<<24;
+        return (buf[idx]&0xFF) | (buf[idx+1]&0xFF) <<8 | (buf[idx+2]&0xFF)<<16 | (buf[idx+3]&0xFF)<<24;
     }
     public long read8(int idx) {
         return (read4(idx) & 0xFFFFFFFFL) | read4(idx+4);
@@ -88,11 +93,32 @@ public void patch4( int idx, int val ) {
         buf[idx+3] = (byte)(val>>24);
     }
 
-    void pad8() {
-        while( (_bits.size()+7 & -8) > _bits.size() )
-            _bits.write(0);
+    static void padN(int n, BAOS bits) {
+        while( (bits.size()+n-1 & -n) > bits.size() )
+            bits.write(0);
     }
 
+    // Convenience for writing log-N
+    static void addN( int log, SONType t, BAOS bits ) {
+        long x = t instanceof SONTypeInteger ti
+            ? ti.value()
+            : log==3
+            ? Double.doubleToRawLongBits(    ((SONTypeFloat)t).value())
+            : Float.floatToRawIntBits((float)((SONTypeFloat)t).value());
+        addN(log,x,bits);
+    }
+    static void addN( int log, long x, BAOS bits ) {
+        for( int i=0; i < 1<<log; i++ ) {
+            bits.write((int)x);
+            x >>= 8;
+        }
+    }
+
+    public Encoding add1( int op ) { addN(0,op,_bits); return this; }
+    public Encoding add2( int op ) { addN(1,op,_bits); return this; }
+    public Encoding add4( int op ) { addN(2,op,_bits); return this; }
+    public Encoding add8(long op ) { addN(3,op,_bits); return this; }
+
 
     // Nodes need "relocation" patching; things done after code is placed.
     // Record src and dst Nodes.
@@ -108,9 +134,7 @@ public Encoding relo( ConstantNode con ) {
         return this;
     }
     public void jump( CFGNode jmp, CFGNode dst ) {
-        while( dst.nOuts() == 1 ) // Skip empty blocks
-            dst = dst.uctrl();
-        _internals.put(jmp,dst);
+        _internals.put(jmp,dst.uctrlSkipEmpty());
     }
 
 
@@ -122,12 +146,11 @@ public Encoding external( Node call, String extern ) {
 
     // Store t as a 32/64 bit constant in the code space; generate RIP-relative
     // addressing to load it
-
-    public final HashMap<Node,SONType> _bigCons = new HashMap<>();
-    public final HashMap<Node,Integer> _cpool = new HashMap<>();
-    public void largeConstant( Node relo, SONType t ) {
+    public void largeConstant( Node relo, SONType t, int off, int elf ) {
         assert t.isConstant();
-        _bigCons.put(relo,t);
+        assert (byte)off == off;
+        assert (byte)elf == elf;
+        _bigCons.put(relo,new Relo(relo,t,(byte)off,(byte)elf));
     }
 
     void encode() {
@@ -141,16 +164,13 @@ void encode() {
         // Record opcode start and length.
         writeEncodings();
 
-        // Write any large constants into a constant pool; they
-        // are accessed by RIP-relative addressing.
-        writeConstantPool();
-
         // Short-form RIP-relative support: replace long encodings with short
         // encodings and compact the code, changing all the offsets.
         compactShortForm();
 
         // Patch RIP-relative and local encodings now.
         patchLocalRelocations();
+
     }
 
     // Basic block layout: invert branches to keep blocks in-order; insert
@@ -161,8 +181,11 @@ private void basicBlockLayout() {
         Ary<CFGNode> rpo = new Ary<>(CFGNode.class);
         BitSet visit = _code.visit();
         for( Node n : _code._start._outputs )
-            if( n instanceof FunNode fun )
+            if( n instanceof FunNode fun ) {
+                int x = rpo._len;
                 _rpo_cfg(fun, visit, rpo);
+                assert rpo.at(x) instanceof ReturnNode;
+            }
         rpo.add(_code._start);
 
         // Reverse in-place
@@ -186,9 +209,14 @@ private void _rpo_cfg(CFGNode bb, BitSet visit, Ary<CFGNode> rpo) {
             // If the *next* BB has already been visited, we may need an
             // unconditional jump here
             if( visit.get(next._nid) && !(next instanceof StopNode) ) {
-                // If all the blocks, in RPO order, to our target
-                // are empty, we will fall in and not need a jump.
-                if( next instanceof LoopNode || !isEmptyBackwardsScan(rpo,next) ) {
+                boolean needJump = next instanceof LoopNode
+                    // If backwards to a loop, and the block has statements,
+                    // will need a jump.  Empty blocks can just backwards branch.
+                    ? (bb.nOuts()>1)
+                    // Forwards jump.  If all the blocks, in RPO order, to our
+                    // target are empty, we will fall in and not need a jump.
+                    : !isEmptyBackwardsScan(rpo,next);
+                if( needJump ) {
                     CFGNode jmp = _code._mach.jump();
                     jmp.setDefX(0,bb);
                     next.setDef(next._inputs.find(bb),jmp);
@@ -224,15 +252,27 @@ private void _rpo_cfg(CFGNode bb, BitSet visit, Ary<CFGNode> rpo) {
                 t.invert();
                 f.invert();
                 CProjNode tmp=f; f=t; t=tmp; // Swap t/f
+                int d=tld; tld=fld; fld=d;   // Swap depth
             }
 
-            // Always visit the False side last (so True side first), so that
+            // Whichever side is visited first becomes last in the RPO.  With
+            // no loops, visit the False side last (so True side first) so that
             // when the False RPO visit returns, the IF is immediately next.
             // When the RPO is reversed, the fall-through path will always be
             // following the IF.
-            _rpo_cfg(t,visit,rpo);
-            _rpo_cfg(f,visit,rpo);
+
+            // If loops are involved, attempt to keep them in a line.  Visit
+            // the exits first, so they follow the loop body when the order gets
+            // reversed.
+            if( fld < tld || (fld==tld && f.nOuts()==1) ) {
+                _rpo_cfg(f,visit,rpo);
+                _rpo_cfg(t,visit,rpo);
+            } else {
+                _rpo_cfg(t,visit,rpo);
+                _rpo_cfg(f,visit,rpo);
+            }
         }
+        assert rpo._len>0 || bb instanceof ReturnNode;
         rpo.add(bb);
     }
 
@@ -253,6 +293,7 @@ private void writeEncodings() {
             if( !(bb instanceof MachNode mach0) )
                 _opStart[bb._nid] = _bits.size();
             else if( bb instanceof FunNode fun ) {
+                padN(16,_bits);
                 _fun = fun;     // Currently encoding function
                 _opStart[bb._nid] = _bits.size();
                 mach0.encoding( this );
@@ -266,44 +307,7 @@ else if( bb instanceof FunNode fun ) {
                 }
             }
         }
-        pad8();
-    }
-
-    // Write the constant pool
-    private void writeConstantPool() {
-        // TODO: Check for cpool dups
-        HashSet<SONType> ts = new HashSet<>();
-        for( SONType t : _bigCons.values() ) {
-            if( ts.contains(t) )
-                throw Utils.TODO(); // Dup!  Compress!
-            ts.add(t);
-        }
-
-        // Write the 8-byte constants
-        for( Node relo : _bigCons.keySet() ) {
-            SONType t = _bigCons.get(relo);
-            if( t.log_size()==3 ) {
-                // Map from relo to constant start
-                _cpool.put(relo,_bits.size());
-                long x = t instanceof SONTypeInteger ti
-                    ? ti.value()
-                    : Double.doubleToRawLongBits(((SONTypeFloat)t).value());
-                add8(x);
-            }
-        }
-
-        // Write the 4-byte constants
-        for( Node relo : _bigCons.keySet() ) {
-            SONType t = _bigCons.get(relo);
-            if( t.log_size()==2 ) {
-                // Map from relo to constant start
-                _cpool.put(relo,_bits.size());
-                int x = t instanceof SONTypeInteger ti
-                    ? (int)ti.value()
-                    : Float.floatToRawIntBits((float)((SONTypeFloat)t).value());
-                add4(x);
-            }
-        }
+        padN(16,_bits);
     }
 
     // Short-form RIP-relative support: replace short encodings with long
@@ -327,9 +331,14 @@ private void compactShortForm() {
             slide = 0;
             for( int i=0; i<len; i++ ) {
                 CFGNode bb = _code._cfg.at(i);
+                // Functions pad to align 16
+                if( bb instanceof FunNode ) {
+                    int newStart = _opStart[bb._nid]+slide;
+                    slide += (newStart+15 & -16)-newStart;
+                }
                 _opStart[bb._nid] += slide;
                 if( bb instanceof RIPRelSize riprel ) {
-                    CFGNode target = (CFGNode)bb.out(0);
+                    CFGNode target = ((CFGNode)bb.out(0)).uctrlSkipEmpty();
                     // Delta is from opStart to opStart.  X86 at least counts
                     // the delta from the opEnd, but we don't have the end until
                     // we decide the size - so the encSize has to deal
@@ -344,12 +353,6 @@ private void compactShortForm() {
                     }
                 }
             }
-
-            // CPool padding is non-linear; in rare cases padding can force a
-            // larger size...  which will shrink the padding and allow the
-            // short form to work.  Too bad.
-            if( !_cpool.isEmpty() )
-                pad8();
         }
 
 
@@ -370,49 +373,49 @@ private void compactShortForm() {
         }
     }
 
+
+    // Write the constant pool into the BAOS and optionally patch locally
+    void writeConstantPool( BAOS bits, boolean patch ) {
+        HashSet<Relo> ts = new HashSet<>();
+        for( Relo t : _bigCons.values() ) {
+            if( ts.contains(t) )
+                throw Utils.TODO(); // Dup!  Compress!
+            ts.add(t);
+        }
+        padN(16,bits);
+
+        // By log size
+        for( int log = 3; log >= 0; log-- ) {
+            // Write the 8-byte constants
+            for( Node op : _bigCons.keySet() ) {
+                Relo relo = _bigCons.get(op);
+                if( relo._t.log_size()==log ) {
+                    // Map from relo to constant start and patch
+                    relo._target = bits.size();
+                    relo._opStart= _opStart[op._nid];
+                    // Go ahead and locally patch in-memory
+                    if( patch )
+                        ((RIPRelSize)op).patch(this, relo._opStart, _opLen[op._nid], relo._target - relo._opStart);
+                    // Put constant into code space.
+                    if( relo._t instanceof SONTypeTuple tt ) // Constant tuples put all entries
+                        for( SONType tx : tt._types )
+                            addN(log,tx,bits);
+                    else
+                        addN(log,relo._t,bits);
+                }
+            }
+        }
+    }
+
+
     // Patch local encodings now
-    private void patchLocalRelocations() {
+    void patchLocalRelocations() {
         // Walk the local code-address relocations
         for( Node src : _internals.keySet() ) {
-            Node dst = _internals.get(src);
+            Node dst =  _internals.get(src);
             int target = _opStart[dst._nid];
             int start  = _opStart[src._nid];
             ((RIPRelSize)src).patch(this, start, _opLen[src._nid], target - start);
         }
-
-        for( Node src : _cpool.keySet() ) {
-            int target = _cpool.get(src);
-            int start = _opStart[src._nid];
-            ((RIPRelSize)src).patch(this, start, _opLen[src._nid], target - start);
-        }
-    }
-
-
-    // Actual stack layout is up to each CPU.
-    // X86, with too many args & spills:
-    // | CALLER |
-    // |  argN  | // slot 1, required by callER
-    // +--------+
-    // |  RPC   | // slot 0, required by callER
-    // | callee | // slot 3, callEE
-    // | callee | // slot 2, callEE
-    // |  PAD16 |
-    // +--------+
-
-    // RISC/ARM, with too many args & spills:
-    // | CALLER |
-    // |  argN  | // slot 0, required by callER
-    // +--------+
-    // | callee | // slot 3, callEE: might be RPC
-    // | callee | // slot 2, callEE
-    // | callee | // slot 1, callEE
-    // |  PAD16 |
-    // +--------+
-    private void frameSize() {
-        for( Node n : _code._start.outs() ) {
-            if( n instanceof FunNode fun ) {
-                throw Utils.TODO();
-            }
-        }
     }
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/GlobalCodeMotion.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/GlobalCodeMotion.java
index f0af74b..86712da 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/GlobalCodeMotion.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/GlobalCodeMotion.java
@@ -24,7 +24,7 @@ public static void buildCFG( CodeGen code ) {
         schedEarly(code);
 
         // Break up shared global constants by functions
-        breakUpGlobalConstants(code);
+        breakUpGlobalConstants(code._start);
 
         code._visit.clear();
         schedLate (code);
@@ -45,23 +45,28 @@ private static void _rpo_cfg(CFGNode def, Node use, BitSet visit, Ary<CFGNode> r
     }
 
     // Break up shared global constants by functions
-    private static void breakUpGlobalConstants( CodeGen code ) {
+    private static void breakUpGlobalConstants( Node start ) {
         // For all global constants
-        for( int i=0; i< code._start.nOuts(); i++ ) {
-            Node con = code._start.out(i);
+        for( int i=0; i< start.nOuts(); i++ ) {
+            Node con = start.out(i);
             if( con instanceof MachNode mach && mach.isClone() ) {
+                breakUpGlobalConstants(con);
                 // While constant has users in different functions
                 while( true ) {
                     // Find a function user, and another function
                     FunNode fun = null;
                     boolean done=true;
                     for( Node use : con.outs() ) {
-                        FunNode fun2 = use.cfg0().fun();
+                        FunNode fun2 = use instanceof ReturnNode ret ? ret.fun() : use.cfg0().fun();
                         if( fun==null || fun==fun2 ) fun=fun2;
                         else { done=false; break; }
                     }
                     // Single function user, so this constant is not shared
-                    if( done ) { i--; con.setDef(0,fun); break; }
+                    if( done ) {
+                        if( con.in(0)==start ) i--;
+                        con.setDef(0,fun);
+                        break;
+                    }
                     // Move function users to a private constant
                     Node con2 = mach.copy();  // Private constant clone
                     con2._inputs.set(0,null); // Preserve edge invariants from clone
@@ -226,6 +231,7 @@ private static CFGNode use_block(Node n, Node use, CFGNode[] late) {
     // Least loop depth first, then largest idepth
     private static boolean better( CFGNode lca, CFGNode best ) {
         return lca.loopDepth() < best.loopDepth() ||
+                lca instanceof NeverNode ||
                 (lca.idepth() > best.idepth() || best instanceof IfNode);
     }
 
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/IFG.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/IFG.java
index 38741ad..a67cf16 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/IFG.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/IFG.java
@@ -78,6 +78,8 @@ public static boolean build( int round, RegAlloc alloc ) {
         while( !WORK.isEmpty() )
             do_block(round,alloc,WORK.pop());
 
+        if( alloc.success() )
+            convert2DAdjacency(alloc);
         return alloc.success();
     }
 
@@ -265,6 +267,24 @@ private static void mergeLiveOut( RegAlloc alloc, CFGNode priorbb, int i ) {
     }
 
 
+    static void convert2DAdjacency( RegAlloc alloc ) {
+        // Convert the 2-D array of bits (a 1-D array of BitSets) into an
+        // adjacency matrix.
+        int maxlrg = alloc._LRGS.length;
+        for( int i=1; i<maxlrg; i++ ) {
+            BitSet ifg = IFG.atX(i);
+            if( ifg != null ) {
+                LRG lrg0 = alloc._LRGS[i];
+                for( int lrg = ifg.nextSetBit(0); lrg>=0; lrg=ifg.nextSetBit(lrg+1) ) {
+                    LRG lrg1 = alloc._LRGS[lrg];
+                    lrg0.addNeighbor(lrg1);
+                    lrg1.addNeighbor(lrg0);
+                }
+            }
+        }
+    }
+
+
     // ------------------------------------------------------------------------
     // Color the inference graph.
 
@@ -446,35 +466,39 @@ private static short biasColor( RegAlloc alloc, LRG lrg, short reg, RegMask mask
         if( mask.size1() ) return reg;
         // Check chain of splits up the def-chain.  Take first allocated
         // register, and if it's available in the mask, take it.
-        Node defSplit = lrg._splitDef, useSplit = lrg._splitUse;
-        int tidx, cnt=0;
+        Node def = lrg._splitDef, use = lrg._splitUse;
+        int tidx=0, cnt=0;
 
-        while( (tidx=biasable(defSplit)) != 0 || biasable(useSplit) != 0 ) {
+        while( def != null || use != null ) {
             if( cnt++ > 10 ) break;
 
-            if( tidx != 0 ) {
-                short bias = biasColor( alloc, defSplit, mask );
+            if( def != null ) {
+                short bias = biasColor( alloc, def, mask );
                 if( bias >= 0 ) return bias; // Good bias
-                if( bias == -2 ) defSplit = null; // Kill this side, no more searching
-            } else defSplit = null;
+                if( bias == -2 ) def = null; // Kill this side, no more searching
+                else if( (tidx=biasable(def)) == 0 ) def = null;
+            }
 
-            if( biasable(useSplit) != 0 ) {
-                short bias = biasColor( alloc, useSplit, mask );
+            if( use != null ) {
+                short bias = biasColor( alloc, use, mask );
                 if( bias >= 0 ) return bias; // Good bias
-                if( bias == -2 ) useSplit = null; // Kill this side, no more searching
-            } else useSplit = null;
+                if( bias == -2 ) use = null; // Kill this side, no more searching
+                else if( biasable(use)==0 ) use = null;
+            }
 
-            if( defSplit != null ) {
-                short bias = biasColorNeighbors( alloc, defSplit, mask );
+            if( def != null ) {
+                short bias = biasColorNeighbors( alloc, def, mask );
                 if( bias >= 0 ) return bias;
                 // Advance def side
-                defSplit = defSplit.in(tidx);
+                def = def.in(tidx);
+                if( alloc.lrg(def)==null ) def=null;
             }
 
-            if( useSplit != null ) {
-                short bias = biasColorNeighbors( alloc, useSplit, mask );
+            if( use != null ) {
+                short bias = biasColorNeighbors( alloc, use, mask );
                 if( bias >= 0 ) return bias;
-                useSplit = useSplit.out(0);
+                use = use.out(0);
+                if( biasable(use)==0 ) use=null;
             }
 
         }
@@ -483,7 +507,7 @@ private static short biasColor( RegAlloc alloc, LRG lrg, short reg, RegMask mask
 
     private static int biasable(Node split) {
         if( split instanceof SplitNode ) return 1; // Yes biasable, advance is slot 1
-        if( split instanceof PhiNode ) return 1;   // Yes biasable, advance is slot 1
+        if( split instanceof PhiNode phi ) return phi.region() instanceof LoopNode ? 2 : 1;   // Yes biasable, advance is slot 1
         if( !(split instanceof MachNode mach) ) return 0; // Not biasable
         return mach.twoAddress();                         // Only biasable if 2-addr
     }
@@ -508,7 +532,7 @@ private static short biasColorNeighbors( RegAlloc alloc, Node split, RegMask mas
 
         // Can I limit my own choices to valid neighbor choices?
         for( LRG alrg : slrg._adj ) {
-            int reg = alrg._reg;
+            short reg = alrg._reg;
             if( reg == -1 && alrg._mask.size1() )
                 reg = alrg._mask.firstReg();
             if( reg != -1 ) {
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/LRG.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/LRG.java
index 0662c5f..79b2ad7 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/LRG.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/LRG.java
@@ -61,6 +61,8 @@ public class LRG {
     // Adjacent Live Range neighbors.  Only valid during coloring
     Ary<LRG> _adj;
 
+    public int nadj() { return _adj==null ? 0 : _adj._len; }
+
     void addNeighbor(LRG lrg) {
         if( _adj==null ) _adj = new Ary<>(LRG.class);
         _adj.push(lrg);
@@ -79,7 +81,7 @@ void reinsert( LRG lrg ) {
     }
 
     // More registers than neighbors
-    boolean lowDegree() { return (_adj==null ? 0 : _adj._len) < _mask.size(); }
+    boolean lowDegree() { return nadj() < _mask.size(); }
 
     LRG( short lrg ) { _lrg = lrg; _reg = -1; }
 
@@ -134,6 +136,7 @@ else if( _machDef==lrg._machDef )
 
         if( _machUse==null ) {
             _machUse = lrg._machUse;
+            _uidx = lrg._uidx;
         } else if( lrg._machUse!=null ) {
             if( _machUse != lrg._machUse ) _multiUse=true;
             if( _1regUseCnt==0 ) {
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/LinkMem.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/LinkMem.java
new file mode 100644
index 0000000..39f3dcb
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/LinkMem.java
@@ -0,0 +1,15 @@
+package com.compilerprogramming.ezlang.compiler.codegen;
+
+// In-memory linking
+public class LinkMem {
+    final CodeGen _code;
+    LinkMem( CodeGen code ) { _code = code; }
+
+    public CodeGen link() {
+        // Write any large constants into a constant pool; they
+        // are accessed by RIP-relative addressing.
+        _code._encoding.writeConstantPool(_code._encoding._bits,true);
+
+        return _code;
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/Machine.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/Machine.java
index 273ac75..73acfd1 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/Machine.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/Machine.java
@@ -16,7 +16,7 @@ abstract public class Machine {
     // Return a MachNode unconditional branch
     public abstract CFGNode jump();
     // Break an infinite loop
-    public abstract IfNode never( CFGNode ctrl );
+    public abstract NeverNode never( CFGNode ctrl );
     // Instruction select from ideal nodes
     public abstract Node instSelect( Node n );
 
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/RegAlloc.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/RegAlloc.java
index 634ebf1..09d6c6b 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/RegAlloc.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/RegAlloc.java
@@ -150,7 +150,6 @@ public void regAlloc() {
             if( bb instanceof CallNode ) lastFun._hasCalls = true;
         }
 
-
         // Top driver: repeated rounds of coloring and splitting.
         byte round=0;
         while( !graphColor(round) ) {
@@ -202,11 +201,15 @@ void split(byte round) {
         // independently... which generally requires a full pass over the
         // program for each failing live range.  i.e., might be a lot of
         // passes.
-        for( LRG lrg : _failed.keySet() )
+
+        // Sort, to avoid non-deterministic HashMap ordering
+        LRG[] splits = _failed.keySet().toArray(new LRG[0]);
+        Arrays.sort(splits, (x,y) -> x._lrg - y._lrg );
+        for( LRG lrg : splits )
             split(round,lrg);
     }
 
-    // Split this live range
+    // Split this live range, top level heuristic
     boolean split( byte round, LRG lrg ) {
         assert lrg.leader();  // Already rolled up
 
@@ -220,8 +223,11 @@ boolean split( byte round, LRG lrg ) {
                 lrg._1regUseCnt <= 1 &&
                 (lrg._1regDefCnt + lrg._1regUseCnt) > 0 )
                 return splitEmptyMaskSimple(round,lrg);
-            // Default to splitByLoop
-            //return splitEmptyMask(round,lrg);
+            // Repeated single-reg uses from a single def.  Special for archs
+            // with more fixed regs.
+            if( !lrg._multiDef && lrg._1regDefCnt <= 1 && lrg._1regUseCnt > 2 )
+                if( splitEmptyMaskByUse(round,lrg) )
+                    return true;
         }
 
         // Generic split-by-loop depth.
@@ -244,39 +250,70 @@ boolean splitEmptyMaskSimple( byte round, LRG lrg ) {
             //   alloc
             //     V2/rax - kills prior RAX
             //   st4 [V1],len - No good, must split around
-            makeSplit("def/empty1",round,lrg).insertAfter((Node)lrg._machDef, false/*true*/);
+            insertAfterAndReplace( makeSplit("def/empty1",round,lrg), (Node)lrg._machDef, false/*true*/);
         // Split just before use
         if( lrg._1regUseCnt==1 || (lrg._1regDefCnt==1 && ((Node)lrg._machDef).nOuts()==1) )
-            insertBefore((Node)lrg._machUse,lrg._uidx,"use/empty1",round,lrg,true);
+            insertBefore((Node)lrg._machUse,lrg._uidx,"use/empty1",round,lrg);
         return true;
     }
 
-    // Split live range with an empty mask.  Specifically forces splits at
-    // single-register defs or uses everywhere.
-    boolean splitEmptyMask( byte round, LRG lrg ) {
-        findAllLRG(lrg);
-        // If no single-use or single-def, assume this is a complete register
-        // kill and force spilling everywhere.
-        boolean all = lrg._killed || (lrg._1regDefCnt + lrg._1regUseCnt)==0;
-        for( Node n : _ns ) {
-            if( !(n instanceof MachNode mach) ) continue;
-            // Find def of spilling live range; spilling everywhere, OR
-            // single-register DEF and not cloneable (since these will clone
-            // before every use)
-            if( lrg(n)==lrg && (all || (!mach.isClone() && mach.outregmap().size1() )) )
-                makeSplit(n,"def/empty2",round,lrg).insertAfter(n,true);
-            // Find all uses
-            for( int i=1; i<n.nIns(); i++ ) {
-                Node def = n.in(i);
-                // Skip any new splits inserted just this pass
-                while( def instanceof SplitNode && lrg(def)==null )
-                    def = def.in(1);
-                // Main def (past splits) is of the spilling lrg, and spilling
-                // single-register USE (or everywhere)
-                if( lrg(def)==lrg && (all || mach.regmap(i).size1()) )
-                    insertBefore(n,i,"use/empty2",round,lrg);
+    // Single-def live range with an empty mask.  There are many single-reg
+    // uses.  Theory is there's many repeats if the same reg amongst the uses.
+    // In of splitting once per use, start by splitting into groups based on
+    // required input register.
+    boolean splitEmptyMaskByUse( byte round, LRG lrg ) {
+        Node def = (Node)lrg._machDef;
+
+        // Look at each use, and break into non-overlapping register classes.
+        Ary<RegMask> rclass = new Ary<>(RegMask.class);
+        boolean done=false;
+        while( !done ) {
+            done = true;
+            for( Node use : def._outputs )
+                if( use instanceof MachNode mach )
+                    for( int i=1; i<use.nIns(); i++ )
+                        if( use.in(i)==def )
+                            done = putIntoRegClass( rclass, mach.regmap(i) );
+        }
+
+        // See how many register classes we split into
+        if( rclass._len <= 1 ) return false;
+
+        // Split by class
+        for( RegMask rmask : rclass ) {
+            Node split = makeSplit(def,"popular",round,lrg);
+            split.insertAfter( def );
+            if( split.nIns()>1 ) split.setDef(1,def);
+            // all uses by class to split
+            for( int j=0; j < def._outputs._len; j++ ) {
+                Node use = def._outputs.at(j);
+                if( use instanceof MachNode mach && use!=split ) {
+                    // Check all use inputs for n, in case there's several
+                    for( int i = 1; i < use.nIns(); i++ )
+                        // Find a def input, and check register class
+                        if( use.in( i ) == def && mach.regmap( i ).overlap( rmask ) )
+                            // Modify use to use the split version specialized to this rclass
+                            { use.setDef( i, split ); j--; break; }
+                }
+            }
+        }
+        return true;
+    }
+
+
+    // Put use into a register class, perhaps adding a class or perhaps
+    // narrowing a class (and causing a repeat)
+    private static boolean putIntoRegClass( Ary<RegMask> rclass, RegMask rmask ) {
+        for( int i=0; i<rclass._len; i++ ) {
+            RegMask omask = rclass.at(i);
+            if( omask.and(rmask) == omask ) return true; // Within the same register class
+            if( omask.overlap(rmask) ) {
+                rclass.set(i,new RegMask(omask.copy().and(rmask)));
+                return false;   // Need go again
             }
         }
+        // Add a new class, no need to go again
+        rclass.push(rmask);
         return true;
     }
 
@@ -294,7 +331,8 @@ boolean splitSelfConflict( byte round, LRG lrg ) {
             // Phi or two-address
             for( int i=0; i<def._outputs._len; i++ ) {
                 Node use = def.out(i);
-                if( (use instanceof PhiNode phi && !(phi.region() instanceof LoopNode && phi.in(2)==def ) ) ||
+                if( (use instanceof PhiNode phi &&
+                     !(phi.region() instanceof LoopNode loop && phi.in(2)==def && def.cfg0().idepth() > loop.idepth() ) ) ||
                         (use instanceof MachNode mach && mach.twoAddress()!=0 && use.in(mach.twoAddress())==def) )
                     insertBefore( use, use._inputs.find(def), "use/self/use",round,lrg );
             }
@@ -303,9 +341,9 @@ boolean splitSelfConflict( byte round, LRG lrg ) {
             // TODO: split before all inputs (except the last; at least 1 split here must be extra)
             if( def instanceof PhiNode phi && !(def instanceof ParmNode) ) {
                 SplitNode split = makeSplit("def/self",round,lrg);
-                split.insertAfter(def,false);
+                insertAfterAndReplace(split,def,false);
                 if( split.nOuts()==0 )
-                    split.kill();
+                    split.killOrdered();
                 insertBefore(phi,1,"use/self/phi",round,lrg);
             }
             // Split before two-address ops which extend the live range
@@ -357,10 +395,10 @@ boolean splitByLoop( byte round, LRG lrg ) {
                 (min==max || n.cfg0().loopDepth() <= min) ) {
                 // Cloneable constants will be cloned at uses, not after def
                 if( !(n instanceof MachNode mach && mach.isClone()) &&
-                    // Single user is already a split
-                    !(n.nOuts()==1 && n.out(0) instanceof SplitNode) )
+                    // Single user is already a split adjacent
+                    !(n.nOuts()==1 && n.out(0) instanceof SplitNode split && sameBlockNoClobber(split) ) )
                     // Split after def in min loop nest
-                    makeSplit("def/loop",round,lrg).insertAfter(n,false);
+                    insertAfterAndReplace( makeSplit("def/loop",round,lrg), n,true);
             }
 
             // PhiNodes check all CFG inputs
@@ -371,22 +409,19 @@ boolean splitByLoop( byte round, LRG lrg ) {
                         // splitting in inner loop or at loop border
                         (min==max || phi.region().cfg(i).loopDepth() <= min) &&
                         // and not around the backedge of a loop (bad place to force a split, hard to remove)
-                        !(phi.region() instanceof LoopNode && i==2) )
+                        !(phi.region() instanceof LoopNode && i==2 && (phi.in(i) instanceof PhiNode pp && pp.region()==phi.region())) )
                         // Split before phi-use in prior block
                         insertBefore(phi,i, "use/loop/phi",round,lrg);
 
             } else {
                 // Others check uses
                 for( int i=1; i<n.nIns(); i++ ) {
-                    boolean sizeN=false;
                     // This is a LRG use
                     // splitting in inner loop or at loop border
                     if( lrgSame( n.in( i ), lrg ) &&
                         (min == max || (n.in(i) instanceof MachNode mach && mach.isClone()) || n.cfg0().loopDepth() <= min) )
-                        // Not a single-use split same block already
-                        //!(n.in(i) instanceof SplitNode && n.in(i).nOuts()==1 && n.cfg0()==n.in(i).cfg0() && (sizeN=!((MachNode)n).regmap(i).size1())) )
                         // Split before in this block
-                        insertBefore( n, i, "use/loop/use", round,lrg, sizeN );
+                        insertBefore( n, i, "use/loop/use", round,lrg, false );
                 }
             }
         }
@@ -407,6 +442,22 @@ private static long ldepth( long ld, Node n, CFGNode cfg ) {
         int min = (int)ld;
         int max = (int)(ld>>32);
         int d = cfg.loopDepth();
+        // if n will lower the min loop and is in the tail end of the loop
+        // header, splitting "around" the loop will not help.  Treat n as being
+        // in the loop.
+        if( d < min ) {
+            if( cfg.uctrl() instanceof LoopNode loop && loop.entry()==cfg ) {
+                for( int i=cfg.nOuts()-2; i>=0; i-- ) {
+                    Node out = cfg.out(i);
+                    if( n==out )
+                        { d = loop.loopDepth(); break; } // Treat n as being "in the loop"
+                    if( !((out instanceof MachNode mach && mach.isClone()) || out instanceof SplitNode ) )
+                        break;  // Treat b as "normal", out of loop
+                }
+            }
+        }
+
+        // lower min, raise max, and re-fold
         min = Math.min(min,d);
         max = Math.max(max,d);
         return ((long)max<<32) | min;
@@ -453,6 +504,24 @@ void insertBefore(Node n, int i, String kind, byte round, LRG lrg) {
         insertBefore(n,i,kind,round,lrg,true);
     }
 
+    // Replace uses of `def` with `split`, and insert `split` immediately after
+    // `def` in the basic block.
+    public void insertAfterAndReplace( Node split, Node def, boolean must ) {
+        split.insertAfter(def);
+        if( split.nIns()>1 ) split.setDef(1,def);
+        for( int j=def.nOuts()-1; j>=0; j-- ) {
+            Node use = def.out(j);
+            if( use==split ) continue; // Skip self
+            // Can we avoid a split of a split?  'this' split is used by
+            // another split in the same block.
+            if( !must && use instanceof SplitNode split2 && sameBlockNoClobber(split2) )
+                continue;
+            int idx = use._inputs.find(def);
+            use.setDefOrdered(idx,split);
+            if( j < def.nOuts() ) j++;
+        }
+    }
+
     private Node makeSplit( Node def, String kind, byte round, LRG lrg ) {
         Node split = def instanceof MachNode mach && mach.isClone()
             ? mach.copy()
@@ -523,4 +592,24 @@ private boolean splitBypass( CFGNode bb, int j, Node lo, int defreg ) {
         lo.setDefOrdered(1,hi.in(1));
         return true;
     }
+
+
+    public boolean sameBlockNoClobber( SplitNode split ) {
+        Node def = split.in(1);
+        CFGNode cfg = def.cfg0();
+        if( cfg != split.cfg0() ) return false; // Not same block
+        // Get multinode head
+        Node def0 = def instanceof ProjNode ? def.in(0) : def;
+        int defreg = lrg(def)._reg;
+        if( defreg == -1 ) defreg = lrg(def)._mask.firstReg();
+        if( defreg == -1 ) return false; // no allowed registers -> clobbered
+        for( int idx = cfg._outputs.find(split) -1; idx >= 0; idx-- ) {
+            Node n = cfg.out(idx);
+            if( n==def0 ) return true;    // No clobbers
+            if( lrg(n) == lrg(def) ) return false; // Self conflict
+            if( lrg(n)!=null && lrg(n)._reg == defreg )
+                return false;   // Clobbered
+        }
+        throw Utils.TODO();
+    }
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/RegMask.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/RegMask.java
index a566187..e09c801 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/RegMask.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/RegMask.java
@@ -32,6 +32,7 @@ public RegMask(int bit) {
     }
     public RegMask(long bits ) { _bits0 = bits; }
     public RegMask(long bits0, long bits1 ) { _bits0 = bits0; _bits1 = bits1; }
+    public RegMask(RegMask r) { _bits0 = r._bits0; _bits1 = r._bits1; }
     private RegMask() { _bits0 = _bits1 = 0; }
 
     // AND, with copy-on-write if changing
@@ -65,6 +66,7 @@ public boolean clr( int reg ) {
 
 
     public short firstReg() {
+        if( isEmpty() ) return -1;
         return (short)(_bits0 != 0
                        ? Long.numberOfTrailingZeros(_bits0)
                        : Long.numberOfTrailingZeros(_bits1)+64);
@@ -118,25 +120,3 @@ public SB toString(SB sb) {
     }
 }
 
-// Mutable regmask(writable)
-class RegMaskRW extends RegMask {
-    public RegMaskRW(long x, long y) { super(x,y);  }
-    // clears bit at position r. Returns true if the mask is still not empty.
-    public boolean clr(int r) {
-        if( r < 64 ) _bits0 &= ~(1L<<(r   ));
-        else         _bits1 &= ~(1L<<(r-64));
-        return _bits0!=0 || _bits1!=0;
-    }
-    @Override RegMaskRW and( RegMask mask ) {
-        if( mask==null ) return this;
-        _bits0 &= mask._bits0;
-        _bits1 &= mask._bits1;
-        return this;
-    }
-    @Override RegMaskRW sub( RegMask mask ) {
-        if( mask==null ) return this;
-        _bits0 &= ~mask._bits0;
-        _bits1 &= ~mask._bits1;
-        return this;
-    }
-}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/RegMaskRW.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/RegMaskRW.java
new file mode 100644
index 0000000..f391fcc
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/RegMaskRW.java
@@ -0,0 +1,31 @@
+package com.compilerprogramming.ezlang.compiler.codegen;
+
+// Mutable regmask(writable)
+public class RegMaskRW extends RegMask {
+    public RegMaskRW(long x, long y) { super(x,y);  }
+    // clears bit at position r. Returns true if the mask is still not empty.
+    public boolean clr(int r) {
+        if( r < 64 ) _bits0 &= ~(1L<<(r   ));
+        else         _bits1 &= ~(1L<<(r-64));
+        return _bits0!=0 || _bits1!=0;
+    }
+    @Override RegMaskRW and( RegMask mask ) {
+        if( mask==null ) return this;
+        _bits0 &= mask._bits0;
+        _bits1 &= mask._bits1;
+        return this;
+    }
+    @Override RegMaskRW sub( RegMask mask ) {
+        if( mask==null ) return this;
+        _bits0 &= ~mask._bits0;
+        _bits1 &= ~mask._bits1;
+        return this;
+    }
+    public RegMaskRW or( RegMask mask ) {
+        if( mask!=null ) {
+            _bits0 |= mask._bits0;
+            _bits1 |= mask._bits1;
+        }
+        return this;
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/BoolNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/BoolNode.java
index 9c9b744..8863de8 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/BoolNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/BoolNode.java
@@ -91,6 +91,19 @@ SONTypeInteger doOp(SONTypeInteger i1, SONTypeInteger i2) {
         Node copy(Node lhs, Node rhs) { return new EQ(lhs,rhs); }
         Node copyF() { return new EQF(null,null); }
     }
+
+    public static class NE extends BoolNode {
+        public NE(Node lhs, Node rhs) { super(lhs,rhs); }
+        public String op() { return "!="; }
+        SONTypeInteger doOp(SONTypeInteger i1, SONTypeInteger i2) {
+            if( i1==i2 && i1.isConstant() ) return TRUE;
+            if( i1._max < i2._min || i1._min > i2._max ) return FALSE;
+            return BOOL;
+        }
+        Node copy(Node lhs, Node rhs) { return new NE(lhs,rhs); }
+        Node copyF() { throw Utils.TODO(); }
+    }
+
     public static class LT extends BoolNode {
         public LT(Node lhs, Node rhs) { super(lhs,rhs); }
         public String op() { return "<" ; }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CFGNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CFGNode.java
index 3876fd2..2c348b7 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CFGNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CFGNode.java
@@ -51,6 +51,13 @@ public CFGNode uctrl() {
         return c;
     }
 
+    // Used by the encoding / final BB layout
+    public CFGNode uctrlSkipEmpty() {
+        CFGNode x = this, y=null;
+        while( x.nOuts() == 1 && (y=x.uctrl())!=null ) // Skip empty blocks
+            x = y;
+        return x;
+    }
 
     // ------------------------------------------------------------------------
     /**
@@ -97,7 +104,7 @@ public FunNode fun() {
 
     LoopTree _ltree;
     public int _pre;            // Pre-order numbers for loop tree finding
-    private static class LoopTree {
+    static class LoopTree {
         LoopTree _par;
         final LoopNode _head;
         int _depth;
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CallNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CallNode.java
index 52b3bf1..56902cf 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CallNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CallNode.java
@@ -48,9 +48,11 @@ public String name() {
     public Node fptr() { return _inputs.last(); }
     // Error if not a TFP
     public SONTypeFunPtr tfp() { return (SONTypeFunPtr)fptr()._type; }
+
     // Call is to an externally supplied code
     public boolean external() { return false; }
 
+
     // Find the Call End from the Call
     public CallEndNode cend() {
         // Always in use slot 0
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ConstantNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ConstantNode.java
index e858248..8ad7f3f 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ConstantNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ConstantNode.java
@@ -47,7 +47,7 @@ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
             if( fun._name != null )
                 return sb.append("{ ").append(fun._name).append("}");
         }
-        return sb.append(_con.print(new SB()));
+        return sb.append(_con==null ? "---" : _con.print(new SB()));
     }
 
     @Override public boolean isConst() { return true; }
@@ -59,7 +59,7 @@ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
     public Node idealize() { return null; }
 
     @Override
-    boolean eq(Node n) {
+    public boolean eq(Node n) {
         ConstantNode con = (ConstantNode)n; // Contract
         return _con==con._con;
     }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/FRefNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/FRefNode.java
index a48814f..3b4a282 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/FRefNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/FRefNode.java
@@ -32,6 +32,6 @@ public class FRefNode extends ConstantNode {
 
     public CompilerException err() { return Compiler.error("Undefined name '"+_n._name+"'"); }
 
-    @Override boolean eq(Node n) { return this==n; }
+    @Override public boolean eq(Node n) { return this==n; }
     @Override int hash() { return _n._idx; }
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/IfNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/IfNode.java
index 5a5145c..b9475bd 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/IfNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/IfNode.java
@@ -68,6 +68,7 @@ public Node idealize() {
     // The following CProjs will be inverted by the caller.
     public void invert() { throw Utils.TODO(); }
 
+    // Negate the sense of a test
     public static String invert( String bop ) {
         return switch( bop ) {
         case "<"  -> ">=";
@@ -79,4 +80,17 @@ public static String invert( String bop ) {
         default -> throw Utils.TODO();
         };
     }
+
+    // Swap compare operands
+    public static String swap( String bop ) {
+        return switch( bop ) {
+        case "<"  -> ">" ;
+        case "<=" -> ">=";
+        case "==" -> "!=";
+        case "!=" -> "==";
+        case ">"  -> "<" ;
+        case ">=" -> "<=";
+        default -> throw Utils.TODO();
+        };
+    }
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/LoopNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/LoopNode.java
index 721926c..e3f63f3 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/LoopNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/LoopNode.java
@@ -1,5 +1,6 @@
 package com.compilerprogramming.ezlang.compiler.nodes;
 
+import com.compilerprogramming.ezlang.compiler.codegen.CodeGen;
 import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
 
 public class LoopNode extends RegionNode {
@@ -33,41 +34,43 @@ public StopNode forceExit( FunNode fun, StopNode stop ) {
         while( x != this ) {
             if( x instanceof CProjNode exit && exit.in(0) instanceof IfNode iff ) {
                 CFGNode other = iff.cproj(1-exit._idx);
-                if( other!=null && other.loopDepth() < loopDepth() )
+                if( other!=null && other._ltree != _ltree && nested(_ltree,other._ltree) )
                     return stop; // Found an exit, not an infinite loop
             }
             x = x.idom();
         }
         // Found a no-exit loop.  Insert an exit
-        NeverNode iff = new NeverNode(back());
-        for( Node use : _outputs )
-            if( use instanceof PhiNode )
-                iff.addDef(use);
+        NeverNode iff = CodeGen.CODE._mach == null
+            ? new NeverNode(back()) // Ideal never-branch
+            : CodeGen.CODE._mach.never(back()); // Machine never-branch
         CProjNode t = new CProjNode(iff,0,"True" ).init();
         CProjNode f = new CProjNode(iff,1,"False").init();
-        setDef(2,f);
+        setDef(2,t);
+        iff._ltree = t._ltree = _ltree;
+        ReturnNode ret = fun.ret();
+        f._ltree = ret._ltree;
 
         // Now fold control into the exit.  Might have 1 valid exit, or an
         // XCtrl or a bunch of prior NeverNode exits.
         Node top = new ConstantNode(SONType.TOP).peephole();
-        ReturnNode ret = fun.ret();
         Node ctrl = ret.ctrl(), mem = ret.mem(), expr = ret.expr();
-        if( ctrl._type != SONType.XCONTROL ) {
+        if( ctrl!=null && ctrl._type != SONType.XCONTROL ) {
             // Perfect aligned exit?
             if( !(ctrl instanceof RegionNode r &&
                   mem  instanceof PhiNode pmem && pmem.region()==r &&
                   expr instanceof PhiNode prez && prez.region()==r ) ) {
                 // Nope, insert an aligned exit layer
-                ctrl = new RegionNode(null,ctrl).init();
-                mem  = new    PhiNode((RegionNode)ctrl,mem ).init();
-                expr = new    PhiNode((RegionNode)ctrl,expr).init();
+                RegionNode r = new RegionNode((Node)null,ctrl).init();
+                ctrl = r;  r._ltree = _ltree;
+                mem  = new PhiNode(r,mem ).init();
+                expr = new PhiNode(r,expr).init();
             }
             // Append new Never exit
-            ctrl.addDef(t  );
+            ctrl.addDef(f  );
             mem .addDef(top);
             expr.addDef(top);
         } else {
-            ctrl = t;
+            ctrl = f;
             mem  = top;
             expr = top;
         }
@@ -77,4 +80,12 @@ public StopNode forceExit( FunNode fun, StopNode stop ) {
 
         return stop;
     }
+
+    private static boolean nested(LoopTree inner, LoopTree outer) {
+        for( ; inner!=null; inner = inner._par )
+            if( inner == outer )
+                return true;
+        return false;
+    }
+
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MemOpNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MemOpNode.java
index ae96bed..1806b27 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MemOpNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MemOpNode.java
@@ -44,7 +44,7 @@ public MemOpNode( Node mach, MemOpNode mop ) {
     }
 
     //
-    static String mlabel(String name) { return name.equals("[]") ? "ary" : (name.equals("#") ? "len" : name); }
+    static String mlabel(String name) { return "[]".equals(name) ? "ary" : ("#".equals(name) ? "len" : name); }
     String mlabel() { return mlabel(_name); }
 
     public Node mem() { return in(1); }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/Node.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/Node.java
index 7d4c936..6975943 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/Node.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/Node.java
@@ -227,6 +227,7 @@ public Node addDef(Node new_def) {
     }
 
     // Breaks the edge invariants, used temporarily
+    @SuppressWarnings("unchecked")
     protected <N extends Node> N addUse(Node n) { _outputs.add(n); return (N)this; }
 
     // Remove node 'use' from 'def's (i.e. our) output list, by compressing the list in-place.
@@ -268,6 +269,15 @@ public void kill( ) {
         assert isDead();        // Really dead now
     }
 
+    // Preserve CFG use-ordering when killing
+    public void killOrdered() {
+        CFGNode cfg = cfg0();
+        cfg._outputs.remove(cfg._outputs.find(this));
+        _inputs.set(0,null);
+        kill();
+    }
+
+
     // Mostly used for asserts and printing.
     public boolean isDead() { return isUnused() && nIns()==0 && _type==null; }
 
@@ -275,6 +285,7 @@ public void kill( ) {
     // Add bogus null use to keep node alive
     public <N extends Node> N keep() { return addUse(null); }
     // Remove bogus null.
+    @SuppressWarnings("unchecked")
     public <N extends Node> N unkeep() {
         delUse(null);
         return (N)this;
@@ -301,9 +312,8 @@ public void subsume( Node nnn ) {
         kill();
     }
 
-    // Replace uses of `def` with `this`, and insert `this` immediately after
-    // `def` in the basic block.
-    public void insertAfter( Node def, boolean must ) {
+    // insert `this` immediately after `def` in the same basic block.
+    public void insertAfter( Node def ) {
         CFGNode cfg = def.cfg0();
         int i = cfg._outputs.find(def)+1;
         if( cfg instanceof CallEndNode ) {
@@ -316,19 +326,6 @@ public void insertAfter( Node def, boolean must ) {
         while( cfg.out(i) instanceof PhiNode || cfg.out(i) instanceof CalleeSaveNode )  i++;
         cfg._outputs.insert(this,i);
         _inputs.set(0,cfg);
-        for( int j=def.nOuts()-1; j>=0; j-- ) {
-            // Can we avoid a split of a split?  'this' split is used by
-            // another split in the same block.
-            if( !must && def.out(j) instanceof SplitNode split && def.out(j).cfg0()==cfg &&
-                !split._kind.contains("self") )
-                continue;
-            Node use = def._outputs.del(j);
-            use.unlock();
-            int idx = use._inputs.find(def);
-            use._inputs.set(idx,this);
-            addUse(use);
-        }
-        if( nIns()>1 ) setDef(1,def);
     }
 
     // Insert this in front of use.in(uidx) with this, and insert this
@@ -338,6 +335,8 @@ public void insertBefore( Node use, int uidx ) {
         int i;
         if( use instanceof PhiNode phi ) {
             cfg = phi.region().cfg(uidx);
+            if( cfg instanceof CProjNode && cfg.in(0) instanceof NeverNode nvr )
+                cfg = nvr.cfg0();
             i = cfg.nOuts()-1;
         } else {
             i = cfg._outputs.find(use);
@@ -349,7 +348,7 @@ public void insertBefore( Node use, int uidx ) {
         use.setDefOrdered(uidx,this);
     }
 
-    public void setDefOrdered(int idx, Node def) {
+    public void setDefOrdered( int idx, Node def) {
         // If old is dying, remove from CFG ordered
         Node old = in(idx);
         if( old!=null && old.nOuts()==1 ) {
@@ -488,6 +487,7 @@ public SONType setType(SONType type) {
         return old;
     }
 
+    @SuppressWarnings("unchecked")
     public <N extends Node> N init() { _type = compute(); return (N)this; }
 
     /**
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AddARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AddARM.java
index d3ad62d..9fc20ac 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AddARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AddARM.java
@@ -11,7 +11,7 @@ public class AddARM extends MachConcreteNode implements MachNode {
     @Override public RegMask outregmap() { return arm.RMASK; }
 
     // ADD (shifted register)
-    @Override public void encoding( Encoding enc ) { arm.r_reg(enc,this,0b10001011); }
+    @Override public void encoding( Encoding enc ) { arm.r_reg(enc,this,arm.OP_ADD); }
     // 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)));
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AddFARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AddFARM.java
index 7517d8f..807c0a4 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AddFARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AddFARM.java
@@ -11,7 +11,7 @@ public class AddFARM extends MachConcreteNode implements MachNode {
     @Override public RegMask outregmap() { return arm.DMASK; }
 
     //FADD (scalar)
-    @Override public void encoding( Encoding enc ) { arm.f_scalar(enc,this,0b00011110); }
+    @Override public void encoding( Encoding enc ) { arm.f_scalar(enc,this,arm.OPF_ADD); }
 
     // Default on double precision for now(64 bits)
     // General form: "addf  rd = src1 + src2
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AddIARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AddIARM.java
index ef16d9f..4c2b94a 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AddIARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AddIARM.java
@@ -18,7 +18,7 @@ public class AddIARM extends MachConcreteNode implements MachNode {
     @Override public RegMask outregmap() { return arm.RMASK; }
     //ADD (immediate)
     @Override public void encoding( Encoding enc ) {
-        arm.imm_inst(enc,this,0b1001000100,_imm);
+        arm.imm_inst(enc,this, in(1), arm.OPI_ADD,_imm);
     }
     // General form: "addi  rd = rs1 + imm"
     @Override public void asm(CodeGen code, SB sb) {
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AndARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AndARM.java
index 903a307..94ee434 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AndARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AndARM.java
@@ -13,7 +13,7 @@ public class AndARM extends MachConcreteNode implements MachNode {
     @Override public RegMask regmap(int i) { return arm.RMASK; }
     @Override public RegMask outregmap() { return arm.RMASK; }
     // AND (shifted register)
-    @Override public void encoding( Encoding enc ) { arm.r_reg(enc,this,0b10001010); }
+    @Override public void encoding( Encoding enc ) { arm.r_reg(enc,this,arm.OP_AND); }
     // 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)));
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AndIARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AndIARM.java
index 2412999..96b4485 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AndIARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AndIARM.java
@@ -15,7 +15,7 @@ public class AndIARM extends MachConcreteNode implements MachNode {
     @Override public RegMask regmap(int i) { return arm.RMASK; }
     @Override public RegMask outregmap() { return arm.RMASK; }
     @Override public void encoding( Encoding enc ) {
-        arm.imm_inst_n(enc,this,0b100100100,_imm);
+        arm.imm_inst_n(enc,this, in(1), arm.OPI_AND,_imm);
     }
     // General form: "andi  rd = rs1 & imm"
     @Override public void asm(CodeGen code, SB sb) {
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AsrARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AsrARM.java
index 4711932..7bd9418 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AsrARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AsrARM.java
@@ -10,7 +10,7 @@ public class AsrARM extends MachConcreteNode implements MachNode {
     @Override public String op() { return "sar"; }
     @Override public RegMask regmap(int i) { return arm.RMASK; }
     @Override public RegMask outregmap() { return arm.RMASK; }
-    @Override public void encoding( Encoding enc ) { arm.shift_reg(enc,this,0b1010); }
+    @Override public void encoding( Encoding enc ) { arm.shift_reg(enc,this,arm.OP_ASR); }
     @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)));
     }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AsrIARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AsrIARM.java
index 05a508c..3f903b3 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AsrIARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AsrIARM.java
@@ -22,7 +22,7 @@ public class AsrIARM extends MachConcreteNode implements MachNode {
         short rd = enc.reg(this);
         short rn = enc.reg(in(1));
         assert _imm > 0;
-        enc.add4(arm.imm_shift(0b1001001101,_imm, 0b111111, rn, rd));
+        enc.add4(arm.imm_shift(arm.OPI_ASR,_imm, 0b111111, rn, rd));
     }
     // General form: "asri  rd = rs1 >> imm"
     @Override public void asm(CodeGen code, SB sb) {
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/BranchARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/BranchARM.java
index 12ce1c2..8494f39 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/BranchARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/BranchARM.java
@@ -28,13 +28,12 @@ public class BranchARM extends IfNode implements MachNode, RIPRelSize {
     @Override public RegMask outregmap() { return null; }
     @Override public void invert() { _bop = invert(_bop);  }
 
-    // Encoding is appended into the byte array; size is returned
     @Override public void encoding( Encoding enc ) {
         // Assuming that condition flags are already set.  These flags are set
         // by comparison (or sub).  No need for regs because it uses flags
         enc.jump(this,cproj(0));
         // B.cond
-        enc.add4( arm.b_cond(0b01010100, 0, arm.make_condition(_bop)) );
+        enc.add4( arm.b_cond(arm.OP_BRANCH, 0, arm.make_condition(_bop)) );
     }
 
     // Delta is from opcode start
@@ -47,7 +46,7 @@ public class BranchARM extends IfNode implements MachNode, RIPRelSize {
     // Delta is from opcode start
     @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) {
         if( opLen==4 ) {
-            enc.patch4(opStart,arm.b_cond(0b01010100, delta, arm.make_condition(_bop)));
+            enc.patch4(opStart,arm.b_cond(arm.OP_BRANCH, delta, arm.make_condition(_bop)));
         } else {
             throw Utils.TODO();
         }
@@ -56,9 +55,8 @@ public class BranchARM extends IfNode implements MachNode, RIPRelSize {
     @Override public void asm(CodeGen code, SB sb) {
         String src = code.reg(in(1));
         if( src!="flags" ) sb.p(src).p(" ");
-        CFGNode prj = cproj(0);
-        while( prj.nOuts() == 1 )
-            prj = prj.uctrl();  // Skip empty blocks
+        CFGNode prj = cproj(0).uctrlSkipEmpty();
+        if( !prj.blockHead() ) prj = prj.cfg0();
         sb.p(label(prj));
     }
     @Override public String comment() { return "L"+cproj(1)._nid; }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CallARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CallARM.java
index d16a25f..94722aa 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CallARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CallARM.java
@@ -27,7 +27,7 @@ public class CallARM extends CallNode implements MachNode, RIPRelSize {
     @Override public void encoding( Encoding enc ) {
         enc.relo(this);
         // BL
-        enc.add4(arm.b(0b100101,0)); // Target patched at link time
+        enc.add4(arm.b(arm.OP_CALL,0)); // Target patched at link time
     }
 
     // Delta is from opcode start, but X86 measures from the end of the 5-byte encoding
@@ -35,7 +35,7 @@ public class CallARM extends CallNode implements MachNode, RIPRelSize {
 
     // Delta is from opcode start
     @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) {
-        enc.patch4(opStart,arm.b(0b100101,delta));
+        enc.patch4(opStart,arm.b(arm.OP_CALL,delta));
     }
 
     @Override public void asm(CodeGen code, SB sb) {
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CallRRARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CallRRARM.java
index a8cb51b..71f9382 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CallRRARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CallRRARM.java
@@ -19,7 +19,7 @@ public class CallRRARM extends CallNode implements MachNode {
         // Needs a register, typically a jump-and-link-register opcode
         // blr
         short self = enc.reg(this);
-        enc.add4(arm.blr(0b1101011000111111000000, self));
+        enc.add4(arm.blr(arm.OP_CALLRARM, self));
     }
 
     @Override public void asm(CodeGen code, SB sb) {
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CmpARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CmpARM.java
index 5e2366b..b5dba20 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CmpARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CmpARM.java
@@ -11,7 +11,7 @@ public class CmpARM extends MachConcreteNode implements MachNode {
     @Override public RegMask outregmap() { return arm.FLAGS_MASK; }
 
     // SUBS (shifted register)
-    @Override public void encoding( Encoding enc ) { arm.r_reg(enc,this,0b11101011); }
+    @Override public void encoding( Encoding enc ) { arm.r_reg(enc,this,arm.OP_CMP); }
 
     // General form: "cmp  rs1, rs2"
     @Override public void asm(CodeGen code, SB sb) {
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CmpIARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CmpIARM.java
index af1fccc..c9b85f4 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CmpIARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CmpIARM.java
@@ -5,7 +5,7 @@
 import com.compilerprogramming.ezlang.compiler.nodes.*;
 
 // Compare with immediate.
-// Conditional compare (immediate)? e.g CCMP(immediate)
+// CMP (immediate) - SUBS
 public class CmpIARM extends MachConcreteNode implements MachNode {
     final int _imm;
     final String _bop;
@@ -27,8 +27,8 @@ public class CmpIARM extends MachConcreteNode implements MachNode {
     @Override public RegMask outregmap() { return arm.FLAGS_MASK; }
     @Override public boolean isClone() { return true; }
     @Override public Node copy() { return new CmpIARM(this); }
-    // Encoding is appended into the byte array; size is returned
-    @Override public void encoding( Encoding enc ) { arm.imm_inst(enc,this,964,_imm); }
+
+    @Override public void encoding( Encoding enc ) { arm.imm_inst(enc,in(1),in(1), arm.OPI_CMP,_imm); }
 
     // General form: "cmp  rs1, 1"
     @Override public void asm(CodeGen code, SB sb) {
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/DivARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/DivARM.java
index 16deea1..e9c8dbc 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/DivARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/DivARM.java
@@ -10,7 +10,7 @@ public class DivARM extends MachConcreteNode implements MachNode {
     @Override public RegMask regmap(int i) { return arm.RMASK; }
     @Override public RegMask outregmap() { return arm.RMASK; }
     // SDIV
-    @Override public void encoding( Encoding enc ) { arm.madd(enc,this,0b10011010110,3); }
+    @Override public void encoding( Encoding enc ) { arm.madd(enc,this,arm.OP_DIV,3); }
     // 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)));
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/DivFARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/DivFARM.java
index fa550cb..f86ccfd 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/DivFARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/DivFARM.java
@@ -11,7 +11,7 @@ public class DivFARM extends MachConcreteNode implements MachNode {
     @Override public RegMask outregmap() {  return arm.DMASK; }
 
     // FDIV (scalar)
-    @Override public void encoding( Encoding enc ) { arm.f_scalar(enc,this,0b000110); }
+    @Override public void encoding( Encoding enc ) { arm.f_scalar(enc,this,arm.OPF_DIV); }
     // General form: "VDIF =  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)));
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/FloatARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/FloatARM.java
index c56d88e..b8f41b1 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/FloatARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/FloatARM.java
@@ -18,11 +18,11 @@ public class FloatARM extends ConstantNode implements MachNode, RIPRelSize {
     @Override public FloatARM copy() { return new FloatARM(this); }
 
     @Override public void encoding( Encoding enc ) {
-        enc.largeConstant(this,_con);
+        enc.largeConstant(this,_con,0,-1/*TODO: ARM-style ELF patching*/);
         short dst = (short)(enc.reg(this) - arm.D_OFFSET);
         double d = ((SONTypeFloat)_con).value();
         long x = Double.doubleToRawLongBits(d);
-        enc.add4(arm.load_pc(0b01011100, 0, dst));
+        enc.add4(arm.load_pc(arm.OPF_ARM, 0, dst));
     }
 
     // Delta is from opcode start.
@@ -31,7 +31,7 @@ public class FloatARM extends ConstantNode implements MachNode, RIPRelSize {
     // Delta is from opcode start
     @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) {
         short dst = (short)(enc.reg(this) - arm.D_OFFSET);
-        enc.add4(arm.load_pc(0b01011100, delta, dst));
+        enc.add4(arm.load_pc(arm.OPF_ARM, delta, dst));
     }
 
     // Human-readable form appended to the SB.  Things like the encoding,
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/FunARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/FunARM.java
index 96fc6cc..a270595 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/FunARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/FunARM.java
@@ -27,7 +27,7 @@ public class FunARM  extends FunNode implements MachNode {
         _maxArgSlot = arm.maxSlot(enc._fun.sig());
         _frameAdjust = (short) (_maxSlot+1 - _maxArgSlot);
         if( _frameAdjust == 0 ) return; // Skip if no frame adjust
-        enc.add4(arm.imm_inst(0b1001000100, (_frameAdjust*8)&0xFFF, arm.RSP, arm.RSP));
+        enc.add4(arm.imm_inst(arm.OPI_ADD, (_frameAdjust*8)&0xFFF, arm.RSP, arm.RSP));
     }
 
     @Override public void asm(CodeGen code, SB sb) {
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/I2F8ARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/I2F8ARM.java
index ef24c5a..2793fbb 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/I2F8ARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/I2F8ARM.java
@@ -13,7 +13,7 @@ public class I2F8ARM extends MachConcreteNode implements MachNode {
         // SCVTF
         short self = (short)(enc.reg(this )-arm.D_OFFSET);
         short reg1 = enc.reg(in(1));
-        int body = arm.float_cast(0b10011110, 1, reg1, self);
+        int body = arm.float_cast(arm.OP_FLOAT_C, 1, reg1, self);
         enc.add4(body);
     }
 
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/IntARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/IntARM.java
index 2608b04..495559b 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/IntARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/IntARM.java
@@ -33,11 +33,11 @@ public class IntARM extends ConstantNode implements MachNode {
         if(nb0 >= nb1) {
             // More 0 blocks then F blocks, use movz
             pattern = 0;
-            op = 0b110100101;
+            op = arm.OP_MOVZ;
         } else {
             // More F blocks then 0 blocks, use movn
             pattern = 0xFFFF;
-            op = 0b100100101;
+            op = arm.OP_MOVN;
         }
         int invert = pattern;
         for (int i=0; i<4; i++) {
@@ -45,11 +45,11 @@ public class IntARM extends ConstantNode implements MachNode {
             x >>= 16;
             if (block != pattern) {
                 enc.add4(arm.mov(op, i, block ^ invert, self));
-                op = 0b111100101;
+                op = arm.OP_MOVK;
                 invert = 0;
             }
         }
-        if (op != 0b111100101) {
+        if (op != arm.OP_MOVK) {
             // All blocks are the same, special case
             enc.add4(arm.mov(op, 0, 0, self));
         }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LoadARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LoadARM.java
index 6d11b2f..2700fdb 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LoadARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LoadARM.java
@@ -21,9 +21,9 @@ public class LoadARM extends MemOpARM{
     // ldr(immediate - unsigned offset) | ldr(register)
     @Override public void encoding( Encoding enc ) {
         if(_declaredType == SONTypeFloat.F32 || _declaredType == SONTypeFloat.F64) {
-            ldst_encode(enc, 0b1111110101, 0b11111100011, this, true);
+            ldst_encode(enc, arm.OPF_LOAD_IMM, arm.OPF_LOAD_R, this, true);
         } else {
-            ldst_encode(enc, 0b1111100101, 0b11111000011, this, false);
+            ldst_encode(enc, arm.OP_LOAD_IMM, arm.OP_LOAD_R, this, false);
         }
     }
     @Override public void asm(CodeGen code, SB sb) {
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LslARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LslARM.java
index 0596fc2..594e0e4 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LslARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LslARM.java
@@ -11,7 +11,7 @@ public class LslARM extends MachConcreteNode implements MachNode {
     @Override public RegMask regmap(int i) { return arm.RMASK; }
     @Override public RegMask outregmap() { return arm.RMASK; }
 
-    @Override public void encoding( Encoding enc ) { arm.shift_reg(enc,this,0b1000); }
+    @Override public void encoding( Encoding enc ) { arm.shift_reg(enc,this,arm.OP_LSL); }
     @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)));
     }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LslIARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LslIARM.java
index 9201580..a82fbf4 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LslIARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LslIARM.java
@@ -24,7 +24,7 @@ public class LslIARM extends MachConcreteNode implements MachNode {
         assert _imm > 0;
         // UBFM <Xd>, <Xn>, #(-<shift> MOD 64), #(63-<shift>)
         // immr must be (-<shift> MOD 64) = 64 - shift
-        enc.add4(arm.imm_shift(0b1101001101, 64 - _imm, (64 - _imm) - 1, rn, rd));
+        enc.add4(arm.imm_shift(arm.OPI_LSL, 64 - _imm, (64 - _imm) - 1, rn, rd));
     }
     // General form: "lsli  rd = rs1 << imm"
     @Override public void asm(CodeGen code, SB sb) {
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LsrARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LsrARM.java
index afd84ba..af4763b 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LsrARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LsrARM.java
@@ -11,7 +11,7 @@ public class LsrARM extends MachConcreteNode implements MachNode {
     @Override public RegMask regmap(int i) { return arm.RMASK; }
     @Override public RegMask outregmap() { return arm.RMASK; }
 
-    @Override public void encoding( Encoding enc ) { arm.shift_reg(enc,this,0b1001); }
+    @Override public void encoding( Encoding enc ) { arm.shift_reg(enc,this,arm.OP_LSR); }
     @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)));
     }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LsrIARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LsrIARM.java
index 72629ff..f1fff80 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LsrIARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LsrIARM.java
@@ -20,7 +20,7 @@ public class LsrIARM extends MachConcreteNode implements MachNode {
         short rd = enc.reg(this);
         short rn = enc.reg(in(1));
         assert _imm > 0;
-        enc.add4(arm.imm_shift(0b1101001101,_imm, 0b111111, rn,rd));
+        enc.add4(arm.imm_shift(arm.OPI_LSR,_imm, 0b111111, rn,rd));
     }
     // General form: "lsri  rd = rs1 >>> imm"
     @Override public void asm(CodeGen code, SB sb) {
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/MulARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/MulARM.java
index 776f507..1f69b83 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/MulARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/MulARM.java
@@ -11,7 +11,7 @@ public class MulARM extends MachConcreteNode implements MachNode {
     @Override public RegMask regmap(int i) { return arm.RMASK; }
     @Override public RegMask outregmap() { return arm.RMASK; }
 
-    @Override public void encoding( Encoding enc ) { arm.madd(enc,this,0b10011011000,31); }
+    @Override public void encoding( Encoding enc ) { arm.madd(enc,this,arm.OP_MUL,31); }
     // 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)));
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/MulFARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/MulFARM.java
index 3143d6e..1464fc9 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/MulFARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/MulFARM.java
@@ -9,7 +9,7 @@ public class MulFARM extends MachConcreteNode implements MachNode{
     @Override public String op() { return "mulf"; }
     @Override public RegMask regmap(int i) { return arm.DMASK; }
     @Override public RegMask outregmap() { return arm.DMASK; }
-    @Override public void encoding( Encoding enc ) { arm.f_scalar(enc,this,0b10); }
+    @Override public void encoding( Encoding enc ) { arm.f_scalar(enc,this,arm.OPF_MUL); }
     // Default on double precision for now(64 bits)
     // General form: "VMUL.f32  rd = src1 * src2
     @Override public void asm(CodeGen code, SB sb) {
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/NewARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/NewARM.java
index 76a1424..e867fed 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/NewARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/NewARM.java
@@ -15,7 +15,7 @@ public class NewARM extends NewNode implements MachNode {
 
     @Override public void encoding( Encoding enc ) {
         // bl
-        enc.external(this,"calloc").add4(arm.b(0b100101, 0));
+        enc.external(this,"calloc").add4(arm.b(arm.OP_CALL, 0));
     }
 
     @Override public void asm(CodeGen code, SB sb) {
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/NotARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/NotARM.java
index 97537eb..19d0408 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/NotARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/NotARM.java
@@ -18,9 +18,9 @@ public class NotARM extends MachConcreteNode implements MachNode{
         // subtracting zero from rs will just yield rs, it sets the zero flag and then it's used in cset
         short self = enc.reg(this );
         short reg1 = enc.reg(in(1));
-        int subs = arm.imm_inst(0b1111000100, 0, reg1, self);
+        int subs = arm.imm_inst(arm.OP_SUBS, 0, reg1, self);
         enc.add4(subs);
-        int cset = arm.cond_set(0b10011010100, 31, arm.COND.EQ, 63, reg1);
+        int cset = arm.cond_set(arm.OP_CSET, 31, arm.COND.EQ, 63, reg1);
         enc.add4(cset);
     }
 
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/OrARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/OrARM.java
index 5b07950..7208ea9 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/OrARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/OrARM.java
@@ -11,7 +11,7 @@ public class OrARM extends MachConcreteNode implements MachNode {
     @Override public String glabel() { return "|"; }
     @Override public RegMask regmap(int i) { return arm.RMASK; }
     @Override public RegMask outregmap() { return arm.RMASK; }
-    @Override public void encoding( Encoding enc ) { arm.r_reg(enc,this,0b10101010); }
+    @Override public void encoding( Encoding enc ) { arm.r_reg(enc,this,arm.OP_OR); }
     // 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)));
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/OrIARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/OrIARM.java
index 7773166..c33de85 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/OrIARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/OrIARM.java
@@ -15,7 +15,7 @@ public class OrIARM extends MachConcreteNode implements MachNode {
     @Override public RegMask regmap(int i) { return arm.RMASK; }
     @Override public RegMask outregmap() { return arm.RMASK; }
     @Override public void encoding( Encoding enc ) {
-        arm.imm_inst_n(enc, this, 0b101100100, _imm);
+        arm.imm_inst_n(enc, this, in(1), arm.OPI_OR, _imm);
     }
     // General form: "ori  rd = rs1 | imm"
     @Override public void asm(CodeGen code, SB sb) {
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/RetARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/RetARM.java
index 1250365..7497cb4 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/RetARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/RetARM.java
@@ -21,8 +21,8 @@ public class RetARM extends ReturnNode implements MachNode {
     @Override public void encoding( Encoding enc ) {
         int frameAdjust = ((FunARM)fun())._frameAdjust;
         if( frameAdjust > 0 )
-            enc.add4(arm.imm_inst(0b1001000100, (frameAdjust*-8)&0xFFF, arm.RSP, arm.RSP));
-        enc.add4(arm.ret(0b1101011001011111000000));
+            enc.add4(arm.imm_inst(arm.OPI_ADD, (frameAdjust*-8)&0xFFF, arm.RSP, arm.RSP));
+        enc.add4(arm.ret(arm.OP_RET));
     }
 
     @Override public void asm(CodeGen code, SB sb) {
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SetARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SetARM.java
index c8b149d..d3806f9 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SetARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SetARM.java
@@ -19,9 +19,8 @@ public class SetARM extends MachConcreteNode implements MachNode {
     @Override public RegMask regmap(int i) { assert i==1; return arm.FLAGS_MASK; }
     @Override public RegMask outregmap() { return arm.RMASK; }
 
-    // Encoding is appended into the byte array; size is returned
     @Override public void encoding( Encoding enc ) {
-        int body = arm.cset(0b1001101010011111, arm.make_condition(_bop), 0b0111111, enc.reg(this));
+        int body = arm.cset(arm.OP_CSET,0b11111, arm.make_condition(_bop), 0b011111, enc.reg(this));
         enc.add4(body);
     }
 
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SplitARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SplitARM.java
index 3498554..1686008 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SplitARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SplitARM.java
@@ -24,31 +24,35 @@ public class SplitARM extends SplitNode {
                 throw Utils.TODO();
             }
             int off = enc._fun.computeStackSlot(dst - arm.MAX_REG)*8;
-            enc.add4(arm.load_str_imm(0b1111100100, off, src, dst));
+            if( srcX ) src -= arm.D_OFFSET;
+            enc.add4(arm.load_str_imm(arm.OP_STORE_IMM, off, arm.RSP, src));
+            return;
         }
 
         if(src >= arm.MAX_REG) {
             // Load from SP
             int off = enc._fun.computeStackSlot(src - arm.MAX_REG) * 8;
-            enc.add4(arm.load_str_imm(0b1111100101, off, src, dst));
+            if( dstX ) dst -= arm.D_OFFSET;
+            enc.add4(arm.load_str_imm(arm.OP_LOAD_IMM, off, arm.RSP, dst));
+            return;
         }
 
         // pick opcode based on regs
         if(!dstX && !srcX) {
             // GPR->GPR
-            enc.add4(arm.mov_reg(0b10101010000, src, dst));
+            enc.add4(arm.mov_reg(arm.OP_MOV, src, dst));
         } else if(dstX && srcX) {
             // FPR->FPR
             // fmov reg
-            enc.add4(arm.f_mov_reg(0b00011110, src,dst));
-        } else if(dstX && !srcX) {
+            enc.add4(arm.f_mov_reg(arm.OP_FMOV_REG, src - arm.D_OFFSET,dst - arm.D_OFFSET));
+        } else if(!srcX && dstX) {
             // GPR->FPR
             // FMOV(general) 64 bits to DOUBLE-PRECISION
-            enc.add4(arm.f_mov_general(0b10011110, 0b01, 0, 0b111, src, dst));
-        } else if(!dstX && srcX) {
+            enc.add4(arm.f_mov_general(arm.OP_FMOV, 0b01, 0, 0b111, src, dst - arm.D_OFFSET));
+        } else if( srcX && !dstX) {
             //FPF->GPR
             // FMOV(general) DOUBLE-PRECISION to 64 bits
-            enc.add4(arm.f_mov_general(0b10011110, 0b01, 0, 0b110, src, dst));
+            enc.add4(arm.f_mov_general(arm.OP_FMOV, 0b01, 0, 0b110, src - arm.D_OFFSET, dst));
         }
     }
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/StoreARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/StoreARM.java
index 69fc9f1..2bab0ee 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/StoreARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/StoreARM.java
@@ -24,9 +24,9 @@ public class StoreARM extends MemOpARM {
     @Override public RegMask outregmap() { return null; }
     @Override public void encoding( Encoding enc ) {
         if(_declaredType == SONTypeFloat.F32 || _declaredType == SONTypeFloat.F64) {
-            ldst_encode(enc, 0b1111110100,0b11111100001, val(), true);
+            ldst_encode(enc, arm.OPF_STORE_IMM,arm.OPF_STORE_R, val(), true);
         } else {
-            ldst_encode(enc, 0b1111100100, 0b11111000001, val(), false);
+            ldst_encode(enc, arm.OP_STORE_IMM, arm.OP_STORE_R, val(), false);
         }
     }
     @Override public void asm(CodeGen code, SB sb) {
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SubARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SubARM.java
index 847a0b4..43e71fb 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SubARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SubARM.java
@@ -10,7 +10,7 @@ public class SubARM extends MachConcreteNode implements MachNode {
     @Override public RegMask regmap(int i) { return arm.RMASK; }
     @Override public RegMask outregmap() { return arm.RMASK; }
 
-    @Override public void encoding( Encoding enc ) { arm.r_reg(enc,this,0b11001011); }
+    @Override public void encoding( Encoding enc ) { arm.r_reg(enc,this,arm.OP_SUB); }
 
     // General form: "sub  # rd = rs1 - rs2"
     @Override public void asm(CodeGen code, SB sb) {
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SubFARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SubFARM.java
index a1326f2..b43dbc4 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SubFARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SubFARM.java
@@ -9,7 +9,7 @@ public class SubFARM extends MachConcreteNode implements MachNode {
     @Override public String op() { return "subf"; }
     @Override public RegMask regmap(int i) { return arm.DMASK; }
     @Override public RegMask outregmap() { return arm.DMASK; }
-    @Override public void encoding( Encoding enc ) { arm.f_scalar(enc,this,0b1110); }
+    @Override public void encoding( Encoding enc ) { arm.f_scalar(enc,this,arm.OPF_SUB); }
     // Default on double precision for now(64 bits)
     // General form: "vsub.f32  rd = src1 + src2
     @Override public void asm(CodeGen code, SB sb) {
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SubIARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SubIARM.java
index 2ada6af..b7ff3a9 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SubIARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SubIARM.java
@@ -18,7 +18,7 @@ public class SubIARM extends MachConcreteNode implements MachNode {
     @Override public RegMask regmap(int i) { return arm.RMASK; }
     @Override public RegMask outregmap() { return arm.RMASK; }
 
-    @Override public void encoding( Encoding enc ) { arm.imm_inst(enc,this,0b1101000100,_imm); }
+    @Override public void encoding( Encoding enc ) { arm.imm_inst(enc,this, in(1), 0b1101000100,_imm); }
 
     // General form: "subi  rd = rs1 - imm"
     @Override public void asm(CodeGen code, SB sb) {
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/TFPARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/TFPARM.java
index 1be5a4e..757c809 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/TFPARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/TFPARM.java
@@ -18,10 +18,10 @@ public class TFPARM extends ConstantNode implements MachNode, RIPRelSize {
         enc.relo(this);
         short self = enc.reg(this);
         // adrp    x0, 0
-        int adrp = arm.adrp(1,0, 0b10000, 0,self);
+        int adrp = arm.adrp(1,0, arm.OP_ADRP, 0,self);
         // add     x0, x0, 0
-        arm.imm_inst(enc,this,0b1001000100,0);
         enc.add4(adrp);
+        arm.imm_inst(enc,arm.OPI_ADD,0, 0);
     }
 
     @Override public byte encSize(int delta) {
@@ -32,15 +32,16 @@ public class TFPARM extends ConstantNode implements MachNode, RIPRelSize {
     // Delta is from opcode start
     @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) {
         short rpc = enc.reg(this);
-        if(opLen ==4 ) {
+        if(opLen == 8 ) {
             // opstart of add
             int next = opStart + opLen;
             int adrp_delta = delta >> 12;
             // patch upper 20 bits via adrp
             enc.patch4(opStart, arm.adrp(1, adrp_delta & 0b11, 0b10000, adrp_delta >> 2, rpc));
             // low 12 bits via add
-            enc.patch4(next, arm.imm_inst_l(enc, this, 0b1001000100, delta & 0xfff));
+            enc.patch4(next, arm.imm_inst_l(arm.OPI_ADD, delta & 0xfff, rpc));
         } else {
+            // should not happen as one instruction is 4 byte, and TFP arm encodes 2.
             throw Utils.TODO();
         }
     }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/UJmpARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/UJmpARM.java
index fa566b5..65e314f 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/UJmpARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/UJmpARM.java
@@ -20,7 +20,7 @@ public class UJmpARM extends CFGNode implements MachNode, RIPRelSize {
     @Override public Node idealize() { throw Utils.TODO(); }
     @Override public void encoding( Encoding enc ) {
         enc.jump(this,uctrl());
-        int body = arm.b(0b01010100, 0);
+        int body = arm.b(arm.OP_UJMP, 0);
         enc.add4(body);
     }
 
@@ -34,7 +34,7 @@ public class UJmpARM extends CFGNode implements MachNode, RIPRelSize {
     // Delta is from opcode start
     @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) {
         if( opLen==4 ) {
-            enc.patch4(opStart,arm.b(0b01010100, delta));
+            enc.patch4(opStart,arm.b(arm.OP_UJMP, delta));
         } else {
             throw Utils.TODO();
         }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/XorARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/XorARM.java
index ac9e38b..0132936 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/XorARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/XorARM.java
@@ -10,7 +10,7 @@ public class XorARM extends MachConcreteNode implements MachNode{
     @Override public String glabel() { return "^"; }
     @Override public RegMask regmap(int i) { return arm.RMASK; }
     @Override public RegMask outregmap() { return arm.RMASK; }
-    @Override public void encoding( Encoding enc ) { arm.r_reg(enc,this,0b11001010); }
+    @Override public void encoding( Encoding enc ) { arm.r_reg(enc,this,arm.OP_XOR); }
     // General form: "rd = x1 ^ x2"
     @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)));
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/XorIARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/XorIARM.java
index 2dc314a..cb3a28d 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/XorIARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/XorIARM.java
@@ -20,7 +20,7 @@ public class XorIARM extends MachConcreteNode implements MachNode {
     @Override public RegMask outregmap() { return arm.RMASK; }
 
     // General form: "xori  rd = rs1 ^ imm"
-    @Override public void encoding( Encoding enc ) { arm.imm_inst_n(enc,this,0b110100100,_imm); }
+    @Override public void encoding( Encoding enc ) { arm.imm_inst_n(enc,this, in(1), arm.OPI_XOR,_imm); }
     @Override public void asm(CodeGen code, SB sb) {
         sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" ^ #").p(_imm);
     }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/arm.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/arm.java
index 4f01b8d..34db62e 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/arm.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/arm.java
@@ -90,6 +90,79 @@ public arm( CodeGen code ) {
     static final RegMask D6_MASK = new RegMask(D6);
     static final RegMask D7_MASK = new RegMask(D7);
 
+    // major opcode: OP
+    public static int OP_ADD       = 0b10_001_011;
+    public static int OPF_ADD      = 0b00_011_110;
+    public static int OPI_ADD      = 0b10_010_00100;
+
+    public static int OP_UJMP      = 0b01010100;
+
+    public static int OP_ADRP      = 0b10000;
+
+    public static int OP_ASR       = 0b1010;
+    public static int OPI_ASR      = 0b10_0100_1101;
+
+    public static int OP_LSL       = 0b1000;
+    public static int OPI_LSL       =0b1101001101;
+
+
+    public static int OP_LSR        = 0b1001;
+    public static int OPI_LSR       = 0b1101001101;
+
+    public static int OP_BRANCH    = 0b01_0101_00;
+    public static int OP_CALL      = 0b10_010_1;
+
+    public static int OP_CALLRARM  = 0b1101011000111111000000;
+
+    public static int OP_SUBS      = 0b1111000100;
+    public static int OP_CSET      = 0b10011010100;
+    public static int OP_CMP       = 0b11101011;
+    public static int OPI_CMP      = 0b1111000100;
+
+    public static int OP_DIV       = 0b10011010110;
+    public static int OPF_DIV      = 0b000110;
+
+    public static int OP_MUL       = 0b10011011000;
+    public static int OPF_MUL      =  0b10;
+
+    public static int OPF_ARM      = 0b01011100;
+
+    public static int OP_FLOAT_C   = 0b10011110;
+
+    public static int OP_XOR       = 0b11001010;
+    public static int OPI_XOR      = 0b110100100;
+
+    public static int OP_AND       = 0b10_0010_10;
+    public static int OPI_AND      = 0b10_0100_100;
+
+    public static int OP_OR        = 0b10101010;
+    public static int OPI_OR       = 0b101100100;
+
+    public static int OP_SUB       = 0b11001011;
+    public static int OPF_SUB      = 0b1110;
+
+    public static int OP_MOVK      = 0b111100101;
+    public static int OP_MOVN      = 0b100100101;
+    public static int OP_MOVZ      = 0b110100101;
+
+    public static int OP_RET       = 0b1101011001011111000000;
+    // Store/load opcodes
+    public static int OP_LOAD_R    = 0b11111000011;
+    public static int OP_LOAD_IMM  = 0b1111100101;
+
+    public static int OPF_LOAD_R    = 0b11111100011;
+    public static int OPF_LOAD_IMM  = 0b1111110101;
+
+    public static int OP_STORE_R    = 0b11111000001;
+    public static int OP_STORE_IMM  = 0b1111100100;
+
+    public static int OPF_STORE_R    = 0b11111100001;
+    public static int OPF_STORE_IMM  = 0b1111110100;
+
+    public static int OP_FMOV        = 0b10011110;
+    public static int OP_FMOV_REG    = 0b00011110;
+    // https://docsmirror.github.io/A64/2023-06/mov_orr_log_shift.html
+    public static int OP_MOV         = 0b10101010000;
     // Calling convention; returns a machine-specific register
     // for incoming argument idx.
     // index 0 for control, 1 for memory, real args start at index 2
@@ -125,7 +198,16 @@ public enum OPTION {
         SXTX,
     }
 
+    static public int cset(int opcode, int rm, COND cond, int rn, int rd) {
+        assert 0 <= rm && rm < 32;
+        assert 0 <= rn && rn < 32;
+        assert 0 <= rd && rd < 32;
+        return (opcode << 21) | (rm << 15) | (cond.ordinal() << 12) | (rn << 5) | rd;
+    }
+
     static public int cset(int opcode, COND cond, int rn, int rd) {
+        assert 0 <= rn && rn < 32;
+        assert 0 <= rd && rd < 32;
         return (opcode << 16) | (cond.ordinal() << 12) | (rn << 5) | rd;
     }
 
@@ -210,24 +292,34 @@ private static int imm12Logical(SONTypeInteger ti) {
 
     // sh is encoded in opcdoe
     public static int imm_inst(int opcode, int imm12, int rn, int rd) {
-        assert opcode >=0 && imm12 >= 0 && rn >=0 && rd>=0; // Caller zeros high order bits
+        assert 0 <= rn && rn < 32;
+        assert 0 <= rd && rd < 32;
+        assert opcode >=0 && imm12 >= 0; // Caller zeros high order bits
         return (opcode << 22) | (imm12 << 10) | (rn << 5) | rd;
     }
 
     public static int imm_shift(int opcode, int imm, int imms, int rn,  int rd)  {
-            return (opcode << 22) | (1 << 22) | (imm << 16) | (imms << 10) | (rn << 5) | rd;
+        assert 0 <= rn && rn < 32;
+        assert 0 <= rd && rd < 32;
+        return (opcode << 22) | (1 << 22) | (imm << 16) | (imms << 10) | (rn << 5) | rd;
     }
 
-    public static void imm_inst(Encoding enc, Node n, int opcode, int imm12) {
+    public static void imm_inst(Encoding enc, Node n,Node n2,  int opcode, int imm12) {
         short self = enc.reg(n);
-        short reg1 = enc.reg(n.in(1));
+        short reg1 = enc.reg(n2);
         int body = imm_inst(opcode, imm12&0xFFF, reg1, self);
         enc.add4(body);
     }
 
-    public static void imm_inst_n(Encoding enc, Node n, int opcode, int imm13) {
+    // for cases where rs1 and dst are the same, eg add x0, x0, 1
+    public static void imm_inst(Encoding enc, int opcode, int imm12, int self) {
+        int body = imm_inst(opcode, imm12&0xFFF, self, self);
+        enc.add4(body);
+    }
+
+    public static void imm_inst_n(Encoding enc, Node n, Node n2, int opcode, int imm13) {
         short self = enc.reg(n);
-        short reg1 = enc.reg(n.in(1));
+        short reg1 = enc.reg(n2);
 
         int body = imm_inst_n(opcode, imm13, reg1, self);
         enc.add4(body);
@@ -235,10 +327,18 @@ public static void imm_inst_n(Encoding enc, Node n, int opcode, int imm13) {
 
     // nth bit comes from immediate and not opcode
     public static int imm_inst_n(int opcode, int imm13, int rn, int rd) {
+        assert 0 <= rn && rn < 32;
+        assert 0 <= rd && rd < 32;
         assert 0 <= imm13 && imm13 <= 0x1FFF;
         return (opcode << 23) | (imm13 << 10) | (rn << 5) | rd;
     }
 
+    public static int imm_inst_l(int opcode, int imm12, int self) {
+        int body = imm_inst(opcode, imm12&0xFFF, self, self);
+        return body;
+   }
+
+    // for cases where rs1 and dst are the same, eg add x0, x0, 1
     public static int imm_inst_l(Encoding enc, Node n, int opcode, int imm12) {
         short self = enc.reg(n);
         short reg1 = enc.reg(n.in(1));
@@ -249,17 +349,23 @@ public static int imm_inst_l(Encoding enc, Node n, int opcode, int imm12) {
     // for normal add, reg1, reg2 cases (reg-to-reg)
     // using shifted-reg form
     public static int r_reg(int opcode, int shift, int rm, int imm6, int rn, int rd) {
+        assert 0 <= rm && rm < 32;
+        assert 0 <= rn && rn < 32;
+        assert 0 <= rd && rd < 32;
         return (opcode << 24) | (shift << 21) | (rm << 16) | (imm6 << 10) << (rn << 5) | rd;
-    }
-    public static void r_reg(Encoding enc, Node n, int opcode) {
-        short self = enc.reg(n);
-        short reg1 = enc.reg(n.in(1));
-        short reg2 = enc.reg(n.in(2));
-        int body = r_reg(opcode, 0, reg2, 0,  reg1, self);
-        enc.add4(body);
+   }
+   public static void r_reg(Encoding enc, Node n, int opcode) {
+       short self = enc.reg(n);
+       short reg1 = enc.reg(n.in(1));
+       short reg2 = enc.reg(n.in(2));
+       int body = r_reg(opcode, 0, reg2, 0,  reg1, self >= 32 ? reg1: self);
+       enc.add4(body);
     }
 
     public static int shift_reg(int opcode, int rm, int op2, int rn, int rd) {
+        assert 0 <= rn && rn < 32;
+        assert 0 <= rm && rm < 32;
+        assert 0 <= rd && rd < 32;
         return (opcode << 21) | (rm << 16) | (op2 << 10) | (rn << 5) | rd;
     }
     public static void shift_reg(Encoding enc, Node n, int op2) {
@@ -272,6 +378,9 @@ public static void shift_reg(Encoding enc, Node n, int op2) {
 
     //  MUL can be considered an alias for MADD with the third operand Ra being set to 0
     public static int madd(int opcode, int rm, int ra, int rn, int rd) {
+        assert 0 <= rn && rn < 32;
+        assert 0 <= rd && rd < 32;
+        assert 0 <= rm && rm < 32;
         return (opcode << 21) | (rm << 16) | (ra << 10) | (rn << 5) | rd;
     }
     public static void madd(Encoding enc, Node n, int opcode, int ra) {
@@ -284,11 +393,13 @@ public static void madd(Encoding enc, Node n, int opcode, int ra) {
 
     // encodes movk, movn, and movz
     public static int mov(int opcode, int shift, int imm16, int rd) {
+        assert 0 <= rd && rd < 32;
         return (opcode << 23) | (shift << 21) | (imm16 << 5) | rd;
     }
 
-    public static int mov_reg(int opcode, int src, int dst) {
-        return (opcode  << 21) | (src << 16) | 0b11111 << 5 | dst;
+    public static int mov_reg(int opcode, int src, int rd) {
+        assert 0 <= rd && rd < 32;
+        return (opcode  << 21) | (src << 16) | 0b11111 << 5 | rd;
     }
 
     public static int ret(int opcode) {
@@ -296,18 +407,21 @@ public static int ret(int opcode) {
     }
 
     // FMOV (scalar, immediate)
-    public static int f_mov(int opcode, int ftype,  int imm8, int rd) {
-        return (opcode << 24) | (ftype << 21) |(imm8 << 13) | (128 << 5) | rd;
-    }
-
     public static int f_scalar(int opcode, int ftype, int rm, int op, int rn, int rd) {
+        assert 0 <= rn && rn < 32;
+        assert 0 <= rd && rd < 32;
+        assert 0 <= rm && rm < 32;
         return (opcode << 24) | (ftype << 22) | (1 << 21) | (rm << 16) | (op << 10) | (rn << 5) | rd;
     }
 
     public static int f_mov_reg(int opcode, int rn, int rd) {
+        assert 0 <= rn && rn < 32;
+        assert 0 <= rd && rd < 32;
         return (opcode << 24) | (0b01100000010000 << 10) | (rn << 5) | rd;
     }
     public static int f_mov_general(int opcode, int ftype, int rmode, int opcode1, int rn, int rd) {
+        assert 0 <= rn && rn < 32;
+        assert 0 <= rd && rd < 32;
         return (opcode << 24) |(ftype << 22) | (1 << 21) | (rmode << 19) | (opcode1 << 16) | (rn << 5) | rd;
     }
     public static void f_scalar(Encoding enc, Node n, int op ) {
@@ -324,19 +438,20 @@ public static int load_pc(int opcode, int offset, int rt) {
     }
     // int l
     public static int adrp(int op, int imlo,int opcode, int imhi, int rd) {
+        assert 0 <= rd && rd < 32;
         return (op << 31) | (imlo << 29) |(opcode << 24) | (imhi << 5) | rd;
     }
 
-    public static int load_adr(int opcode, int offset, int base, int rt) {
-        return (opcode << 22) | (offset << 10) | (base << 5) | rt;
-    }
-
     // [Rptr+Roff]
     public static int indr_adr(int opcode, int off, STORE_LOAD_OPTION option, int s, int ptr, int rt) {
+        assert 0 <= ptr && ptr < 32;
+        assert 0 <= rt &&  rt  < 32;
         return (opcode << 21) | (off << 16) | (option.ordinal() << 13) | (s << 12) | (2 << 10) | (ptr << 5) | rt;
     }
     // [Rptr+imm9]
     public static int load_str_imm(int opcode, int imm12, int ptr, int rt) {
+        assert 0 <= ptr && ptr < 32;
+        assert 0 <= rt &&  rt  < 32;
         return (opcode << 22) | (imm12 << 10)  |(ptr << 5) | rt;
     }
 
@@ -351,12 +466,16 @@ public static int f_convert(int opcode_1, int opcode_2, int opcode_3, int opcode
                 (vd << 12) | (0x01100010 << 4) | vm;
     }
     public static int float_cast(int opcode, int ftype, int rn, int rd) {
+        assert 0 <= rd &&  rd < 32;
+        assert 0 <= rn &&  rn  < 32;
         return (opcode << 24) | (ftype << 22) | (2176 << 10) | (rn << 5) | rd;
     }
 
 
     // ftype = 3
     public static int f_cmp(int opcode, int ftype, int rm, int rn) {
+        assert 0 <= rn && rn  < 32;
+        assert 0 <= rm && rm  < 32;
         return (opcode  << 24) | (ftype << 21) | (rm << 16) | (8 << 10) | (rn << 5) | 8;
     }
     public static void f_cmp(Encoding enc, Node n) {
@@ -386,11 +505,14 @@ public static int b_cond(int opcode, int delta, COND cond) {
     }
 
     public static int cond_set(int opcode, int rm, COND cond, int rn, int rd) {
+        assert 0 <= rd &&  rd < 32;
+        assert 0 <= rn &&  rn  < 32;
         return (opcode << 21) | (rm << 16) | (cond.ordinal() << 12) | (rn << 5) | rd;
     }
 
     // Branch with Link to Register calls a subroutine at an address in a register, setting register X30 to PC+4.
     public static int blr(int opcode, int rd) {
+        assert 0 <= rd && rd < 32;
         return opcode << 10 | rd << 5;
     }
     public static int b(int opcode, int delta) {
@@ -485,7 +607,7 @@ static RegMask retMask( SONTypeFunPtr tfp, int i ) {
     }
 
     // Break an infinite loop
-    @Override public IfNode never(CFGNode ctrl ) {
+    @Override public NeverNode never(CFGNode ctrl ) {
         throw Utils.TODO();
     }
 
@@ -569,7 +691,7 @@ private Node add(AddNode add) {
 
     private Node sub(SubNode sub) {
         return sub.in(2) instanceof ConstantNode con && con._con instanceof SONTypeInteger ti && imm12(ti)
-            ? new SubIARM(sub, (int)ti.value())
+            ? new AddIARM(sub, (int)(-ti.value()))
             : new SubARM(sub);
     }
 
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/AUIPC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/AUIPC.java
index 5a848a1..8691f78 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/AUIPC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/AUIPC.java
@@ -4,20 +4,20 @@
 import com.compilerprogramming.ezlang.compiler.codegen.*;
 import com.compilerprogramming.ezlang.compiler.nodes.ConstantNode;
 import com.compilerprogramming.ezlang.compiler.nodes.MachNode;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFunPtr;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
 
 // Add upper 20bits to PC.  Immediate comes from the relocation info.
 public class AUIPC extends ConstantNode implements MachNode, RIPRelSize {
-    AUIPC( SONTypeFunPtr tfp ) { super(tfp); }
+    AUIPC( SONType tfp ) { super(tfp); }
     @Override public RegMask regmap(int i) { return null; }
     @Override public RegMask outregmap() { return riscv.WMASK; }
     @Override public boolean isClone() { return true; }
-    @Override public AUIPC copy() { return new AUIPC((SONTypeFunPtr)_con); }
+    @Override public AUIPC copy() { return new AUIPC(_con); }
     @Override public String op() { return "auipc"; }
     @Override public void encoding( Encoding enc ) {
         enc.relo(this);
         short dst = enc.reg(this);
-        enc.add4(riscv.u_type(0x17, dst, 0));
+        enc.add4(riscv.u_type(riscv.OP_AUIPC, dst, 0));
     }
 
     // Delta is from opcode start, but X86 measures from the end of the 5-byte encoding
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/AddIRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/AddIRISC.java
index 1de9018..44ed6e0 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/AddIRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/AddIRISC.java
@@ -8,7 +8,7 @@ public class AddIRISC extends ImmRISC {
     public AddIRISC( Node add, int imm12, boolean pop ) { super(add,imm12,pop); }
     @Override public String op() { return "addi"; }
     @Override public String glabel() { return "+"; }
-    @Override int opcode() {  return riscv.I_TYPE; }
+    @Override int opcode() {  return riscv.OP_IMM; }
     @Override int func3() {return 0;}
     @Override public AddIRISC copy() {
         // Clone the AddI, using the same inputs-only code used during inst select.
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/AndIRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/AndIRISC.java
index b37f960..b10049d 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/AndIRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/AndIRISC.java
@@ -4,9 +4,9 @@
 import com.compilerprogramming.ezlang.compiler.nodes.Node;
 
 public class AndIRISC extends ImmRISC {
-    AndIRISC( Node and, int imm) { super(and,imm); }
+    public AndIRISC( Node and, int imm) { super(and,imm); }
     @Override public String op() { return "andi"; }
     @Override public String glabel() { return "&"; }
-    @Override int opcode() {  return riscv.I_TYPE; }
+    @Override int opcode() {  return riscv.OP_IMM; }
     @Override int func3() {return 7;}
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/AndRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/AndRISC.java
index 01677f0..8916ddb 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/AndRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/AndRISC.java
@@ -5,11 +5,11 @@
 import com.compilerprogramming.ezlang.compiler.nodes.*;
 
 public class AndRISC extends MachConcreteNode implements MachNode {
-    AndRISC(Node and) { super(and); }
+    public AndRISC(Node and) { super(and); }
     @Override public String op() { return "and"; }
     @Override public RegMask regmap(int i) { return riscv.RMASK; }
     @Override public RegMask outregmap() { return riscv.WMASK; }
-    @Override public void encoding( Encoding enc ) { riscv.r_type(enc,this,7,0);  }
+    @Override public void encoding( Encoding enc ) { riscv.r_type(enc,this,0b111,0);  }
     @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)));
     }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/BranchRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/BranchRISC.java
index a6bfb29..3fe6397 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/BranchRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/BranchRISC.java
@@ -68,9 +68,8 @@ public BranchRISC( IfNode iff, String bop, Node n1, Node n2 ) {
         String src1 = in(1)==null ? "#0" : code.reg(in(1));
         String src2 = in(2)==null ? "#0" : code.reg(in(2));
         sb.p(src1).p(" ").p(_bop).p(" ").p(src2).p(" ");
-        CFGNode prj = cproj(0);
-        while( prj.nOuts() == 1 )
-            prj = prj.uctrl();       // Skip empty blocks
+        CFGNode prj = cproj(0).uctrlSkipEmpty();
+        if( !prj.blockHead() ) prj = prj.cfg0();
         sb.p(label(prj));
     }
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CallRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CallRISC.java
index 67ff05d..5b4512f 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CallRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CallRISC.java
@@ -32,7 +32,7 @@ public class CallRISC extends CallNode implements MachNode, RIPRelSize {
         // Long form:  auipc rX,imm20/32; jal r0,[rX+imm12/32]
         enc.relo(this);
         short rpc = enc.reg(this);
-        enc.add4(riscv.j_type(riscv.J_JAL, rpc, 0));
+        enc.add4(riscv.j_type(riscv.OP_JAL, rpc, 0));
     }
 
     // Delta is from opcode start
@@ -46,7 +46,7 @@ public class CallRISC extends CallNode implements MachNode, RIPRelSize {
     @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) {
         short rpc = enc.reg(this);
         if( opLen==4 ) {
-            enc.patch4(opStart,riscv.j_type(riscv.J_JAL, rpc, delta));
+            enc.patch4(opStart,riscv.j_type(riscv.OP_JAL, rpc, delta));
         } else {
             throw Utils.TODO();
         }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CallRRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CallRRISC.java
index c3a7757..22d08d6 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CallRRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CallRRISC.java
@@ -18,7 +18,7 @@ public class CallRRISC extends CallNode implements MachNode {
     @Override public void encoding( Encoding enc ) {
         short rpc = enc.reg(this);
         short src = enc.reg(in(_inputs._len-1));
-        int body = riscv.i_type(0x67, rpc, 0, src, 0);
+        int body = riscv.i_type(riscv.OP_JALR, rpc, 0, src, 0);
         enc.add4(body);
     }
 
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CastRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CastRISC.java
index 0bc06b3..e879d3e 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CastRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CastRISC.java
@@ -8,7 +8,7 @@
 import com.compilerprogramming.ezlang.compiler.nodes.MachNode;
 
 public class CastRISC extends CastNode implements MachNode {
-    CastRISC( CastNode cast ) { super(cast); }
+    public CastRISC( CastNode cast ) { super(cast); }
     @Override public String op() { return label(); }
     @Override public RegMask regmap(int i) { assert i==1; return RegMask.FULL; }
     @Override public RegMask outregmap() { return RegMask.FULL; }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/DivRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/DivRISC.java
index c7482d7..efc6b96 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/DivRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/DivRISC.java
@@ -5,7 +5,7 @@
 import com.compilerprogramming.ezlang.compiler.nodes.*;
 
 public class DivRISC extends MachConcreteNode implements MachNode{
-    DivRISC(Node div) {super(div);}
+    public DivRISC(Node div) {super(div);}
     @Override public String op() { return "div"; }
     @Override public RegMask regmap(int i) { return riscv.RMASK; }
     @Override public RegMask outregmap() { return riscv.WMASK; }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/FltRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/FltRISC.java
index 0a477dd..32e205d 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/FltRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/FltRISC.java
@@ -16,13 +16,15 @@ public class FltRISC extends ConstantNode implements MachNode, RIPRelSize {
     @Override public FltRISC copy() { return new FltRISC(this); }
 
     @Override public void encoding( Encoding enc ) {
-        enc.largeConstant(this,_con);
+        enc.largeConstant(this,_con, 0, -1/*TODO: RISC5 style patching*/);
         short dst = (short)(enc.reg(this) - riscv.F_OFFSET);
+        short tmp = (short)riscv.T6;
         // AUIPC dst,#hi20_constant_pool
-        enc.add4(riscv.u_type(0b0010111, dst, 0));
+        enc.add4(riscv.u_type(riscv.OP_AUIPC, tmp, 0));
         // Load dst,[dst+#low12_constant_pool]
-        enc.add4(riscv.i_type(0x07, dst, 0x03, dst, 0));
+        enc.add4(riscv.i_type(riscv.OP_LOAD, dst, 0b11, tmp, 0));
     }
+    @Override public RegMask killmap() { return new RegMask(riscv.T6); }
 
     // Delta is from opcode start.
     // TODO: always size 8?
@@ -31,10 +33,11 @@ public class FltRISC extends ConstantNode implements MachNode, RIPRelSize {
     // Delta is from opcode start
     @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) {
         short dst = (short)(enc.reg(this) - riscv.F_OFFSET);
+        short tmp = (short)riscv.T6;
         // AUIPC dst,#hi20_constant_pool
-        enc.patch4(opStart  , riscv.u_type(0b0010111, dst, delta));
+        enc.patch4(opStart  , riscv.u_type(riscv.OP_AUIPC, tmp, delta));
         // Load dst,[dst+#low12_constant_pool]
-        enc.patch4(opStart+4, riscv.i_type(0x07, dst, 0x03, dst, delta));
+        enc.patch4(opStart+4, riscv.i_type(riscv.OP_LOAD, dst, 0b11, tmp, delta));
     }
 
     @Override public void asm(CodeGen code, SB sb) {
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/FunRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/FunRISC.java
index 6f7b98d..c06a40c 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/FunRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/FunRISC.java
@@ -27,7 +27,7 @@ public class FunRISC extends FunNode implements MachNode {
         _maxArgSlot = riscv.maxSlot(enc._fun.sig());
         _frameAdjust = (short) (_maxSlot+1 - _maxArgSlot);
         if( _frameAdjust == 0 ) return; // Skip if no frame adjust
-        enc.add4(riscv.i_type(riscv.I_TYPE, riscv.SP, 0, riscv.SP, (_frameAdjust*8) & 0xFFF));
+        enc.add4(riscv.i_type(riscv.OP_IMM, riscv.SP, 0, riscv.SP, (_frameAdjust*8) & 0xFFF));
     }
 
     @Override public void asm(CodeGen code, SB sb) {
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/I2F8RISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/I2F8RISC.java
index 00854e4..4202af5 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/I2F8RISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/I2F8RISC.java
@@ -14,7 +14,7 @@ public class I2F8RISC extends MachConcreteNode implements MachNode {
     @Override public void encoding( Encoding enc ) {
         short dst  = (short)(enc.reg(this )-riscv.F_OFFSET);
         short src1 =         enc.reg(in(1));
-        int body = riscv.r_type(0b1010011,dst,riscv.RM.RNE.ordinal(),src1,0,0x69);
+        int body = riscv.r_type(riscv.OP_FP,dst,riscv.RM.RNE.ordinal(),src1,0,0x69);
         enc.add4(body);
     }
     @Override public void asm(CodeGen code, SB sb) {
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/IntRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/IntRISC.java
index 635155e..c230467 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/IntRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/IntRISC.java
@@ -20,7 +20,7 @@ public class IntRISC extends ConstantNode implements MachNode {
         SONTypeInteger ti = (SONTypeInteger)_con;
         // Explicit truncation of larger immediates; this will sign-extend on
         // load and this is handled during instruction selection.
-        enc.add4(riscv.i_type(riscv.I_TYPE, dst, 0, riscv.ZERO, (int)(ti.value() & 0xFFF)));
+        enc.add4(riscv.i_type(riscv.OP_IMM, dst, 0, riscv.ZERO, (int)(ti.value() & 0xFFF)));
     }
     @Override public void asm(CodeGen code, SB sb) {
         String reg = code.reg(this);
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/LUI.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/LUI.java
index a85eba9..e63e001 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/LUI.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/LUI.java
@@ -21,7 +21,7 @@ public LUI( int imm20 ) {
         long x = ((SONTypeInteger)_con).value();
         int imm20 = (int)(x>>12) & 0xFFFFF;
         short dst = enc.reg(this);
-        int lui = riscv.u_type(0x17, dst, imm20);
+        int lui = riscv.u_type(riscv.OP_LUI, dst, imm20);
         enc.add4(lui);
     }
 
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/LoadRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/LoadRISC.java
index d3e6479..89e1f0f 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/LoadRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/LoadRISC.java
@@ -10,15 +10,16 @@
 // idx  = null
 // off  = off - imm12 added to base
 public class LoadRISC extends MemOpRISC {
-    LoadRISC(LoadNode ld, Node base, int off) { super(ld, base, off, null); }
+    public LoadRISC(LoadNode ld, Node base, int off) { super(ld, base, off, null); }
     @Override public String op() { return "ld" +_sz; }
     @Override public RegMask regmap(int i) { return riscv.RMASK; }
     @Override public RegMask outregmap() { return riscv.MEM_MASK; }
     @Override public void encoding( Encoding enc ) {
-        short dst = xreg(enc);
+        short dst = enc.reg(this );
         short ptr = enc.reg(ptr());
-        int body = riscv.i_type(opcode(enc), dst, func3(), ptr, _off);
-        enc.add4(body);
+        int op = dst >= riscv.F_OFFSET ? riscv.OP_LOADFP  : riscv.OP_LOAD;
+        if( dst >= riscv.F_OFFSET  ) dst -= riscv.F_OFFSET;
+        enc.add4(riscv.i_type(op, dst, func3(), ptr, _off));
     }
 
     @Override public void asm(CodeGen code, SB sb) {
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/MemOpRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/MemOpRISC.java
index 23988e6..c66f079 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/MemOpRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/MemOpRISC.java
@@ -9,7 +9,7 @@
 
 public abstract class MemOpRISC extends MemOpNode implements MachNode {
     final int _off;             // Limit 12 bits
-    final char _sz = (char)('0'+(1<<_declaredType.log_size()));
+    final char _sz;
     MemOpRISC(MemOpNode mop, Node base, int off, Node val) {
         super(mop,mop);
         assert base._type instanceof SONTypeMemPtr;
@@ -17,6 +17,7 @@ public abstract class MemOpRISC extends MemOpNode implements MachNode {
         _inputs.setX(3, null);  // Never an index
         _inputs.setX(4, val );
         _off = off;
+        _sz = (char)('0'+(1<<_declaredType.log_size()));
     }
 
     @Override public String label() { return op(); }
@@ -30,14 +31,24 @@ public abstract class MemOpRISC extends MemOpNode implements MachNode {
     // func3 is based on load/store size and extend
     int func3() {
         int func3 = -1;
-        if( _declaredType == SONTypeInteger. I8 ) func3=0; // LB   SB
-        if( _declaredType == SONTypeInteger.I16 ) func3=1; // LH   SH
-        if( _declaredType == SONTypeInteger.I32 ) func3=2; // LW   SW
-        if( _declaredType == SONTypeInteger.BOT ) func3=3; // LD   SD
+        // no unsigned flavour for store, so both signed and unsigned trigger the same
+        if(this instanceof StoreRISC) {
+            if( _declaredType == SONTypeInteger. I8 || _declaredType == SONTypeInteger.U8  || _declaredType == SONTypeInteger.BOOL) func3=0; //   SB
+            if( _declaredType == SONTypeInteger.I16 || _declaredType == SONTypeInteger.U16 ) func3=1; // SH
+            if( _declaredType == SONTypeInteger.I32 || _declaredType == SONTypeInteger.U32 || _declaredType instanceof SONTypeMemPtr) func3=2; //  SW
+            if( _declaredType == SONTypeInteger.BOT ) func3=3; //   SD
+            if( func3 == -1 ) throw Utils.TODO();
+            return func3;
+        }
+        if( _declaredType == SONTypeInteger. I8 ) func3=0; // LB
+        if( _declaredType == SONTypeInteger.I16 ) func3=1; // LH
+        if( _declaredType == SONTypeInteger.I32 ) func3=2; // LW
+        if( _declaredType == SONTypeInteger.BOT ) func3=3; // LD
         if( _declaredType == SONTypeInteger. U8 ) func3=4; // LBU
         if( _declaredType == SONTypeInteger.BOOL) func3=4; // LBU
         if( _declaredType == SONTypeInteger.U16 ) func3=5; // LHU
         if( _declaredType == SONTypeInteger.U32 ) func3=6; // LWU
+
         // float
         if(_declaredType == SONTypeFloat.F32) func3 = 2; // fLW   fSW
         if(_declaredType == SONTypeFloat.F64) func3 = 3; // fLD   fSD
@@ -46,19 +57,6 @@ int func3() {
         return func3;
     }
 
-    // 7 bits, 00 000 11 or 00 001 11 for FP
-    int opcode(Encoding enc) {
-        if( enc.reg(this) < riscv.F_OFFSET ) return 3;
-        else if( this instanceof StoreRISC )
-            // opcode is the same for 32 bit and 64 bits
-            return 39;
-        return 7;
-    }
-    short xreg(Encoding enc) {
-        short xreg = enc.reg(this );
-        return xreg < riscv.F_OFFSET ? xreg : (short)(xreg-riscv.F_OFFSET);
-    }
-
     // Register mask allowed on input i.
     @Override public RegMask regmap(int i) {
         // 0 - ctrl
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/MulRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/MulRISC.java
index 1930da7..585cc1f 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/MulRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/MulRISC.java
@@ -6,7 +6,7 @@
 
 // mulh signed multiply instruction(no-imm form)
 public class MulRISC extends MachConcreteNode implements MachNode{
-    MulRISC(Node mul) {super(mul);}
+    public MulRISC(Node mul) {super(mul);}
     @Override public String op() { return "mul"; }
     @Override public RegMask regmap(int i) { assert i==1 || i==2; return riscv.RMASK; }
     @Override public RegMask outregmap() { return riscv.WMASK; }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/NJmpRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/NJmpRISC.java
new file mode 100644
index 0000000..d7d6e2e
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/NJmpRISC.java
@@ -0,0 +1,46 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFunPtr;
+import java.util.BitSet;
+
+public class NJmpRISC extends NeverNode implements MachNode, RIPRelSize {
+    // label is obtained implicitly
+    public NJmpRISC( CFGNode cfg ) { super(cfg); }
+
+    @Override public String op() { return "jmp"; }
+    @Override public String label() { return op(); }
+    @Override public String comment() { return "L"+cproj(1)._nid; }
+    @Override public StringBuilder _print1( StringBuilder sb, BitSet visited ) { return sb.append("jmp "); }
+    @Override public RegMask regmap(int i) { throw Utils.TODO(); }
+    @Override public RegMask outregmap() { return null; }
+
+    @Override public void encoding( Encoding enc ) {
+        // Short form +/-4K:  beq r0,r0,imm12
+        // Long form:  auipc rX,imm20/32; jal r0,[rX+imm12/32]
+        enc.jump(this,cproj(0));
+        enc.add4(riscv.j_type(riscv.OP_JAL, 0, 0));
+    }
+
+    // Delta is from opcode start
+    @Override public byte encSize(int delta) {
+        if( -(1L<<20) <= delta && delta < (1L<<20) ) return 4;
+        // 2 word encoding needs a tmp register, must teach RA
+        throw Utils.TODO();
+    }
+
+    // Delta is from opcode start
+    @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) {
+        if( opLen==4 ) {
+            enc.patch4(opStart,riscv.j_type(riscv.OP_JAL, 0, delta));
+        } else {
+            throw Utils.TODO();
+        }
+    }
+
+    @Override public void asm(CodeGen code, SB sb) {
+        sb.p(label(cproj(0).uctrl()));
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/NewRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/NewRISC.java
index a561f3b..8c3fbeb 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/NewRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/NewRISC.java
@@ -16,14 +16,13 @@ public class NewRISC extends NewNode implements MachNode {
     @Override public RegMask outregmap() { return null; }
     @Override public RegMask killmap() { return riscv.riscCallerSave(); }
 
-    // Encoding is appended into the byte array; size is returned
     @Override public void encoding( Encoding enc ) {
         // Generic external encoding; 2 ops.
         enc.external(this,"calloc");
         // A1 is a caller-save, allowed to crush building external address
         // auipc
-        enc.add4(riscv.u_type(0x17, riscv.A1, 0));
-        enc.add4(riscv.i_type(0x67, riscv.RPC, 0, riscv.A1, 0));
+        enc.add4(riscv.u_type(riscv.OP_AUIPC, riscv.A1, 0));
+        enc.add4(riscv.i_type(riscv.OP_JALR, riscv.RPC, 0, riscv.A1, 0));
     }
 
     // General form: "alloc #bytes  PC"
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/NotRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/NotRISC.java
index 94d2a92..81b568a 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/NotRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/NotRISC.java
@@ -10,7 +10,7 @@ public class NotRISC extends ImmRISC {
     @Override public RegMask outregmap() { return riscv.RMASK;  }
     @Override public String op() { return "not"; }
     // sltiu: x <u 1 ? 1 : 0;  only zero is unsigned-less-than 1
-    @Override int opcode() { return 19; }
+    @Override int opcode() { return riscv.OP_IMM; }
     @Override int func3() { return 2; }
     @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(this)); }
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/OrIRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/OrIRISC.java
index 5f42d2c..5d5923c 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/OrIRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/OrIRISC.java
@@ -4,8 +4,8 @@
 import com.compilerprogramming.ezlang.compiler.nodes.Node;
 
 public class OrIRISC extends ImmRISC {
-    OrIRISC( Node and, int imm) { super(and,imm); }
-    @Override int opcode() {  return riscv.I_TYPE; }
+    public OrIRISC( Node and, int imm) { super(and,imm); }
+    @Override int opcode() {  return riscv.OP_IMM; }
     @Override int func3() {  return 6; }
     @Override public String glabel() { return "|"; }
     @Override public String op() { return "ori"; }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/OrRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/OrRISC.java
index 2a3c298..abfd57d 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/OrRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/OrRISC.java
@@ -5,7 +5,7 @@
 import com.compilerprogramming.ezlang.compiler.nodes.*;
 
 public class OrRISC extends MachConcreteNode implements MachNode {
-    OrRISC(Node or) { super(or); }
+    public OrRISC(Node or) { super(or); }
     @Override public String op() { return "or"; }
     @Override public RegMask regmap(int i) { return riscv.RMASK; }
     @Override public RegMask outregmap() { return riscv.WMASK; }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/RetRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/RetRISC.java
index 14dba81..0cf0e8d 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/RetRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/RetRISC.java
@@ -27,9 +27,9 @@ public RetRISC(ReturnNode ret, FunNode fun) {
     @Override public void encoding( Encoding enc ) {
         int frameAdjust = fun()._frameAdjust;
         if( frameAdjust > 0 )
-            enc.add4(riscv.i_type(riscv.I_TYPE, riscv.SP, 0, riscv.SP, (frameAdjust*-8) & 0xFFF));
+            enc.add4(riscv.i_type(riscv.OP_IMM, riscv.SP, 0, riscv.SP, (frameAdjust*-8) & 0xFFF));
         short rpc = enc.reg(rpc());
-        enc.add4(riscv.i_type(0x67, riscv.ZERO, 0, rpc, 0));
+        enc.add4(riscv.i_type(riscv.OP_JALR, riscv.ZERO, 0, rpc, 0));
     }
 
     @Override public void asm(CodeGen code, SB sb) {
@@ -41,7 +41,7 @@ public RetRISC(ReturnNode ret, FunNode fun) {
             // Prints return reg (either A0 or FA0), RPC (always R1) and then
             // the callee-save registers.
             for( int i=2; i<nIns(); i++ )
-                sb.p(code.reg(in(i))).p("  ");
+                sb.p(in(i)==null ? "---" : code.reg(in(i))).p("  ");
 
         // Post-allocation, if we did not get the expected return register, print what we got
         else if( code._regAlloc.regnum(rpc()) != riscv.RPC )
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SetFRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SetFRISC.java
index dff08f4..a56b1a2 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SetFRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SetFRISC.java
@@ -23,7 +23,7 @@ public class SetFRISC extends MachConcreteNode implements MachNode {
         short dst  =         enc.reg(this );
         short src1 = (short)(enc.reg(in(1))-riscv.F_OFFSET);
         short src2 = (short)(enc.reg(in(2))-riscv.F_OFFSET);
-        int body = riscv.r_type(0x53,dst,riscv.fsetop(_bop),src1,src2,0);
+        int body = riscv.r_type(riscv.OP_FP,dst,riscv.fsetop(_bop),src1,src2,0);
         enc.add4(body);
     }
 
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SetIRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SetIRISC.java
index fe388c2..7b3b3a4 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SetIRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SetIRISC.java
@@ -6,12 +6,12 @@
 // corresponds to slti,sltiu
 public class SetIRISC extends ImmRISC {
     final boolean _unsigned;    // slti vs sltiu
-    SetIRISC( Node src, int imm12, boolean unsigned ) {
+    public SetIRISC( Node src, int imm12, boolean unsigned ) {
         super(src,imm12);
         _unsigned = unsigned;
     }
-    @Override public String op() { return "setlt" + (_unsigned ? "u":""); }
+    @Override public String op() { return "slt" + (_unsigned ? "u":"") + "i"; }
     @Override public String glabel() { return  "<"+ (_unsigned ? "u":""); }
-    @Override int opcode() { return 19; }
-    @Override int func3() { return 2; }
+    @Override int opcode() { return 0b0010011; }
+    @Override int func3() { return _unsigned ? 0b011 : 0b010; }
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SetRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SetRISC.java
index c65c9c3..aedd8ee 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SetRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SetRISC.java
@@ -4,14 +4,17 @@
 import com.compilerprogramming.ezlang.compiler.codegen.*;
 import com.compilerprogramming.ezlang.compiler.nodes.*;
 
-// corresponds to slt (and NOT sltu, slti, sltiu)
+// corresponds to slt sltu
 public class SetRISC extends MachConcreteNode implements MachNode {
-    SetRISC( Node cmp ) { super(cmp); }
-    @Override public String op() { return "slt"; }
+    final boolean _unsigned;
+    public SetRISC( Node cmp, boolean unsigned ) { super(cmp); _unsigned=unsigned; }
+    @Override public String op() { return "slt" + (_unsigned ? "u":""); }
     @Override public RegMask regmap(int i) { return riscv.RMASK; }
     @Override public RegMask outregmap() { return riscv.WMASK; }
-    @Override public void encoding( Encoding enc ) { riscv.r_type(enc,this,2,0); }
+    @Override public void encoding( Encoding enc ) {
+        riscv.r_type(enc,this,_unsigned ? 0b011 : 0b010,0);
+    }
     @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)));
     }
-}
+}
\ No newline at end of file
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SllIRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SllIRISC.java
index c0f419f..391d35c 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SllIRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SllIRISC.java
@@ -4,8 +4,8 @@
 import com.compilerprogramming.ezlang.compiler.nodes.Node;
 
 public class SllIRISC extends ImmRISC {
-    SllIRISC( Node and, int imm) { super(and,imm); }
-    @Override int opcode() {  return riscv.I_TYPE; }
+    public SllIRISC( Node and, int imm) { super(and,imm); }
+    @Override int opcode() {  return riscv.OP_IMM; }
     @Override int func3() {  return 1; }
     @Override public String glabel() { return "<<"; }
     @Override public String op() { return "slli"; }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SllRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SllRISC.java
index 01e6e5a..7a2200a 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SllRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SllRISC.java
@@ -6,7 +6,7 @@
 
 // Shift left logical
 public class SllRISC extends MachConcreteNode implements MachNode {
-    SllRISC(Node sll) { super(sll); }
+    public SllRISC(Node sll) { super(sll); }
     @Override public String op() { return "shl"; }
     @Override public RegMask regmap(int i) { return riscv.RMASK; }
     @Override public RegMask outregmap() { return riscv.WMASK; }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SplitRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SplitRISC.java
index bface26..2c64a2e 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SplitRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SplitRISC.java
@@ -22,15 +22,19 @@ public class SplitRISC extends SplitNode {
             if( src >= riscv.MAX_REG ) {
                 throw Utils.TODO(); // Very rare stack-stack move
             }
-
             int off = enc._fun.computeStackSlot(dst - riscv.MAX_REG)*8;
-            // store 64 bit values
-            enc.add4(riscv.s_type(39, 3, riscv.SP, dst, off));
+            int op = srcX ? riscv.OP_STOREFP : riscv.OP_STORE;
+            if( srcX ) src -= riscv.F_OFFSET;
+            enc.add4(riscv.s_type(op, 0b011, riscv.SP, src, off));
+            return;
         }
         if( src >= riscv.MAX_REG ) {
             // Load from SP
             int off = enc._fun.computeStackSlot(src - riscv.MAX_REG)*8;
-            enc.add4(riscv.i_type(7, dst, 3,riscv.SP, off));
+            int op = dstX ? riscv.OP_LOADFP : riscv.OP_LOAD;
+            if( dstX ) dst -= riscv.F_OFFSET;
+            enc.add4(riscv.i_type(op, dst, 0b011, riscv.SP, off));
+            return;
         }
         // pick opcode based on regs
         if( !dstX && !srcX ) {
@@ -38,17 +42,17 @@ public class SplitRISC extends SplitNode {
             enc.add4(riscv.r_type(riscv.OP,dst,0,src,riscv.ZERO,0));
         } else if( dstX && srcX ) {
             // FPR->FPR
-            // Can do: FPR->GPR->FPR
-            enc.add4(riscv.r_type(0b1010011, dst, 0, src, 0, 0b1110001));
-            enc.add4(riscv.r_type(0b1010011, dst, 0, src, 0, 0b0100000));
-        } else if( dstX && !srcX ) {
+            src -= riscv.F_OFFSET;
+            dst -= riscv.F_OFFSET;
+            enc.add4(riscv.r_type(riscv.OP_FP, dst, 0b000, src, src, 0b0010101)); // dst = FMIN(src,src)
+        } else if(!srcX && dstX) {
             //GPR->FPR
             // fmv.d.x
-            enc.add4(riscv.r_type(0b1010011, dst, 0, src, 0, 0b0100000));
-        } else if( !dstX && srcX ) {
+            enc.add4(riscv.r_type(riscv.OP_FP, dst - riscv.F_OFFSET, 0, src, 0, 0b1111001));
+        } else if(srcX && !dstX) {
             //FPR->GPR
             //fmv.x.d
-            enc.add4(riscv.r_type(0b1010011, dst, 0, src, 0, 0b1110001));
+            enc.add4(riscv.r_type(riscv.OP_FP, dst, 0, src - riscv.F_OFFSET, 0, 0b1110001));
         }
 
     }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SraIRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SraIRISC.java
index 95b66c7..2ccf60a 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SraIRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SraIRISC.java
@@ -4,8 +4,8 @@
 import com.compilerprogramming.ezlang.compiler.nodes.Node;
 
 public class SraIRISC extends ImmRISC {
-    SraIRISC( Node and, int imm) { super(and,imm); }
-    @Override int opcode() {  return riscv.I_TYPE; }
+    public SraIRISC( Node and, int imm) { super(and,imm); }
+    @Override int opcode() {  return riscv.OP_IMM; }
     @Override int func3() {  return 5;}
     @Override public String glabel() { return ">>"; }
     @Override public String op() { return "srai"; }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SraRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SraRISC.java
index 9d919cb..61cd5aa 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SraRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SraRISC.java
@@ -6,11 +6,11 @@
 
 // Right Shift Arithmetic
 public class SraRISC extends MachConcreteNode implements MachNode {
-    SraRISC(Node sra) { super(sra); }
+    public SraRISC(Node sra) { super(sra); }
     @Override public String op() { return "sar"; }
     @Override public RegMask regmap(int i) { return riscv.RMASK; }
     @Override public RegMask outregmap() { return riscv.WMASK; }
-    @Override public void encoding( Encoding enc ) { riscv.r_type(enc,this,5,0x20);  }
+    @Override public void encoding( Encoding enc ) { riscv.r_type(enc,this,0b101,0b00100000);  }
     @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)));
     }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SrlIRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SrlIRISC.java
index a19c72b..45da4c4 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SrlIRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SrlIRISC.java
@@ -4,8 +4,8 @@
 import com.compilerprogramming.ezlang.compiler.nodes.Node;
 
 public class SrlIRISC extends ImmRISC {
-    SrlIRISC( Node and, int imm, boolean pop) { super(and,imm,pop); }
-    @Override int opcode() {  return riscv.I_TYPE; }
+    public SrlIRISC( Node and, int imm, boolean pop) { super(and,imm,pop); }
+    @Override int opcode() {  return riscv.OP_IMM; }
     @Override int func3() {  return 5; }
     @Override public String glabel() { return ">>>"; }
     @Override public String op() { return "srli"; }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SrlRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SrlRISC.java
index 37b7059..d3c09f0 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SrlRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SrlRISC.java
@@ -6,7 +6,7 @@
 
 // Right Shift Logical
 public class SrlRISC extends MachConcreteNode implements MachNode {
-    SrlRISC(Node srli) { super(srli); }
+    public SrlRISC(Node srli) { super(srli); }
     @Override public String op() { return "shr"; }
     @Override public RegMask regmap(int i) { return riscv.RMASK; }
     @Override public RegMask outregmap() { return riscv.WMASK; }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/StoreRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/StoreRISC.java
index 7f16a1b..7a76f55 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/StoreRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/StoreRISC.java
@@ -32,10 +32,11 @@ public class StoreRISC extends MemOpRISC {
     @Override public RegMask outregmap() { return null; }
 
     @Override public void encoding( Encoding enc ) {
-        short val = xreg(enc);
+        short val = enc.reg(val());
         short ptr = enc.reg(ptr());
-        int body = riscv.s_type(opcode(enc), func3()&3, ptr, val, _off);
-        enc.add4(body);
+        int op = val >= riscv.F_OFFSET ? riscv.OP_STOREFP : riscv.OP_STORE;
+        if( val >= riscv.F_OFFSET  ) val -= riscv.F_OFFSET;
+        enc.add4(riscv.s_type(op, func3()&7, ptr, val, _off));
     }
 
     @Override public void asm(CodeGen code, SB sb) {
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SubRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SubRISC.java
index 652210f..76be3ef 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SubRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SubRISC.java
@@ -5,7 +5,7 @@
 import com.compilerprogramming.ezlang.compiler.nodes.*;
 
 public class SubRISC extends MachConcreteNode implements MachNode {
-    SubRISC( Node sub ) { super(sub); }
+    public SubRISC( Node sub ) { super(sub); }
     @Override public String op() { return "sub"; }
     @Override public RegMask regmap(int i) { assert i==1 || i==2; return riscv.RMASK; }
     @Override public RegMask outregmap() { return riscv.WMASK; }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/TFPRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/TFPRISC.java
index e0cf2df..d04275d 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/TFPRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/TFPRISC.java
@@ -19,9 +19,9 @@ public class TFPRISC extends ConstantNode implements MachNode, RIPRelSize {
         short dst = enc.reg(this);
         SONTypeFunPtr tfp = (SONTypeFunPtr)_con;
         // auipc  t0,0
-        int auipc = riscv.u_type(0b0010111, dst, 0);
+        int auipc = riscv.u_type(riscv.OP_AUIPC, dst, 0);
         // addi   t1,t0 + #0
-        int addi = riscv.i_type(0b0010011, dst, 0, dst, 0);
+        int addi = riscv.i_type(riscv.OP_IMM, dst, 0, dst, 0);
         enc.add4(auipc);
         enc.add4(addi);
     }
@@ -34,15 +34,16 @@ public class TFPRISC extends ConstantNode implements MachNode, RIPRelSize {
     // Delta is from opcode start
     @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) {
         short rpc = enc.reg(this);
-        if( opLen==4 ) {
+        if( opLen==8 ) {
             // AUIPC (upper 20 bits)
             // opstart of add
             int next = opStart + opLen;
-            enc.patch4(opStart,riscv.u_type(0b0010111, rpc, delta));
+            enc.patch4(opStart,riscv.u_type(riscv.OP_AUIPC, rpc, delta));
             // addi(low 12 bits)
-            enc.patch4(next,riscv.i_type(0b0010011, rpc, 0, rpc, delta & 0xFFF));
+            enc.patch4(next,riscv.i_type(riscv.OP_IMM, rpc, 0, rpc, delta & 0xFFF));
             // addi
         } else {
+             // should not happen as one instruction is 4 byte, and TFP arm encodes 2.
             throw Utils.TODO();
         }
     }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/UJmpRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/UJmpRISC.java
index ca5087c..07959cf 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/UJmpRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/UJmpRISC.java
@@ -8,12 +8,9 @@
 
 // unconditional jump
 public class UJmpRISC extends CFGNode implements MachNode, RIPRelSize {
-    //UJmpRISC() { }
     @Override public String op() { return "jmp"; }
     @Override public String label() { return op(); }
-    @Override public StringBuilder _print1( StringBuilder sb, BitSet visited ) {
-        return sb.append("jmp ");
-    }
+    @Override public StringBuilder _print1( StringBuilder sb, BitSet visited ) { return sb.append("jmp "); }
     @Override public RegMask regmap(int i) {return null; }
     @Override public RegMask outregmap() { return null; }
     @Override public SONType compute() { throw Utils.TODO(); }
@@ -22,7 +19,7 @@ public class UJmpRISC extends CFGNode implements MachNode, RIPRelSize {
         // Short form +/-4K:  beq r0,r0,imm12
         // Long form:  auipc rX,imm20/32; jal r0,[rX+imm12/32]
         enc.jump(this,uctrl());
-        enc.add4(riscv.j_type(riscv.J_JAL, 0, 0));
+        enc.add4(riscv.j_type(riscv.OP_JAL, 0, 0));
     }
 
     // Delta is from opcode start
@@ -35,7 +32,7 @@ public class UJmpRISC extends CFGNode implements MachNode, RIPRelSize {
     // Delta is from opcode start
     @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) {
         if( opLen==4 ) {
-            enc.patch4(opStart,riscv.j_type(riscv.J_JAL, 0, delta));
+            enc.patch4(opStart,riscv.j_type(riscv.OP_JAL, 0, delta));
         } else {
             throw Utils.TODO();
         }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/XorIRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/XorIRISC.java
index 0f2338c..b7c03b0 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/XorIRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/XorIRISC.java
@@ -4,8 +4,8 @@
 import com.compilerprogramming.ezlang.compiler.nodes.Node;
 
 public class XorIRISC extends ImmRISC {
-    XorIRISC( Node and, int imm) { super(and,imm); }
-    @Override int opcode() {  return riscv.I_TYPE; }
+    public XorIRISC( Node and, int imm) { super(and,imm); }
+    @Override int opcode() {  return riscv.OP_IMM; }
     @Override int func3() {  return 4; }
     @Override public String glabel() { return "^"; }
     @Override public String op() { return "xori"; }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/XorRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/XorRISC.java
index 2542201..0d77e5e 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/XorRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/XorRISC.java
@@ -5,7 +5,7 @@
 import com.compilerprogramming.ezlang.compiler.nodes.*;
 
 public class XorRISC extends MachConcreteNode implements MachNode {
-    XorRISC(Node or) { super(or); }
+    public XorRISC(Node or) { super(or); }
     @Override public String op() { return "xor"; }
     @Override public RegMask regmap(int i) { return riscv.RMASK; }
     @Override public RegMask outregmap() { return riscv.WMASK; }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/riscv.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/riscv.java
index 7cead1f..c9adfeb 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/riscv.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/riscv.java
@@ -101,22 +101,34 @@ public riscv(CodeGen code) {
             FA6_MASK,
             FA7_MASK
     };
-    //
+
     // major opcode: OP
-    public static int OP    = 0b01_100_11;
-    public static int OP_FP = 0b10_100_11;
+    public static int OP_LOAD    = 0b00_000_11;
+    public static int OP_LOADFP  = 0b00_001_11;
     public static int OP_CUSTOM0 = 0b00_010_11;
-    public static int OP_BRANCH  = 0b11_000_11;
+    public static int OP_IMM     = 0b00_100_11;
+    public static int OP_AUIPC   = 0b00_101_11;
 
-    //I_type opcode: 0010 0011
-    public static int I_TYPE = 0x13;
+    public static int OP_STORE   = 0b01_000_11;
+    public static int OP_STOREFP = 0b01_001_11;
+    public static int OP_CUSTOM1 = 0b01_010_11;
+    public static int OP         = 0b01_100_11;
+    public static int OP_LUI     = 0b01_101_11;
 
-    // 0110 0111
-    public static int J_JAL = 0b1101111;
+    public static int OP_CUSTOM2 = 0b10_010_11;
+    public static int OP_FP      = 0b10_100_11;
+
+    public static int OP_BRANCH  = 0b11_000_11;
+    public static int OP_JALR    = 0b11_001_11;
+    public static int OP_RESERVED= 0b11_010_11;
+    public static int OP_JAL     = 0b11_011_11;
 
 
     // Since riscv instructions are fixed we can just or them togehter
     public static int r_type(int opcode, int rd, int func3, int rs1, int rs2, int func7) {
+        assert 0 <= rs1 && rs1 < 32;
+        assert 0 <= rs2 && rs2 < 32;
+        assert 0 <= rd &&  rd  < 32;
         return (func7 << 25) | (rs2 << 20) | (rs1 << 15) | (func3 << 12) | (rd << 7) | opcode;
     }
     public static void r_type(Encoding enc, Node n, int func3, int func7) {
@@ -126,6 +138,7 @@ public static void r_type(Encoding enc, Node n, int func3, int func7) {
         int body = r_type(OP,dst,func3,src1,src2,func7);
         enc.add4(body);
     }
+
     public static void rf_type(Encoding enc, Node n, RM func3, int func7) {
         short dst  = (short)(enc.reg(n      )-F_OFFSET);
         short src1 = (short)(enc.reg(n.in(1))-F_OFFSET);
@@ -136,11 +149,13 @@ public static void rf_type(Encoding enc, Node n, RM func3, int func7) {
 
 
     public static int u_type(int opcode, int rd, int imm20) {
+        assert 0 <= rd && rd < 32;
         return (imm20 << 12) | (rd << 7) | opcode;
     }
 
     public static int j_type(int opcode, int rd, int delta) {
         assert -(1L<<20) <= delta && delta < (1L<<20);
+        assert 0 <= rd && rd < 32;
         // Messy branch offset encoding
         // 31 30-21 20 19-12 11-7  6-0
         // 20 10- 1 11 19-12 rpc   JAL
@@ -149,22 +164,25 @@ public static int j_type(int opcode, int rd, int delta) {
         int imm11    = (delta>>11) &     1;
         int imm12_19 = (delta>>12) &  0xFF;
         int imm20    = (delta>>19) &     1;
-        int bits = imm20<<20 | imm12_19 << 12 | imm11 << 11 | imm10_01;
+        int bits = imm20<<19 | imm10_01 << 9 | imm11 << 8 | imm12_19;
         return bits << 12 | rd << 7 | opcode;
     }
 
 
     public static int i_type(int opcode, int rd, int func3, int rs1, int imm12) {
-        assert opcode >= 0 && rd >=0 && func3 >=0 && rs1 >=0 && imm12 >= 0; // Zero-extend by caller
+        assert 0 <= rd  &&  rd  < 32;
+        assert 0 <= rs1 &&  rs1 < 32;
+        assert opcode >= 0 && func3 >=0 && imm12 >= 0; // Zero-extend by caller
         return  (imm12 << 20) | (rs1 << 15) | (func3 << 12) | (rd << 7) | opcode;
     }
 
 
     // S-type instructions(store)
-    public static int s_type(int opcode, int offset1, int func3, int rs1, int rs2, int offset2) {
-        return (offset2 << 25) | (rs2 << 20) | (rs1 << 15) | (func3 << 12) | (offset1 << 7) | opcode;
-    }
     public static int s_type(int opcode, int func3, int rs1, int rs2, int imm12) {
+        assert 0 <= rs1 &&  rs1 < 32;
+        assert 0 <= rs2 &&  rs2 < 32;
+        assert 0 <= func3;
+
         assert imm12 >= 0;      // Masked to high zero bits by caller
         int imm_lo = imm12 & 0x1F;
         int imm_hi = imm12 >> 5;
@@ -173,6 +191,8 @@ public static int s_type(int opcode, int func3, int rs1, int rs2, int imm12) {
 
     // BRANCH
     public static int b_type(int opcode, int func3, short rs1, short rs2, int delta) {
+        assert 0 <= rs1 && rs1 < 32;
+        assert 0 <= rs2 && rs2 < 32;
         assert -4*1024 <= delta && delta < 4*1024;
         assert (delta&1)==0;    // Low bit is always zero, not encoded
         // Messy branch offset encoding
@@ -320,9 +340,7 @@ static RegMask retMask( SONTypeFunPtr tfp, int i ) {
     @Override  public SplitNode split(String kind, byte round, LRG lrg) { return new SplitRISC(kind,round);  }
 
     // Break an infinite loop
-    @Override public IfNode never( CFGNode ctrl ) {
-        throw Utils.TODO();
-    }
+    @Override public NeverNode never( CFGNode ctrl ) { return new NJmpRISC(ctrl);}
 
     // True if signed 12-bit immediate
     public static boolean imm12(SONTypeInteger ti) {
@@ -433,9 +451,9 @@ private Node cmp(BoolNode bool) {
         return switch( bool.op() ) {
         case "<" -> bool.in(2) instanceof ConstantNode con && con._con instanceof SONTypeInteger ti && imm12(ti)
             ? new SetIRISC(bool, (int)ti.value(),false)
-            : new SetRISC(bool);
+            : new SetRISC(bool, false);
         // x <= y - flip and invert; !(y < x); `slt tmp=y,x; xori dst=tmp,#1`
-        case "<=" -> new XorIRISC(new SetRISC(bool.swap12()),1);
+        case "<=" -> new XorIRISC(new SetRISC(bool.swap12(), false),1);
         // x == y - sub and vs0 == `sub tmp=x-y; sltu dst=tmp,#1`
         case "==" -> new SetIRISC(new SubRISC(bool),1,true);
         default -> throw Utils.TODO();
@@ -460,6 +478,7 @@ private Node con( ConstantNode con ) {
             // from a constant pool, which does not need an extra register
             throw Utils.TODO();
         }
+        // Load from constant pool
         case SONTypeFloat   tf  -> new FltRISC(con);
         case SONTypeFunPtr  tfp -> new TFPRISC(con);
         case SONTypeMemPtr  tmp -> throw Utils.TODO();
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CmpIX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CmpIX86.java
index fe08e20..02d4e55 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CmpIX86.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CmpIX86.java
@@ -31,7 +31,7 @@ public class CmpIX86 extends MachConcreteNode implements MachNode {
     @Override public Node copy() { return new CmpIX86(this); }
 
     @Override public final void encoding( Encoding enc ) {
-        short dst = enc.reg(this); // Also src1
+        short dst = enc.reg(in(1)); // Also src1
         enc.add1(x86_64_v2.rex(0, dst, 0));
         // opcode; 0x81 or 0x83; 0x69 or 0x6B
         enc.add1( 0x81 + (x86_64_v2.imm8(_imm) ? 2 : 0) );
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CmpMemX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CmpMemX86.java
index f5d1fb5..4744dc2 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CmpMemX86.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CmpMemX86.java
@@ -15,16 +15,18 @@ public class CmpMemX86 extends MemOpX86 {
     @Override public void encoding( Encoding enc ) {
         // REX.W + 81 /7 id	CMP r/m64, imm32 | REX.W + 39 /r	CMP r/m64,r64
         // CMP [mem], imm32
+
         short ptr = enc.reg(ptr());
         short idx = enc.reg(idx());
         short src = enc.reg(val());
 
         enc.add1(x86_64_v2.rex(src, ptr, idx));
         // opcode varies by immediate
-        enc.add1( src == -1 ? 0x39 : 0x81 );
+        enc.add1( src == -1 ? 0x81 : 0x39);
 
         // includes modrm
         x86_64_v2.indirectAdr(_scale, idx, ptr, _off, src == -1 ? 7 : src, enc);
+
         if( src == -1 ) enc.add4(_imm);
     }
 
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CmpX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CmpX86.java
index 3965433..b238ed2 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CmpX86.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CmpX86.java
@@ -1,15 +1,23 @@
 package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2;
 
-import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.*;
 import com.compilerprogramming.ezlang.compiler.codegen.*;
-import com.compilerprogramming.ezlang.compiler.nodes.Node;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
 
-public class CmpX86 extends RegX86 {
+public class CmpX86 extends MachConcreteNode implements MachNode {
     CmpX86( Node add ) { super(add); }
     @Override public String op() { return "cmp"; }
-    @Override int opcode() { return 0x3B; }
+    @Override public RegMask regmap(int i) { return x86_64_v2.RMASK; }
     @Override public RegMask outregmap() { return x86_64_v2.FLAGS_MASK; }
-    @Override public int twoAddress() { return 0; }
+
+    @Override public void encoding( Encoding enc ) {
+        short dst = enc.reg(in(1));
+        short src = enc.reg(in(2));
+
+        enc.add1(x86_64_v2.rex(dst, src, 0));
+        enc.add1(0x3B);
+        enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, src));
+    }
     @Override public void asm(CodeGen code, SB sb) {
         String dst = code.reg(this);
         if( dst!="flags" )  sb.p(dst).p(" = ");
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/FltX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/FltX86.java
index a351d6b..7003825 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/FltX86.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/FltX86.java
@@ -4,7 +4,7 @@
 import com.compilerprogramming.ezlang.compiler.codegen.*;
 import com.compilerprogramming.ezlang.compiler.nodes.*;
 
-public class FltX86 extends ConstantNode implements MachNode, RIPRelSize {
+public class FltX86 extends ConstantNode implements MachNode, RIPRelSize{
     private byte _encSize;
     FltX86(ConstantNode con ) { super(con); }
     @Override public String op() { return "ld8"; }
@@ -19,7 +19,6 @@ public class FltX86 extends ConstantNode implements MachNode, RIPRelSize {
         // F2 0F 10 /r MOVSD xmm1, m64
         _encSize = 1/*Fop*/+0/*REX*/+2/*op*/+1/*MODRM*/+4/*offset*/;
         short dst = (short)(enc.reg(this ) - x86_64_v2.XMM_OFFSET);
-        enc.largeConstant(this,_con);
         // Fopcode
         enc.add1(0xF2);
         // rex prefix must come next (REX.W is not set)
@@ -28,6 +27,7 @@ public class FltX86 extends ConstantNode implements MachNode, RIPRelSize {
         // hard-code rip here
         enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.INDIRECT, dst, 0x05));
         enc.add4(0);
+        enc.largeConstant(this,_con,_encSize-4,2/*ELF encoding PC32*/);
     }
 
     // Delta is from opcode start
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/JmpX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/JmpX86.java
index 426dde7..d6947bf 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/JmpX86.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/JmpX86.java
@@ -32,8 +32,6 @@ public class JmpX86 extends IfNode implements MachNode, RIPRelSize {
         int op = x86_64_v2.jumpop(_bop);
         enc.add1(op-16);      // Short form jump
         enc.add1(0);          // Offset
-        //enc.add1(0x0F).add1(x86_64_v2.jumpop(_bop));
-        //enc.add4(0);            // Offset patched later
     }
 
     // Delta is from opcode start, but X86 measures from the end of the 2-byte encoding
@@ -59,9 +57,8 @@ public class JmpX86 extends IfNode implements MachNode, RIPRelSize {
     @Override public void asm(CodeGen code, SB sb) {
         String src = code.reg(in(1));
         if( src!="flags" ) sb.p(src).p(" ");
-        CFGNode prj = cproj(0); // 0 is True is jump target
-        while( prj.nOuts() == 1 && !(prj instanceof ReturnNode) )
-            prj = prj.uctrl(); // Skip empty blocks
+        CFGNode prj = cproj(0).uctrlSkipEmpty(); // 0 is True is jump target
+        if( !prj.blockHead() ) prj = prj.cfg0();
         sb.p(label(prj));
     }
 
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/LoadX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/LoadX86.java
index 1f5dc56..0412888 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/LoadX86.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/LoadX86.java
@@ -1,12 +1,14 @@
 package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2;
 
 import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.Utils;
 import com.compilerprogramming.ezlang.compiler.codegen.*;
 import com.compilerprogramming.ezlang.compiler.nodes.LoadNode;
 import com.compilerprogramming.ezlang.compiler.nodes.Node;
 import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
 import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFloat;
 import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeInteger;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeMemPtr;
 
 public class LoadX86 extends MemOpX86 {
     LoadX86( LoadNode ld, Node base, Node idx, int off, int scale ) {
@@ -18,60 +20,36 @@ public class LoadX86 extends MemOpX86 {
         // REX.W + 8B /r	MOV r64, r/m64
         // Zero extension for u8, u16 and u32 but sign extension i8, i16, i32
         // Use movsx and movzx
-
         short dst = enc.reg(this );
         short ptr = enc.reg(ptr());
         short idx = enc.reg(idx());
+        enc(enc, _declaredType, dst, ptr, idx, _off, _scale);
+    }
 
-        if (_declaredType != SONTypeInteger.U32 && _declaredType != SONTypeFloat.F32 && _declaredType != SONTypeFloat.F64) {
+    static void enc( Encoding enc, SONType decl, short dst, short ptr, short idx, int off, int scale ) {
+        if( decl != SONTypeInteger.U32 && decl != SONTypeFloat.F32 && decl != SONTypeFloat.F64 )
             enc.add1(x86_64_v2.rex(dst, ptr, idx));
-        }
 
-        if(_declaredType == SONTypeFloat.F32) {
-            // F3 0F 10 /r MOVSS xmm1, m32
-            enc.add1(0xF3);
-            enc.add1(0x0F);
-            enc.add1(0x10);
-            dst -= (short)x86_64_v2.XMM_OFFSET;
-        }
-        if(_declaredType == SONTypeFloat.F64) {
-            //  F2 0F 10 /r MOVSD xmm1, m64
-            enc.add1(0xF2);
-            enc.add1(0x0F);
-            enc.add1(0x10);
+        if( decl.isa(SONTypeFloat.F64) )
             dst -= (short)x86_64_v2.XMM_OFFSET;
-        }
-        if(_declaredType == SONTypeInteger.I8) {
-            // sign extend: REX.W + 0F BE /r	MOVSX r64, r/m8
-            enc.add1(0x0F);
-            enc.add1(0xBE);
-        } else if(_declaredType == SONTypeInteger.I16) {
-            // sign extend: REX.W + 0F BF /r	MOVSX r64, r/m16
-            enc.add1(0x0F);
-            enc.add1(0xBF);
-        } else if(_declaredType == SONTypeInteger.I32) {
-            // sign extend: REX.W + 63 /r	MOVSXD r64, r/m32
-            enc.add1(0x63);
-        } else if(_declaredType == SONTypeInteger.U8) {
-            // zero extend: REX.W + 0F B6 /r	MOVZX r64, r/m8
-            enc.add1(0x0F);
-            enc.add1(0xB6);
-        } else if(_declaredType == SONTypeInteger.U16) {
-            // zero extend:   REX.W + 0F B7 /r	MOVZX r64, r/m16
-            enc.add1(0x0F);
-            enc.add1(0xB7);
-        } else if(_declaredType == SONTypeInteger.U32) {
-            // zero extend:   8B /r	MOV r32, r/m32
-            enc.add1(0x8B);
-        } else if(_declaredType == SONTypeInteger.BOT) {
-            // REX.W + 8B /r    MOV r64, r/m64
-            enc.add1(0x8B);
-        }
+
+        if( false ) ;
+        else if( decl == SONTypeFloat.F32  ) enc.add1(0xF3).add1(0x0F).add1(0x10); // F3 0F 10 /r MOVSS xmm1, m32
+        else if( decl == SONTypeFloat.F64  ) enc.add1(0xF2).add1(0x0F).add1(0x10); // F2 0F 10 /r MOVSD xmm1, m64
+        else if( decl == SONTypeInteger.I8 ) enc.add1(0x0F).add1(0xBE); // sign extend: REX.W + 0F BE /r MOVSX r64, r/m8
+        else if( decl == SONTypeInteger.I16) enc.add1(0x0F).add1(0xBF); // sign extend: REX.W + 0F BF /r MOVSX r64, r/m16
+        else if( decl == SONTypeInteger.I32) enc.add1(0x63);            // sign extend: REX.W + 63 /r    MOVSXD r64, r/m32
+        else if( decl == SONTypeInteger.U8 ) enc.add1(0x0F).add1(0xB6); // zero extend: REX.W + 0F B6 /r MOVZX r64, r/m8
+        else if( decl == SONTypeInteger.U16) enc.add1(0x0F).add1(0xB7); // zero extend: REX.W + 0F B7 /r MOVZX r64, r/m16
+        // Covers U32, I64/BOT, TMP
+        else if( decl.log_size()>=2 )     enc.add1(0x8B);            // zero extend:         8B /r    MOV r32, r/m32
+        else throw Utils.TODO();
 
         // includes modrm internally
-        x86_64_v2.indirectAdr(_scale, idx, ptr, _off, dst, enc);
+        x86_64_v2.indirectAdr(scale, idx, ptr, off, dst, enc);
     }
 
+
     // General form: "ldN  dst,[base + idx<<2 + 12]"
     @Override public void asm(CodeGen code, SB sb) {
         sb.p(code.reg(this)).p(",");
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/MemOpX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/MemOpX86.java
index f511e97..dde6bca 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/MemOpX86.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/MemOpX86.java
@@ -14,7 +14,7 @@
 //   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
+//   val/null - Value to store, as part of an 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
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/RegX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/RegX86.java
index fc49513..8a5e305 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/RegX86.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/RegX86.java
@@ -12,8 +12,9 @@ public abstract class RegX86 extends MachConcreteNode {
     abstract int opcode();
     @Override public void encoding( Encoding enc ) {
         // REX.W + 01 /r
-        short dst = enc.reg(this ); // src1
+        short dst = enc.reg(in(1)); // src1
         short src = enc.reg(in(2)); // src2
+
         enc.add1(x86_64_v2.rex(dst, src, 0));
         enc.add1(opcode()); // opcode
         enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, src));
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/SarIX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/SarIX86.java
index ac1a420..656e84a 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/SarIX86.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/SarIX86.java
@@ -1,12 +1,39 @@
 package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2;
 
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.codegen.CodeGen;
+import com.compilerprogramming.ezlang.compiler.codegen.Encoding;
+import com.compilerprogramming.ezlang.compiler.codegen.RegMask;
+import com.compilerprogramming.ezlang.compiler.nodes.MachNode;
 import com.compilerprogramming.ezlang.compiler.nodes.Node;
 
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
 // Arithmetic Right Shift
-public class SarIX86 extends ImmX86 {
-    SarIX86( Node add, int imm ) { super(add,imm); assert x86_64_v2.imm8(imm); }
+public class SarIX86 extends MachConcreteNode implements MachNode {
+    final int _imm;
+    SarIX86( Node sari, int imm ) { super(sari); assert x86_64_v2.imm8(imm); _inputs.pop(); _imm = imm; }
     @Override public String op() { return "sari"; }
     @Override public String glabel() { return ">>"; }
-    @Override int opcode() { return 0xC1; }
-    @Override int mod() { return 7; }
+    int opcode() { return 0xC1; }
+    int mod() { return 7; }
+
+    @Override public RegMask regmap(int i) { return x86_64_v2.RMASK; }
+    @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
+
+    @Override public void encoding(Encoding enc) {
+        short dst = enc.reg(this); // Also src1
+        enc.add1(x86_64_v2.rex(0, dst, 0));
+        enc.add1( opcode());
+
+        enc.add1( x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, mod(), dst) );
+
+        // immediate(4 bytes) 32 bits or (1 byte)8 bits
+        if( x86_64_v2.imm8(_imm) ) enc.add1(_imm);
+        else                       enc.add4(_imm);
+    }
+
+    @Override public void asm(CodeGen code, SB sb) {
+        sb.p(code.reg(this)).p(" ").p(glabel()).p("= #").p(_imm);
+    }
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/SetX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/SetX86.java
index 7ee2305..df76f45 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/SetX86.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/SetX86.java
@@ -8,13 +8,15 @@
 // Use result of comparison without jump.
 public class SetX86 extends MachConcreteNode implements MachNode {
     final String _bop;          // One of <,<=,==
+    final boolean _unsigned;
     // Constructor expects input is an X86 and not an Ideal node.
-    SetX86( Node cmp, String bop ) {
+    SetX86( Node cmp, String bop, boolean unsigned ) {
         super(cmp);
         _inputs.setLen(1);   // Pop the cmp inputs
         // Replace with the matched cmp
         _inputs.push(cmp);
         _bop = bop;
+        _unsigned = unsigned;
     }
     @Override public String op() { return "set"+_bop; }
     @Override public RegMask regmap(int i) { assert i==1; return x86_64_v2.FLAGS_MASK; }
@@ -29,8 +31,8 @@ public class SetX86 extends MachConcreteNode implements MachNode {
         enc.add1(0x0F);         // opcode
         enc.add1(switch (_bop) {
             case "==" -> 0x94;  // SETE
-            case "<"  -> 0x9C;  // SETL
-            case "<=" -> 0X9E;  // SETLE
+            case "<"  -> _unsigned ? 0x92 : 0x9C;  // SETB /SETL
+            case "<=" -> _unsigned ? 0x96 : 0x9E;  // SETBE/SETLE
             default -> throw Utils.TODO();
             });
         enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, 0, dst));
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/ShlIX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/ShlIX86.java
index ee8dfb6..319a303 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/ShlIX86.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/ShlIX86.java
@@ -2,10 +2,38 @@
 
 import com.compilerprogramming.ezlang.compiler.nodes.Node;
 
-public class ShlIX86 extends ImmX86 {
-    ShlIX86( Node add, int imm ) { super(add,imm); }
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+public class ShlIX86 extends MachConcreteNode implements MachNode {
+    final int _imm;
+    ShlIX86( Node shli, int imm ) {
+        super(shli);
+        assert x86_64_v2.imm8(imm);
+        _inputs.pop();          // Pop constant input off
+        _imm = imm;
+    }
+    public int opcode() { return 0xC1; }
+    public int mod() { return 4; }
+
+    @Override public RegMask regmap(int i) { return x86_64_v2.RMASK; }
+    @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
+
     @Override public String op() { return "shli"; }
     @Override public String glabel() { return "<<"; }
-    @Override int opcode() { return 0xC1; }
-    @Override int mod() { return 4; }
+    @Override public void encoding(Encoding enc) {
+        short dst = enc.reg(this); // Also src1
+        enc.add1(x86_64_v2.rex(0, dst, 0));
+        enc.add1( opcode());
+
+        enc.add1( x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, mod(), dst) );
+
+        // immediate(4 bytes) 32 bits or (1 byte)8 bits
+        if( x86_64_v2.imm8(_imm) ) enc.add1(_imm);
+        else                       enc.add4(_imm);
+    }
+    @Override public void asm(CodeGen code, SB sb) {
+        sb.p(code.reg(this)).p(" ").p(glabel()).p("= #").p(_imm);
+    }
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/ShrIX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/ShrIX86.java
index de39108..dd0f9cb 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/ShrIX86.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/ShrIX86.java
@@ -2,10 +2,33 @@
 
 import com.compilerprogramming.ezlang.compiler.nodes.Node;
 
-public class ShrIX86 extends ImmX86 {
-    ShrIX86( Node add, int imm ) { super(add,imm); }
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+public class ShrIX86 extends MachConcreteNode implements MachNode {
+    final int _imm;
+    ShrIX86( Node shri, int imm ) { super(shri); assert x86_64_v2.imm8(imm); _inputs.pop(); _imm = imm; }
     @Override public String op() { return "shri"; }
     @Override public String glabel() { return ">>>"; }
-    @Override int opcode() { return 0xC1; }
-    @Override int mod() { return 5; }
+    int opcode() { return 0xC1; }
+    int mod() { return 5; }
+
+    @Override public RegMask regmap(int i) { return x86_64_v2.RMASK; }
+    @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
+
+    @Override public void encoding(Encoding enc) {
+        short dst = enc.reg(this); // Also src1
+        enc.add1(x86_64_v2.rex(0, dst, 0));
+        enc.add1( opcode());
+
+        enc.add1( x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, mod(), dst) );
+
+        // immediate(4 bytes) 32 bits or (1 byte)8 bits
+        if( x86_64_v2.imm8(_imm) ) enc.add1(_imm);
+        else                       enc.add4(_imm);
+    }
+    @Override public void asm(CodeGen code, SB sb) {
+        sb.p(code.reg(this)).p(" ").p(glabel()).p("= #").p(_imm);
+    }
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/SplitX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/SplitX86.java
index 49129a2..fefefc3 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/SplitX86.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/SplitX86.java
@@ -5,6 +5,9 @@
 import com.compilerprogramming.ezlang.compiler.codegen.*;
 import com.compilerprogramming.ezlang.compiler.nodes.Node;
 import com.compilerprogramming.ezlang.compiler.nodes.SplitNode;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFloat;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeInteger;
 
 public class SplitX86 extends SplitNode {
     SplitX86( String kind, byte round ) { super(kind,round, new Node[2]); }
@@ -18,12 +21,12 @@ public class SplitX86 extends SplitNode {
         // REX.W + 8B /r	MOV r64, r/m64
         short dst = enc.reg(this );
         short src = enc.reg(in(1));
-
         if( dst == x86_64_v2.FLAGS ) {
             // mov reg, flags
             // push rcx
             // popf (Pop the top of the stack into the FLAGS register)
             // 50+rd	PUSH r64
+            if( src >= 8 ) x86_64_v2.rex(0,src,0,true);
             enc.add1(0x50 + src);
             // popf
             enc.add1(0x9D);
@@ -34,6 +37,7 @@ public class SplitX86 extends SplitNode {
             // pushf; pop reg
             enc.add1(0x9C);
             // 58+ rd	POP r64
+            if( dst >= 8 ) x86_64_v2.rex(0,dst,0,true);
             enc.add1(0x58 + dst);
             return;
         }
@@ -46,20 +50,13 @@ public class SplitX86 extends SplitNode {
         if( dst >= x86_64_v2.MAX_REG ) {
             if( src >= x86_64_v2.MAX_REG )
                 throw Utils.TODO(); // Very rare stack-stack move
-            // TODO: Missing FP 0x66 prefix
-            if( srcX ) { src -= (short)x86_64_v2.XMM_OFFSET;  enc.add1(0x66); }
             int off = enc._fun.computeStackSlot(dst - x86_64_v2.MAX_REG)*8;
-            enc.add1(x86_64_v2.rex(src, x86_64_v2.RSP, -1));
-            enc.add1( 0x89 );
-            x86_64_v2.indirectAdr(0, (short)-1, (short)x86_64_v2.RSP, off, src, enc);
+            StoreX86.encVal(enc, srcX ? SONTypeFloat.F64 : SONTypeInteger.BOT, (short)x86_64_v2.RSP, (short)-1/*index*/, src, off, 0);
             return;
         }
         if( src >= x86_64_v2.MAX_REG ) {
-            if( dstX ) { dst -= (short)x86_64_v2.XMM_OFFSET;  enc.add1(0x66); }
             int off = enc._fun.computeStackSlot(src - x86_64_v2.MAX_REG)*8;
-            enc.add1(x86_64_v2.rex(dst, x86_64_v2.RSP, -1));
-            enc.add1( 0x8B );
-            x86_64_v2.indirectAdr(0, (short)-1, (short)x86_64_v2.RSP, off, dst, enc);
+            LoadX86.enc(enc, dstX ? SONTypeFloat.F64 : SONTypeInteger.BOT, dst, (short)x86_64_v2.RSP, (short)-1, off, 0);
             return;
         }
 
@@ -80,13 +77,14 @@ public class SplitX86 extends SplitNode {
             enc.add1(0x0F);
             enc.add1(0x28);
         } else if( dstX && !srcX ) {
-            // xmm->reg (66 REX.W 0F 6E /r MOVQ xmm, r/m64)
+            // reg->xmm (66 REX.W 0F 6E /r MOVQ xmm, r/m64)
             enc.add1(0x0F);
             enc.add1(0x6E);
         } else if( !dstX && srcX ) {
-            // reg->xmm(66 REX.W 0F 7E /r MOVQ r/m64, xmm)
+            // xmm->reg(66 0F 7E /r MOVQ r/m64, xmm)
             enc.add1(0x0F);
             enc.add1(0x7E);
+            short tmp=src; src=dst; dst=tmp;
         }
 
         enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, src));
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/StoreX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/StoreX86.java
index 501121f..5728951 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/StoreX86.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/StoreX86.java
@@ -5,8 +5,8 @@
 import com.compilerprogramming.ezlang.compiler.nodes.ConstantNode;
 import com.compilerprogramming.ezlang.compiler.nodes.Node;
 import com.compilerprogramming.ezlang.compiler.nodes.StoreNode;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
 import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFloat;
-
 import java.util.BitSet;
 
 public class StoreX86 extends MemOpX86 {
@@ -30,30 +30,38 @@ public class StoreX86 extends MemOpX86 {
         short idx = enc.reg(idx());
         short src = enc.reg(val());
 
-        int imm_op = x86_64_v2.selectOpcodeForImmStore(_imm);
-        if(src == -1 && _imm != 0) {
-            if(imm_op == -1)         {enc.add1(x86_64_v2.rex(src, ptr, idx));enc.add1(0xC7); }
-            else enc.add1(imm_op);
-        } else {
-            if(_declaredType == SONTypeFloat.F32) {src -= (short)x86_64_v2.XMM_OFFSET; enc.add1(0xF3); enc.add1(0x0F); enc.add1(0x11);}
-            else if(_declaredType == SONTypeFloat.F64) {src -= (short)x86_64_v2.XMM_OFFSET; enc.add1(0xF2); enc.add1(0x0F); enc.add1(0x11);}
-            else if(_declaredType.log_size() == 0) enc.add1(0x88);
-            else if(_declaredType.log_size() == 1) enc.add1(0x89);
-            else if(_declaredType.log_size() == 2) enc.add1(0x89);
-            else if(_declaredType.log_size() == 3) {enc.add1(x86_64_v2.rex(src, ptr, idx)); enc.add1(0x89);}
-        }
-
-        x86_64_v2.indirectAdr(_scale, idx, ptr, _off, src, enc);
         if( src == -1 ) {
+            // return opcode for optimised immediate store
+            if( x86_64_v2.imm8(_imm) ) enc.add1(0xC6);
+            else if( x86_64_v2.imm32(_imm) ) enc.add1(0xC7);
+            else enc.add1(x86_64_v2.rex(-1, ptr, idx)).add1(0xC7);
+            x86_64_v2.indirectAdr(_scale, idx, ptr, _off, src, enc);
             switch (x86_64_v2.imm_size(_imm)) {
-                case 8: enc.add1(_imm); break;
-                case 16: enc.add2(_imm); break;
-                case 32: enc.add4(_imm); break;
-                case 64: enc.add8(_imm); break;
+            case  8: enc.add1(_imm); break;
+            case 16: enc.add2(_imm); break;
+            case 32: enc.add4(_imm); break;
+            case 64: enc.add8(_imm); break;
             }
+        } else {
+            encVal(enc,_declaredType,ptr,idx,src,_off,_scale);
         }
     }
 
+    // Non-immediate encoding
+    static void encVal( Encoding enc, SONType decl, short ptr, short idx, short src, int off, int scale ) {
+        if( decl instanceof SONTypeFloat ) {
+            src -= (short)x86_64_v2.XMM_OFFSET;
+            enc.add1( decl==SONTypeFloat.F32 ? 0xF3 : 0xF2 ).add1(0x0F).add1(0x11);
+        }
+        else if( decl.log_size() == 0 ) enc.add1(0x88);
+        else if( decl.log_size() == 1 ) enc.add1(0x89);
+        else if( decl.log_size() == 2 ) enc.add1(0x89);
+        else if( decl.log_size() == 3 ) enc.add1(x86_64_v2.rex(src, ptr, idx)).add1(0x89);
+
+        x86_64_v2.indirectAdr(scale, idx, ptr, off, src, enc);
+    }
+
+
     // General form: "stN  [base + idx<<2 + 12],val"
     @Override public void asm(CodeGen code, SB sb) {
         asm_address(code,sb).p(",");
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/x86_64_v2.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/x86_64_v2.java
index a7691d7..61035af 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/x86_64_v2.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/x86_64_v2.java
@@ -85,13 +85,18 @@ static public int jumpop(String op) {
     }
 
     public static int modrm(MOD mod, int reg, int m_r) {
+        if( reg == -1 ) reg=0;  // Missing reg in this flavor
         // combine all the bits
+        assert 0 <= reg  &&  reg < 16;
+        assert 0 <= m_r  &&  m_r < 16;
         return (mod.ordinal() << 6) | ((reg & 0x07) << 3) | m_r & 0x07;
     }
 
     // 00 000 000
     // same bit-layout as modrm
     public static int sib(int scale, int index, int base) {
+        assert 0 <= base  &&  base < 16;
+        assert 0 <= index && index < 16;
         return (scale << 6) | ((index & 0x07) << 3) | base & 0x07;
     }
 
@@ -103,19 +108,17 @@ public static int sib(int scale, int index, int base) {
     // 0 denotes no direct register
     public static int rex(int reg, int ptr, int idx, boolean wide) {
         // assuming 64 bit by default so: 0100 1000
+        assert -1 <= reg && reg < 16;
+        assert -1 <= ptr && ptr < 16;
+        assert -1 <= idx && idx < 16;
+
         int rex = wide ? REX_W : REX;
-        if( 8 <= reg && reg <= 15 ) rex |= 0b00000100; // REX.R
-        if( 8 <= ptr && ptr <= 15 ) rex |= 0b00000001; // REX.B
-        if( 8 <= idx && idx <= 15 ) rex |= 0b00000010; // REX.X
+        if( 8 <= reg ) rex |= 0b00000100; // REX.R
+        if( 8 <= ptr ) rex |= 0b00000001; // REX.B
+        if( 8 <= idx ) rex |= 0b00000010; // REX.X
         return rex;
     }
-    // return opcode for optimised immediate store
-    public static int selectOpcodeForImmStore(long imm) {
-        if(imm8(imm)) return 0xC6;
-        if(imm16(imm)) return 0xC7;
-        if(imm32(imm)) return 0xC7;
-        return -1;
-    }
+
     // return the size of the immediate
     public static int imm_size(long imm) {
         if(imm8(imm)) return 8;
@@ -143,26 +146,25 @@ public static byte rexF(int reg, int ptr, int idx, boolean wide, Encoding enc) {
     public static void indirectAdr( int scale, short index, short base, int offset, int reg, Encoding enc ) {
         // Assume indirect
         assert 0 <= base && base < 16;
-        assert index != RSP;
+        assert -1 <= index && index < 16 && index != RSP;
 
         MOD mod = MOD.INDIRECT;
         // is 1 byte enough or need more?
         if( offset != 0 )
             mod = imm8(offset)
-                ? MOD.INDIRECT_disp8
-                : MOD.INDIRECT_disp32;
+                    ? MOD.INDIRECT_disp8
+                    : MOD.INDIRECT_disp32;
 
-        // needs to pick optimal displacement mod if we want to encode base
-        if( mod == MOD.INDIRECT && (base == RBP || base == R13) )
+        if( mod == MOD.INDIRECT && (base == RBP || base == R13) ) {
             mod = MOD.INDIRECT_disp8;
+        } else if( index == -1 && (base == RSP || base == R12) ) {
+            index = RSP;
+        }
 
-        // special encoding for [base +offset]
         if( index == -1 ) {
-            // Case for mov reg, [disp] (load)
-            enc.add1(modrm(mod, reg == -1 ? 0 : reg, base));
+            enc.add1(modrm(mod, reg, base));
         } else {
-            // rsp is hard-coded here(0x04)
-            enc.add1(modrm(mod, reg, 0x04));
+            enc.add1(modrm(mod, reg, x86_64_v2.RSP));
             enc.add1(sib(scale, index, base));
         }
 
@@ -372,8 +374,7 @@ static RegMask retMask( SONTypeFunPtr tfp, int i ) {
     }
 
     // Break an infinite loop
-    @Override
-    public IfNode never(CFGNode ctrl) {
+    @Override public NeverNode never(CFGNode ctrl) {
         throw Utils.TODO();
     }
 
@@ -381,45 +382,45 @@ public IfNode never(CFGNode ctrl) {
     @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 CastNode cast -> new CastX86(cast);
-            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 NotNode not -> new NotX86(not);
-            case OrNode or -> or(or);
-            case ParmNode parm -> new ParmX86(parm);
-            case PhiNode phi -> new PhiNode(phi);
-            case ProjNode prj -> prj(prj);
-            case ReadOnlyNode read -> new ReadOnlyNode(read);
-            case ReturnNode ret -> new RetX86(ret, ret.fun());
-            case SarNode sar -> sar(sar);
-            case ShlNode shl -> shl(shl);
-            case ShrNode shr -> shr(shr);
-            case StartNode start -> new StartNode(start);
-            case StopNode stop -> new StopNode(stop);
-            case StoreNode st -> st(st);
-            case SubFNode subf -> new SubFX86(subf);
-            case SubNode sub -> sub(sub);
-            case ToFloatNode tfn -> i2f8(tfn);
-            case XorNode xor -> xor(xor);
-
-            case LoopNode loop -> new LoopNode(loop);
-            case RegionNode region -> new RegionNode(region);
-            default -> throw Utils.TODO();
+        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 CastNode cast -> new CastX86(cast);
+        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 NotNode not -> new NotX86(not);
+        case OrNode or -> or(or);
+        case ParmNode parm -> new ParmX86(parm);
+        case PhiNode phi -> new PhiNode(phi);
+        case ProjNode prj -> prj(prj);
+        case ReadOnlyNode read -> new ReadOnlyNode(read);
+        case ReturnNode ret -> new RetX86(ret, ret.fun());
+        case SarNode sar -> sar(sar);
+        case ShlNode shl -> shl(shl);
+        case ShrNode shr -> shr(shr);
+        case StartNode start -> new StartNode(start);
+        case StopNode stop -> new StopNode(stop);
+        case StoreNode st -> st(st);
+        case SubFNode subf -> new SubFX86(subf);
+        case SubNode sub -> sub(sub);
+        case ToFloatNode tfn -> i2f8(tfn);
+        case XorNode xor -> xor(xor);
+
+        case LoopNode loop -> new LoopNode(loop);
+        case RegionNode region -> new RegionNode(region);
+        default -> throw Utils.TODO();
         };
     }
 
@@ -501,17 +502,19 @@ private Node call(CallNode 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 static boolean invert;
+    private static boolean swap, unsigned;
     private Node cmp( BoolNode bool ) {
-        invert = false;
+        swap = unsigned = false;
         Node cmp = _cmp(bool);
-        return new SetX86(cmp, invert ? IfNode.invert(bool.op()) : bool.op());
+        return new SetX86(cmp, swap ? IfNode.swap(bool.op()) : bool.op(), unsigned);
     }
 
     private Node _cmp(BoolNode bool) {
         // Float variant
-        if( bool.isFloat() )
+        if( bool.isFloat() ) {
+            unsigned = true;
             return new CmpFX86(bool);
+        }
         Node lhs = bool.in(1);
         Node rhs = bool.in(2);
 
@@ -520,14 +523,14 @@ private Node _cmp(BoolNode bool) {
             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 new CmpMemX86(bool, address(ld), ld.ptr(), idx, off, scale, imm(lhs), val, true );
 
         // Vs immediate
         if( rhs instanceof ConstantNode con && con._con instanceof SONTypeInteger ti && imm32(ti.value()) )
             return new CmpIX86(bool, (int)ti.value());
 
         if( lhs instanceof ConstantNode con && con._con instanceof SONTypeInteger ti && imm32(ti.value()) ) {
-            invert = true;
+            swap = true;
             return new CmpIX86(bool, (int)ti.value(), 0.5);
         }
 
@@ -559,7 +562,7 @@ private Node jmp(IfNode iff) {
         // Loads do not set the flags, and will need an explicit TEST
         BoolNode bool;
         if( iff.in(1) instanceof BoolNode bool0 ) bool = bool0;
-        else iff.setDef(1, bool=new BoolNode.EQ(iff.in(1), new ConstantNode(SONTypeInteger.ZERO)));
+        else iff.setDef(1, bool=new BoolNode.NE(iff.in(1), new ConstantNode(SONTypeInteger.ZERO)));
         return new JmpX86(iff, bool.op());
     }
 
@@ -584,20 +587,20 @@ private Node prj( ProjNode prj ) {
     }
 
     private Node sar(SarNode sar) {
-        if( sar.in(2) instanceof ConstantNode con && con._con instanceof SONTypeInteger ti )
-            return new SarIX86(sar, (int)(ti.value() & 0x03f) );
+        if( sar.in(2) instanceof ConstantNode con && con._con instanceof SONTypeInteger ti && imm8(ti.value()))
+            return new SarIX86(sar, (int)(ti.value() & 0xff) );
         return new SarX86(sar);
     }
 
     private Node shl(ShlNode shl) {
-        if( shl.in(2) instanceof ConstantNode con && con._con instanceof SONTypeInteger ti )
-            return new ShlIX86(shl, (int)(ti.value() & 0x03f) );
+        if( shl.in(2) instanceof ConstantNode con && con._con instanceof SONTypeInteger ti && imm8(ti.value()))
+            return new ShlIX86(shl, (int)(ti.value() & 0xff) );
         return new ShlX86(shl);
     }
 
     private Node shr( ShrNode shr ) {
-        if( shr.in(2) instanceof ConstantNode con && con._con instanceof SONTypeInteger ti )
-            return new ShrIX86(shr, (int)(ti.value() & 0x03f) );
+        if( shr.in(2) instanceof ConstantNode con && con._con instanceof SONTypeInteger ti && imm8(ti.value()) )
+            return new ShrIX86(shr, (int)(ti.value() & 0xff) );
         return new ShrX86(shr);
     }
 
@@ -610,9 +613,7 @@ private Node st (StoreNode st ){
                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 MemAddX86(address(st), st.ptr(), idx, off, scale, imm(op.in(2)), val);
             }
         }
 
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/print/ASMPrinter.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/print/ASMPrinter.java
index 3c6df73..646c7e0 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/print/ASMPrinter.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/print/ASMPrinter.java
@@ -4,10 +4,7 @@
 import com.compilerprogramming.ezlang.compiler.SB;
 import com.compilerprogramming.ezlang.compiler.codegen.Encoding;
 import com.compilerprogramming.ezlang.compiler.nodes.*;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFunPtr;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeMem;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeRPC;
+import com.compilerprogramming.ezlang.compiler.sontypes.*;
 
 public abstract class ASMPrinter {
 
@@ -29,20 +26,34 @@ public static SB print(SB sb, CodeGen code) {
         Encoding enc = code._encoding;
         if(  enc!=null && !enc._bigCons.isEmpty() ) {
             sb.p("--- Constant Pool ------").nl();
-            for( Node relo : enc._bigCons.keySet() ) {
-                SONType t = enc._bigCons.get(relo);
-                if( t.log_size()==3 ) {
-                    sb.hex2(iadr).p("  ").hex8(enc.read8(iadr)).p(" ");
-                    t.print(sb).nl();
-                    iadr += 8;
-                }
-            }
-            for( Node relo : enc._bigCons.keySet() ) {
-                SONType t = enc._bigCons.get(relo);
-                if( t.log_size()==2 ) {
-                    sb.hex2(iadr).p("  ").hex4(enc.read4(iadr)).fix(9,"");
-                    t.print(sb).nl();
-                    iadr += 4;
+            // By log size
+            for( int log = 3; log >= 0; log-- ) {
+                for( Node op : enc._bigCons.keySet() ) {
+                    Encoding.Relo relo = enc._bigCons.get(op);
+                    if( relo._t.log_size()==log ) {
+                        sb.hex2(iadr).p("  ");
+                        if( relo._t instanceof SONTypeTuple tt ) {
+                            for( SONType tx : tt._types ) {
+                                switch( log ) {
+                                case 0: sb.hex1(enc.read1(iadr)); break;
+                                case 1: sb.hex2(enc.read2(iadr)); break;
+                                case 2: sb.hex4(enc.read4(iadr)); break;
+                                case 3: sb.hex8(enc.read8(iadr)); break;
+                                }
+                                iadr += (1<<log);
+                                sb.p(" ");
+                            }
+                        } else {
+                            switch( log ) {
+                            case 0: sb.hex1(enc.read1(iadr)).fix(9-1,""); break;
+                            case 1: sb.hex2(enc.read2(iadr)).fix(9-2,""); break;
+                            case 2: sb.hex4(enc.read4(iadr)).fix(9-4,""); break;
+                            case 3: sb.hex8(enc.read8(iadr)).p(" "); break;
+                            }
+                            iadr += (1<<log);
+                        }
+                        relo._t.print(sb).nl();
+                    }
                 }
             }
         }
@@ -73,7 +84,7 @@ private static int print(int iadr, SB sb, CodeGen code, FunNode fun, int cfgidx)
     static private final int opWidth = 5;
     static private final int argWidth = 30;
     static int doBlock(int iadr, SB sb, CodeGen code, FunNode fun, int cfgidx) {
-        final int encWidth = code._mach.defaultOpSize()*2;
+        final int encWidth = code._mach==null ? 2 : code._mach.defaultOpSize()*2;
         CFGNode bb = code._cfg.at(cfgidx);
         if( bb != fun && !(bb instanceof IfNode) && !(bb instanceof CallEndNode) && !(bb instanceof CallNode)  && !(bb instanceof CProjNode && bb.in(0) instanceof CallEndNode ))
             sb.p(label(bb)).p(":").nl();
@@ -112,11 +123,12 @@ static int doBlock(int iadr, SB sb, CodeGen code, FunNode fun, int cfgidx) {
     }
 
     static int doInst( int iadr, SB sb, CodeGen code, int cfgidx, Node n, boolean postAlloc, boolean postEncode ) {
-        if( n instanceof CProjNode ) return iadr;
+        if( n==null || n instanceof CProjNode ) return iadr;
         if( postAlloc && n instanceof CalleeSaveNode ) return iadr;
         if( postEncode && n instanceof ProjNode ) return iadr;
         if( n instanceof MemMergeNode ) return iadr;
-        final int dopz = code._mach.defaultOpSize();
+        if( n.getClass() == ConstantNode.class ) return iadr; // Default placeholders
+        final int dopz = code._mach==null ? 2 : code._mach.defaultOpSize();
         final int encWidth = dopz*2;
 
         // All blocks ending in a Region will need to either fall into or jump
@@ -151,8 +163,14 @@ static int doInst( int iadr, SB sb, CodeGen code, int cfgidx, Node n, boolean po
         int fatEncoding = 0;
         if( code._encoding != null ) {
             int size = code._encoding._opLen[n._nid];
-            for( int i=0; i<Math.min(size,encWidth>>1); i++ )
-                sb.hex1(code._encoding._bits.buf()[iadr++]);
+            if( code._asmLittle )
+                for( int i=0; i<Math.min(size,dopz); i++ )
+                    sb.hex1(code._encoding._bits.buf()[iadr++]);
+            else {
+                iadr += Math.min(size,dopz);
+                for( int i=0; i<Math.min(size,dopz); i++ )
+                    sb.hex1(code._encoding._bits.buf()[iadr-i-1]);
+            }
             for( int i=size*2; i<encWidth; i++ )
                 sb.p(" ");
             fatEncoding = size - (encWidth>>1); // Not-printed parts of encoding
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/print/IRPrinter.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/print/IRPrinter.java
index a69581d..c3d38e1 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/print/IRPrinter.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/print/IRPrinter.java
@@ -15,6 +15,7 @@ public abstract class IRPrinter {
     // NNID NNAME DDEF DDEF  [[  UUSE UUSE  ]]  TYPE
     // 1234 sssss 1234 1234 1234 1234 1234 1234 tttttt
     public static SB printLine( Node n, SB sb ) {
+        if( n==null ) return sb;
         sb.p("%4d %-7.7s ".formatted(n._nid,n.label()));
         if( n._inputs==null )
             return sb.p("DEAD\n");
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONType.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONType.java
index ba7eaac..86a8f70 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONType.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONType.java
@@ -103,6 +103,7 @@ public static SONType[] gather() {
     // checks with every type compare, only during interning.
 
     // Factory method which interns "this"
+    @SuppressWarnings("unchecked")
     public  <T extends SONType> T intern() {
         T nnn = (T)INTERN.get(this);
         if( nnn==null )
@@ -217,7 +218,7 @@ public final SONType join(SONType t) {
     // Sizes are expected to be between 1 and 64 bits.
     // Size 0 means this either takes no space (such as a known-zero field)
     // or isn't a scalar to be stored in memory.
-    public int log_size() { throw Utils.TODO(); }
+    public int log_size() { return 3; }
 
     // ----------------------------------------------------------
     // Useful in the debugger, which calls toString everywhere.
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeTuple.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeTuple.java
index f3acceb..ecb14ef 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeTuple.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeTuple.java
@@ -55,6 +55,21 @@ public SONType glb() {
         return make(ts);
     }
 
+    @Override public boolean isConstant() {
+        for( SONType t : _types )
+            if( !t.isConstant() )
+                return false;
+        return true;
+    }
+
+    @Override public int log_size() {
+        assert isConstant();
+        int log_size = 0;
+        for( SONType t : _types )
+            log_size = Math.max(log_size,t.log_size());
+        return log_size;
+    }
+
     public SONType ret() { assert _types.length==3; return _types[2]; }
 
     @Override public String str() { return print(new SB()).toString(); }
diff --git a/seaofnodes/src/test/java/com/compilerprogramming/ezlang/compiler/Simple.java b/seaofnodes/src/test/java/com/compilerprogramming/ezlang/compiler/Simple.java
index c649fbd..2fa8e9d 100644
--- a/seaofnodes/src/test/java/com/compilerprogramming/ezlang/compiler/Simple.java
+++ b/seaofnodes/src/test/java/com/compilerprogramming/ezlang/compiler/Simple.java
@@ -251,7 +251,7 @@ public static void main(String[] args) throws Exception {
         dump(code, dump, DUMP_AFTER_TYPE_CHECK);
 
         if (do_codegen) {
-            code.instSelect(PORTS, cpu, abi);
+            code.instSelect(cpu, abi);
             dump(code, dump, DUMP_AFTER_INSTR_SELECT);
         }
 
diff --git a/seaofnodes/src/test/java/com/compilerprogramming/ezlang/compiler/TestSONTypes.java b/seaofnodes/src/test/java/com/compilerprogramming/ezlang/compiler/TestSONTypes.java
index 78c7e87..7acfabd 100644
--- a/seaofnodes/src/test/java/com/compilerprogramming/ezlang/compiler/TestSONTypes.java
+++ b/seaofnodes/src/test/java/com/compilerprogramming/ezlang/compiler/TestSONTypes.java
@@ -196,7 +196,7 @@ func foo()->Int {
 
         static void testCPU( String src, String cpu, String os, int spills, String stop ) {
         CodeGen code = new CodeGen(src);
-        code.parse().opto().typeCheck().instSelect(PORTS,cpu,os).GCM().localSched().regAlloc().encode();
+        code.parse().opto().typeCheck().instSelect(cpu,os).GCM().localSched().regAlloc().encode();
         int delta = spills>>3;
         if( delta==0 ) delta = 1;
         assertEquals("Expect spills:",spills,code._regAlloc._spillScaled,delta);

From 5e713266155f78823ebbc067a99a618b8d4a0494 Mon Sep 17 00:00:00 2001
From: Dibyendu Majumdar <dibyendumajumdar@users.noreply.github.com>
Date: Sun, 13 Apr 2025 14:51:28 +0100
Subject: [PATCH 055/119] Update README.md

---
 README.md | 17 +++++++++--------
 1 file changed, 9 insertions(+), 8 deletions(-)

diff --git a/README.md b/README.md
index 058345e..086bf81 100644
--- a/README.md
+++ b/README.md
@@ -13,22 +13,23 @@ The language has the following features:
 * If and While statements
 * Functions
 
-The language syntax is described [ANTLR Grammar](antlr-parser/src/main/antlr4/com/compilerprogramming/ezlang/antlr/EZLanguage.g4).
+The EeZee language specifiaction is [available](https://compilerprogramming.github.io/ez-lang.html).
 The language is intentionally very simple and is meant to have just enough functionality to experiment with compiler implementation techniques.
 
 ## Modules
 
 The project is under development and subject to change. At this point in time, we have following initial implementations:
 
-* [lexer](./lexer/README.md) - a simple tokenizer
-* [parser](./parser/README.md) - a recursive descent parser and AST
-* [types](/types/README.md) - the type definitions
-* [semantic](./semantic/README.md) - semantic analyzer
-* [stackvm](./stackvm/README.md) - a compiler that generates IR for a stack based virtual machine
-* [registervm](./registervm/README.md) - a compiler that generates a so called three-address IR and an interpreter that can execute the IR
+* [lexer](./lexer/README.md) - a simple tokenizer.
+* [parser](./parser/README.md) - a recursive descent parser and AST.
+* [types](/types/README.md) - the type definitions.
+* [semantic](./semantic/README.md) - semantic analyzer.
+* [stackvm](./stackvm/README.md) - a compiler that generates IR for a stack based virtual machine.
+* [registervm](./registervm/README.md) - a compiler that generates a so called three-address IR and an interpreter that can execute the IR.
 * [optvm](./optvm/README.md) - WIP optimizing compiler with SSA transformation, constant propagation, graph coloring register allocation 
-  targeting an abstract machine.
+  targeting an abstract machine. Includes Interpreter to run the abstract machine.
 * [seaofnodes](./seaofnodes/README.md) - WIP compiler that generates Sea of Nodes IR, using SoN backend from [Simple](https://github.com/SeaOfNodes/Simple).
+  Will generate native code.
 
 ## How can you contribute?
 

From b7218870811a47a0b60061a9317c96a49839cea4 Mon Sep 17 00:00:00 2001
From: Dibyendu Majumdar <dibyendumajumdar@users.noreply.github.com>
Date: Sun, 13 Apr 2025 14:53:08 +0100
Subject: [PATCH 056/119] Update README.md

---
 optvm/README.md | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/optvm/README.md b/optvm/README.md
index 60c6eed..0b37839 100644
--- a/optvm/README.md
+++ b/optvm/README.md
@@ -9,6 +9,7 @@ This module implements various compiler optimization techniques such as:
 Our goal here is to perform optimizations on the Intermediate Representation targeting an abstract machine, rather than
 a physical machine. Therefore, all our optimization passes will work on the instruction set of this abstract machine.
 
+A VM / Interpreter is provided that can run the generated code.
 
 ## Guide
 
@@ -57,4 +58,4 @@ a physical machine. Therefore, all our optimization passes will work on the inst
 * [CompiledFunction](src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java) - builds and encapsulates the IR for a single function.
 * [Compiler](src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java) - simple orchestrator of compilation tasks.
 * [Optimizer](src/main/java/com/compilerprogramming/ezlang/compiler/Optimizer.java) - simple orchestrator of optimization steps. Currently
-  does not have optimization passes, but translates to SSA and out and then runs the graph coloring register allocator.
\ No newline at end of file
+  does not have optimization passes, but translates to SSA and out and then runs the graph coloring register allocator.

From 152026afd3427b56c383915f38f2dae1fb9f3a08 Mon Sep 17 00:00:00 2001
From: Dibyendu Majumdar <dibyendumajumdar@users.noreply.github.com>
Date: Sun, 13 Apr 2025 14:57:55 +0100
Subject: [PATCH 057/119] Update README.md

---
 optvm/README.md | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/optvm/README.md b/optvm/README.md
index 0b37839..6775bd6 100644
--- a/optvm/README.md
+++ b/optvm/README.md
@@ -59,3 +59,10 @@ A VM / Interpreter is provided that can run the generated code.
 * [Compiler](src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java) - simple orchestrator of compilation tasks.
 * [Optimizer](src/main/java/com/compilerprogramming/ezlang/compiler/Optimizer.java) - simple orchestrator of optimization steps. Currently
   does not have optimization passes, but translates to SSA and out and then runs the graph coloring register allocator.
+
+## VM/Interpreter
+
+A simple VM / Interpreter is provided that can run the IR, both pre and post optimizations.
+The SSA form is not executable, it must be transformed out of SSA for execuation.
+
+* [VM/Interpreter](src/main/java/com/compilerprogramming/ezlang/interpreter)

From b3701a80e6d01f6108d956947887b0a58b6b6070 Mon Sep 17 00:00:00 2001
From: Dibyendu Majumdar <dibyendumajumdar@users.noreply.github.com>
Date: Sun, 13 Apr 2025 19:33:50 +0100
Subject: [PATCH 058/119] Update README.md

---
 README.md | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/README.md b/README.md
index 086bf81..430ba59 100644
--- a/README.md
+++ b/README.md
@@ -1,19 +1,19 @@
-# EZ Programming Language
+# EeZee Programming Language
 
 This project is part of the https://compilerprogramming.github.io/ project.
 
-The EZ (pronounced EeZee) programming language is designed to allow us to learn various compiler techniques.
+The EeZee programming language is designed to allow us to learn various compiler techniques.
 
-## The EZ Language
+## The EeZee Language
 
-The EZ programming language is a tiny statically typed language with syntax inspired by Swift.
+The EeZee programming language is a tiny statically typed language with syntax inspired by Swift.
 The language has the following features:
 
 * Integer, Struct and 1-Dimensional Array types
 * If and While statements
 * Functions
 
-The EeZee language specifiaction is [available](https://compilerprogramming.github.io/ez-lang.html).
+The EeZee language specification is [available](https://compilerprogramming.github.io/ez-lang.html).
 The language is intentionally very simple and is meant to have just enough functionality to experiment with compiler implementation techniques.
 
 ## Modules

From c3f5ce5f8b87974ebfd0d2334882d8ab7a335bda Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Sun, 13 Apr 2025 11:59:46 +0100
Subject: [PATCH 059/119] Allow fields in array initializers so that size and
 initial value can be set.

---
 antlr-parser/examples/all-features.lang            | 14 +++++++++++---
 .../compilerprogramming/ezlang/parser/Parser.java  |  5 -----
 .../ezlang/parser/TestParser.java                  |  2 +-
 3 files changed, 12 insertions(+), 9 deletions(-)

diff --git a/antlr-parser/examples/all-features.lang b/antlr-parser/examples/all-features.lang
index 2a026c8..21f60a5 100644
--- a/antlr-parser/examples/all-features.lang
+++ b/antlr-parser/examples/all-features.lang
@@ -19,12 +19,20 @@ func foo(a: Int, b: [Int]) {
     }
 }
 func bar() -> Test {
-    var v = new Test { intArray = new [Tree] {} }
+    var v = new Test { intArray = new [Tree] { new Tree{left=null,right=null} } }
     return v
 }
 func main() {
     var m = 42
+    var j: Int
     var t: Tree
-    var b = true
-    var c: Bool
+    var array = new [Int] {size=10,1,2,3}
+    array[1] = 42
+    t.left = null
+    if (m < 1)
+       print(1)
+    else if (m == 5)
+       print(2)
+    else
+       print(3)
 }
diff --git a/parser/src/main/java/com/compilerprogramming/ezlang/parser/Parser.java b/parser/src/main/java/com/compilerprogramming/ezlang/parser/Parser.java
index 0d95fcb..821ce6c 100644
--- a/parser/src/main/java/com/compilerprogramming/ezlang/parser/Parser.java
+++ b/parser/src/main/java/com/compilerprogramming/ezlang/parser/Parser.java
@@ -334,13 +334,10 @@ private AST.Expr parseNew(Lexer lexer) {
         AST.TypeExpr resultType = parseTypeExpr(lexer);
         var newExpr = new AST.NewExpr(resultType);
         List<AST.Expr> initExpr = new ArrayList<>();
-        int initType = 0;
         int index = 0;
         if (testPunctuation(lexer, "{")) {
             while (!isToken(currentToken, "}")) {
                 if (currentToken.kind == Token.Kind.IDENT && lexer.peekChar() == '=') {
-                    if (initType == 0) initType = 1;
-                    else if (initType != 1) throw new CompilerException("Cannot mix initializer expressions");
                     String fieldname = currentToken.str;
                     nextToken(lexer);
                     matchPunctuation(lexer, "=");
@@ -348,8 +345,6 @@ private AST.Expr parseNew(Lexer lexer) {
                     initExpr.add(new AST.InitFieldExpr(newExpr, fieldname, value));
                 }
                 else {
-                    if (initType == 0) initType = 2;
-                    else if (initType != 2) throw new CompilerException("Cannot mix initializer expressions");
                     var indexLit = Integer.valueOf(index++);
                     var indexExpr = new AST.LiteralExpr(Token.newNum(indexLit,indexLit.toString(),0));
                     initExpr.add(new AST.ArrayInitExpr(newExpr, indexExpr, parseBool(lexer)));
diff --git a/parser/src/test/java/com/compilerprogramming/ezlang/parser/TestParser.java b/parser/src/test/java/com/compilerprogramming/ezlang/parser/TestParser.java
index 101c75d..1c60dc0 100644
--- a/parser/src/test/java/com/compilerprogramming/ezlang/parser/TestParser.java
+++ b/parser/src/test/java/com/compilerprogramming/ezlang/parser/TestParser.java
@@ -36,7 +36,7 @@ func bar() -> Test {
 func main() {
     var m = 42
     var t: Tree
-    var array = new [Int] {1,2,3}
+    var array = new [Int] {size=10,1,2,3}
     array[1] = 42
     t.left = null
     if (m < 1)

From 3144cb5d15d5841cc25b7456f0ccb3449f305a21 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <dibyendu@redukti.com>
Date: Mon, 14 Apr 2025 10:38:50 +0100
Subject: [PATCH 060/119] Revised language grammar and fib example

---
 antlr-parser/examples/all-features.lang       | 12 +++++++++++
 .../ezlang/antlr/EZLanguage.g4                |  9 ++++----
 .../ezlang/interpreter/TestInterpreter.java   | 21 ++++++++-----------
 seaofnodes/src/test/cases/fib/fib.ez          | 20 ++++++++----------
 4 files changed, 35 insertions(+), 27 deletions(-)

diff --git a/antlr-parser/examples/all-features.lang b/antlr-parser/examples/all-features.lang
index 21f60a5..9c4bea4 100644
--- a/antlr-parser/examples/all-features.lang
+++ b/antlr-parser/examples/all-features.lang
@@ -36,3 +36,15 @@ func main() {
     else
        print(3)
 }
+func fib(n: Int)->Int {
+    var f1=1
+    var f2=1
+    var i=n
+    while( i>1 ){
+        var temp = f1+f2
+        f1=f2
+        f2=temp
+        i=i-1
+    }
+    return f2
+}
diff --git a/antlr-parser/src/main/antlr4/com/compilerprogramming/ezlang/antlr/EZLanguage.g4 b/antlr-parser/src/main/antlr4/com/compilerprogramming/ezlang/antlr/EZLanguage.g4
index 476ca54..bfcd0c8 100644
--- a/antlr-parser/src/main/antlr4/com/compilerprogramming/ezlang/antlr/EZLanguage.g4
+++ b/antlr-parser/src/main/antlr4/com/compilerprogramming/ezlang/antlr/EZLanguage.g4
@@ -22,16 +22,17 @@ varDeclaration
     ;
 
 typeName
-    : simpleType
+    : nominalType
     | arrayType
     ;
 
-simpleType
-    : IDENTIFIER ('?')?
+nominalType
+    : 'Int'
+    | IDENTIFIER ('?')?
     ;
 
 arrayType
-    : '[' simpleType ']' ('?')?
+    : '[' nominalType ']' ('?')?
     ;
 
 functionDeclaration
diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java b/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java
index 161cf38..78af875 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java
@@ -173,22 +173,19 @@ func foo()->Int {
     public void testFunction9() {
         String src = """
                 func fib(n: Int)->Int {
-                    var i: Int;
-                    var temp: Int;
-                    var f1=1;
-                    var f2=1;
-                    i=n;
+                    var f1=1
+                    var f2=1
+                    var i=n
                     while( i>1 ){
-                        temp = f1+f2;
-                        f1=f2;
-                        f2=temp;
-                        i=i-1;
+                        var temp = f1+f2
+                        f1=f2
+                        f2=temp
+                        i=i-1
                     }
-                    return f2;
+                    return f2
                 }
-
                 func foo()->Int {
-                    return fib(10);
+                    return fib(10)
                 }
                 """;
         var value = compileAndRun(src, "foo", Options.OPT);
diff --git a/seaofnodes/src/test/cases/fib/fib.ez b/seaofnodes/src/test/cases/fib/fib.ez
index a30b6d9..9d07943 100644
--- a/seaofnodes/src/test/cases/fib/fib.ez
+++ b/seaofnodes/src/test/cases/fib/fib.ez
@@ -1,18 +1,16 @@
 func fib(n: Int)->Int {
-    var i: Int;
-    var temp=0;
-    var f1=1;
-    var f2=1;
-    i=n;
+    var f1=1
+    var f2=1
+    var i=n
     while( i>1 ){
-        temp = f1+f2;
-        f1=f2;
-        f2=temp;
-        i=i-1;
+        var temp = f1+f2
+        f1=f2
+        f2=temp
+        i=i-1
     }
-    return f2;
+    return f2
 }
 
 func foo()->Int {
-    return fib(10);
+    return fib(10)
 }
\ No newline at end of file

From 9fff7d0803c4759c2f95f010c3a52f531822d1f9 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Sat, 26 Apr 2025 00:34:04 +0100
Subject: [PATCH 061/119] Sync with final ch21

---
 .../compiler/nodes/cpus/arm/AddARM.java       |   2 +-
 .../compiler/nodes/cpus/arm/AddFARM.java      |   2 +-
 .../compiler/nodes/cpus/arm/AddIARM.java      |   2 +-
 .../compiler/nodes/cpus/arm/AndARM.java       |   2 +-
 .../compiler/nodes/cpus/arm/AndIARM.java      |   4 +-
 .../compiler/nodes/cpus/arm/AsrARM.java       |   2 +-
 .../compiler/nodes/cpus/arm/AsrIARM.java      |   2 +-
 .../compiler/nodes/cpus/arm/BranchARM.java    |  23 +-
 .../compiler/nodes/cpus/arm/CallEndARM.java   |  18 +-
 .../compiler/nodes/cpus/arm/CastARM.java      |  18 +
 .../compiler/nodes/cpus/arm/CmpARM.java       |   2 +-
 .../compiler/nodes/cpus/arm/CmpIARM.java      |   2 +-
 .../compiler/nodes/cpus/arm/DivARM.java       |   2 +-
 .../compiler/nodes/cpus/arm/FloatARM.java     |   2 +-
 .../compiler/nodes/cpus/arm/FunARM.java       |  36 +-
 .../compiler/nodes/cpus/arm/IntARM.java       |   3 +-
 .../compiler/nodes/cpus/arm/LoadARM.java      |  20 +-
 .../compiler/nodes/cpus/arm/LslARM.java       |   2 +-
 .../compiler/nodes/cpus/arm/LslIARM.java      |   2 +-
 .../compiler/nodes/cpus/arm/LsrARM.java       |   2 +-
 .../compiler/nodes/cpus/arm/LsrIARM.java      |   2 +-
 .../compiler/nodes/cpus/arm/MemOpARM.java     |  10 +-
 .../compiler/nodes/cpus/arm/MulARM.java       |   2 +-
 .../compiler/nodes/cpus/arm/NegARM.java       |  25 ++
 .../compiler/nodes/cpus/arm/NewARM.java       |  32 +-
 .../compiler/nodes/cpus/arm/NotARM.java       |   2 +-
 .../ezlang/compiler/nodes/cpus/arm/OrARM.java |   2 +-
 .../compiler/nodes/cpus/arm/OrIARM.java       |   2 +-
 .../compiler/nodes/cpus/arm/RetARM.java       |  28 --
 .../compiler/nodes/cpus/arm/SetARM.java       |   2 +-
 .../compiler/nodes/cpus/arm/SplitARM.java     |  16 +-
 .../compiler/nodes/cpus/arm/StoreARM.java     |  16 +-
 .../compiler/nodes/cpus/arm/SubARM.java       |   2 +-
 .../compiler/nodes/cpus/arm/SubIARM.java      |   6 +-
 .../compiler/nodes/cpus/arm/UJmpARM.java      |   2 +-
 .../compiler/nodes/cpus/arm/XorARM.java       |   2 +-
 .../compiler/nodes/cpus/arm/XorIARM.java      |   2 +-
 .../ezlang/compiler/nodes/cpus/arm/arm.java   | 375 ++++++++--------
 .../compiler/nodes/cpus/riscv/BranchRISC.java |  45 +-
 .../nodes/cpus/riscv/CallEndRISC.java         |  17 +-
 .../compiler/nodes/cpus/riscv/CallRISC.java   |   2 -
 .../compiler/nodes/cpus/riscv/FltRISC.java    |   6 +-
 .../compiler/nodes/cpus/riscv/FunRISC.java    |  36 +-
 .../compiler/nodes/cpus/riscv/ImmRISC.java    |   2 +-
 .../compiler/nodes/cpus/riscv/IntRISC.java    |   5 +-
 .../compiler/nodes/cpus/riscv/MemOpRISC.java  |   2 +-
 .../compiler/nodes/cpus/riscv/NJmpRISC.java   |  46 --
 .../compiler/nodes/cpus/riscv/NegRISC.java    |  20 +
 .../compiler/nodes/cpus/riscv/NewRISC.java    |  39 +-
 .../compiler/nodes/cpus/riscv/NotRISC.java    |  18 +-
 .../compiler/nodes/cpus/riscv/RetRISC.java    |  39 +-
 .../compiler/nodes/cpus/riscv/SetFRISC.java   |   2 +-
 .../compiler/nodes/cpus/riscv/SplitRISC.java  |  15 +-
 .../compiler/nodes/cpus/riscv/StoreRISC.java  |   2 +-
 .../compiler/nodes/cpus/riscv/UJmpRISC.java   |   2 +-
 .../compiler/nodes/cpus/riscv/riscv.java      | 236 +++++-----
 .../nodes/cpus/x86_64_v2/AddMemX86.java       |   5 +-
 .../nodes/cpus/x86_64_v2/CallEndX86.java      |  17 +-
 .../nodes/cpus/x86_64_v2/CallX86.java         |   1 -
 .../nodes/cpus/x86_64_v2/CmpIX86.java         |   9 +-
 .../nodes/cpus/x86_64_v2/CmpMemX86.java       |  57 ++-
 .../compiler/nodes/cpus/x86_64_v2/FunX86.java |  48 +--
 .../compiler/nodes/cpus/x86_64_v2/JmpX86.java |  34 +-
 .../nodes/cpus/x86_64_v2/LoadX86.java         |  20 +-
 .../nodes/cpus/x86_64_v2/MemAddX86.java       |   2 +-
 .../nodes/cpus/x86_64_v2/MemOpX86.java        |  11 +-
 .../compiler/nodes/cpus/x86_64_v2/NegX86.java |  21 +
 .../compiler/nodes/cpus/x86_64_v2/NewX86.java |  17 +-
 .../compiler/nodes/cpus/x86_64_v2/RetX86.java |  34 +-
 .../nodes/cpus/x86_64_v2/SarIX86.java         |   1 +
 .../compiler/nodes/cpus/x86_64_v2/SetX86.java |   1 +
 .../nodes/cpus/x86_64_v2/ShlIX86.java         |   1 +
 .../nodes/cpus/x86_64_v2/ShrIX86.java         |   1 +
 .../nodes/cpus/x86_64_v2/SplitX86.java        |  15 +-
 .../nodes/cpus/x86_64_v2/StoreX86.java        |  46 +-
 .../nodes/cpus/x86_64_v2/UJmpX86.java         |   2 +-
 .../nodes/cpus/x86_64_v2/x86_64_v2.java       | 407 ++++++++----------
 77 files changed, 974 insertions(+), 986 deletions(-)
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CastARM.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/NegARM.java
 delete mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/NJmpRISC.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/NegRISC.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/NegX86.java

diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AddARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AddARM.java
index 9fc20ac..ee8f426 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AddARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AddARM.java
@@ -8,7 +8,7 @@ public class AddARM extends MachConcreteNode implements MachNode {
     AddARM( Node add) { super(add); }
     @Override public String op() { return "add"; }
     @Override public RegMask regmap(int i) { return arm.RMASK; }
-    @Override public RegMask outregmap() { return arm.RMASK; }
+    @Override public RegMask outregmap() { return arm.WMASK; }
 
     // ADD (shifted register)
     @Override public void encoding( Encoding enc ) { arm.r_reg(enc,this,arm.OP_ADD); }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AddFARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AddFARM.java
index 807c0a4..4701712 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AddFARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AddFARM.java
@@ -11,7 +11,7 @@ public class AddFARM extends MachConcreteNode implements MachNode {
     @Override public RegMask outregmap() { return arm.DMASK; }
 
     //FADD (scalar)
-    @Override public void encoding( Encoding enc ) { arm.f_scalar(enc,this,arm.OPF_ADD); }
+    @Override public void encoding( Encoding enc ) { arm.f_scalar(enc,this,arm.OPF_OP_ADD);}
 
     // Default on double precision for now(64 bits)
     // General form: "addf  rd = src1 + src2
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AddIARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AddIARM.java
index 4c2b94a..e85f38a 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AddIARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AddIARM.java
@@ -15,7 +15,7 @@ public class AddIARM extends MachConcreteNode implements MachNode {
         return _imm == 1 ? "inc" : (_imm == -1 ? "dec" : "addi");
     }
     @Override public RegMask regmap(int i) { return arm.RMASK; }
-    @Override public RegMask outregmap() { return arm.RMASK; }
+    @Override public RegMask outregmap() { return arm.WMASK; }
     //ADD (immediate)
     @Override public void encoding( Encoding enc ) {
         arm.imm_inst(enc,this, in(1), arm.OPI_ADD,_imm);
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AndARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AndARM.java
index 94ee434..d932154 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AndARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AndARM.java
@@ -11,7 +11,7 @@ public class AndARM extends MachConcreteNode implements MachNode {
     @Override public String op() { return "and"; }
     @Override public String glabel() { return "&"; }
     @Override public RegMask regmap(int i) { return arm.RMASK; }
-    @Override public RegMask outregmap() { return arm.RMASK; }
+    @Override public RegMask outregmap() { return arm.WMASK; }
     // AND (shifted register)
     @Override public void encoding( Encoding enc ) { arm.r_reg(enc,this,arm.OP_AND); }
     // General form:  #rd = rs1 & rs2
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AndIARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AndIARM.java
index 96b4485..4961645 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AndIARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AndIARM.java
@@ -13,12 +13,12 @@ public class AndIARM extends MachConcreteNode implements MachNode {
     }
     @Override public String op() { return "andi"; }
     @Override public RegMask regmap(int i) { return arm.RMASK; }
-    @Override public RegMask outregmap() { return arm.RMASK; }
+    @Override public RegMask outregmap() { return arm.WMASK; }
     @Override public void encoding( Encoding enc ) {
         arm.imm_inst_n(enc,this, in(1), arm.OPI_AND,_imm);
     }
     // 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(" & #").p(_imm);
+        sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" & #").p(arm.decodeImm12(_imm));
     }
 }
\ No newline at end of file
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AsrARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AsrARM.java
index 7bd9418..0ca9280 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AsrARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AsrARM.java
@@ -9,7 +9,7 @@ public class AsrARM extends MachConcreteNode implements MachNode {
     AsrARM(Node asr) {super(asr);}
     @Override public String op() { return "sar"; }
     @Override public RegMask regmap(int i) { return arm.RMASK; }
-    @Override public RegMask outregmap() { return arm.RMASK; }
+    @Override public RegMask outregmap() { return arm.DMASK; }
     @Override public void encoding( Encoding enc ) { arm.shift_reg(enc,this,arm.OP_ASR); }
     @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)));
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AsrIARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AsrIARM.java
index 3f903b3..5f78666 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AsrIARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AsrIARM.java
@@ -17,7 +17,7 @@ public class AsrIARM extends MachConcreteNode implements MachNode {
     }
     @Override public String op() { return "asri"; }
     @Override public RegMask regmap(int i) { return arm.RMASK; }
-    @Override public RegMask outregmap() { return arm.RMASK; }
+    @Override public RegMask outregmap() { return arm.WMASK; }
     @Override public void encoding( Encoding enc ) {
         short rd = enc.reg(this);
         short rn = enc.reg(in(1));
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/BranchARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/BranchARM.java
index 8494f39..8bb1ad6 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/BranchARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/BranchARM.java
@@ -16,6 +16,7 @@ public class BranchARM extends IfNode implements MachNode, RIPRelSize {
 
     @Override public void postSelect(CodeGen code) {
         Node set = in(1);
+        if( set==null ) return; // Never-node cutout
         Node cmp = set.in(1);
         // Bypass an expected Set and just reference the cmp directly
         if( set instanceof SetARM)
@@ -26,18 +27,24 @@ public class BranchARM extends IfNode implements MachNode, RIPRelSize {
 
     @Override public RegMask regmap(int i) { assert i==1; return arm.FLAGS_MASK; }
     @Override public RegMask outregmap() { return null; }
-    @Override public void invert() { _bop = invert(_bop);  }
+    @Override public void negate() { _bop = negate(_bop);  }
 
     @Override public void encoding( Encoding enc ) {
         // Assuming that condition flags are already set.  These flags are set
         // by comparison (or sub).  No need for regs because it uses flags
+        if( in(1)!=null ) {
+            // B.cond
+            enc.add4( arm.b_cond(arm.OP_BRANCH, 0, arm.make_condition(_bop)) );
+        } else {
+            if( _bop=="!=" ) return; // Inverted, no code
+            enc.add4(arm.b(arm.OP_UJMP, 0));
+        }
         enc.jump(this,cproj(0));
-        // B.cond
-        enc.add4( arm.b_cond(arm.OP_BRANCH, 0, arm.make_condition(_bop)) );
     }
 
     // Delta is from opcode start
     @Override public byte encSize(int delta) {
+        if( in(1)==null && _bop=="!=" ) return 0; // Inverted never-node, no code
         if( -(1<<19) <= delta && delta < (1<<19) ) return 4;
         // 2 word encoding needs a tmp register, must teach RA
         throw Utils.TODO();
@@ -45,6 +52,7 @@ public class BranchARM extends IfNode implements MachNode, RIPRelSize {
 
     // Delta is from opcode start
     @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) {
+        assert !( in(1)==null && _bop=="!=" ); // Inverted never-node, no code no patch
         if( opLen==4 ) {
             enc.patch4(opStart,arm.b_cond(arm.OP_BRANCH, delta, arm.make_condition(_bop)));
         } else {
@@ -53,8 +61,13 @@ public class BranchARM extends IfNode implements MachNode, RIPRelSize {
     }
 
     @Override public void asm(CodeGen code, SB sb) {
-        String src = code.reg(in(1));
-        if( src!="flags" ) sb.p(src).p(" ");
+        if( in(1)!=null ) {     // Never-node
+            String src = code.reg(in(1));
+            if( src!="flags" ) sb.p(src).p(" ");
+        } else if( _bop=="!=" ) {
+            sb.p("never");
+            return;
+        }
         CFGNode prj = cproj(0).uctrlSkipEmpty();
         if( !prj.blockHead() ) prj = prj.cfg0();
         sb.p(label(prj));
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CallEndARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CallEndARM.java
index af4d96c..eb868c1 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CallEndARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CallEndARM.java
@@ -1,24 +1,8 @@
 package com.compilerprogramming.ezlang.compiler.nodes.cpus.arm;
 
-import com.compilerprogramming.ezlang.compiler.SB;
-import com.compilerprogramming.ezlang.compiler.codegen.*;
 import com.compilerprogramming.ezlang.compiler.nodes.CallEndNode;
 import com.compilerprogramming.ezlang.compiler.nodes.MachNode;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFunPtr;
 
 public class CallEndARM extends CallEndNode implements MachNode {
-    final SONTypeFunPtr _tfp;
-    CallEndARM( CallEndNode cend ) {
-        super(cend);
-        _tfp = (SONTypeFunPtr)(cend.call().fptr()._type);
-    }
-
-    @Override public String op() { return "cend"; }
-    @Override public String label() { return op(); }
-    @Override public RegMask regmap(int i) { return null; }
-    @Override public RegMask outregmap() { return null; }
-    @Override public RegMask outregmap(int idx) { return idx == 2 ? arm.retMask(_tfp,2) : null; }
-    @Override public RegMask killmap() { return arm.armCallerSave(); }
-    @Override public void encoding( Encoding enc ) { }
-    @Override public void asm(CodeGen code, SB sb) {  }
+    CallEndARM( CallEndNode cend ) { super(cend); }
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CastARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CastARM.java
new file mode 100644
index 0000000..432a5b8
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CastARM.java
@@ -0,0 +1,18 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.arm;
+
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.codegen.CodeGen;
+import com.compilerprogramming.ezlang.compiler.codegen.Encoding;
+import com.compilerprogramming.ezlang.compiler.codegen.RegMask;
+import com.compilerprogramming.ezlang.compiler.nodes.CastNode;
+import com.compilerprogramming.ezlang.compiler.nodes.MachNode;
+
+public class CastARM extends CastNode implements MachNode {
+    public CastARM( CastNode cast ) { super(cast); }
+    @Override public String op() { return label(); }
+    @Override public RegMask regmap(int i) { assert i==1; return RegMask.FULL; }
+    @Override public RegMask outregmap() { return RegMask.FULL; }
+    @Override public int twoAddress( ) { return 1; }
+    @Override public void encoding( Encoding enc ) { }
+    @Override public void asm(CodeGen code, SB sb) { _t.print(sb.p(code.reg(in(1))).p(" isa ")); }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CmpARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CmpARM.java
index b5dba20..cdf80f2 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CmpARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CmpARM.java
@@ -11,7 +11,7 @@ public class CmpARM extends MachConcreteNode implements MachNode {
     @Override public RegMask outregmap() { return arm.FLAGS_MASK; }
 
     // SUBS (shifted register)
-    @Override public void encoding( Encoding enc ) { arm.r_reg(enc,this,arm.OP_CMP); }
+    @Override public void encoding( Encoding enc ) { arm.r_reg_subs(enc,this,arm.OP_CMP); }
 
     // General form: "cmp  rs1, rs2"
     @Override public void asm(CodeGen code, SB sb) {
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CmpIARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CmpIARM.java
index c9b85f4..3dce7f2 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CmpIARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CmpIARM.java
@@ -28,7 +28,7 @@ public class CmpIARM extends MachConcreteNode implements MachNode {
     @Override public boolean isClone() { return true; }
     @Override public Node copy() { return new CmpIARM(this); }
 
-    @Override public void encoding( Encoding enc ) { arm.imm_inst(enc,in(1),in(1), arm.OPI_CMP,_imm); }
+    @Override public void encoding( Encoding enc ) { arm.imm_inst_subs(enc,in(1),in(1), arm.OPI_CMP,_imm); }
 
     // General form: "cmp  rs1, 1"
     @Override public void asm(CodeGen code, SB sb) {
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/DivARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/DivARM.java
index e9c8dbc..0c4c17e 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/DivARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/DivARM.java
@@ -8,7 +8,7 @@ public class DivARM extends MachConcreteNode implements MachNode {
     DivARM( Node div ) { super(div); }
     @Override public String op() { return "div"; }
     @Override public RegMask regmap(int i) { return arm.RMASK; }
-    @Override public RegMask outregmap() { return arm.RMASK; }
+    @Override public RegMask outregmap() { return arm.WMASK; }
     // SDIV
     @Override public void encoding( Encoding enc ) { arm.madd(enc,this,arm.OP_DIV,3); }
     // General form: "div  dst /= src"
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/FloatARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/FloatARM.java
index b8f41b1..db04638 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/FloatARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/FloatARM.java
@@ -31,7 +31,7 @@ public class FloatARM extends ConstantNode implements MachNode, RIPRelSize {
     // Delta is from opcode start
     @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) {
         short dst = (short)(enc.reg(this) - arm.D_OFFSET);
-        enc.add4(arm.load_pc(arm.OPF_ARM, delta, dst));
+        enc.patch4(opStart, arm.load_pc(arm.OPF_ARM, delta, dst));
     }
 
     // Human-readable form appended to the SB.  Things like the encoding,
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/FunARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/FunARM.java
index a270595..382543c 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/FunARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/FunARM.java
@@ -1,36 +1,22 @@
 package com.compilerprogramming.ezlang.compiler.nodes.cpus.arm;
 
-import com.compilerprogramming.ezlang.compiler.codegen.*;
 import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.Utils;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
 import com.compilerprogramming.ezlang.compiler.nodes.FunNode;
 import com.compilerprogramming.ezlang.compiler.nodes.MachNode;
 
 public class FunARM  extends FunNode implements MachNode {
     FunARM(FunNode fun) { super(fun); }
-    @Override public String op() { return "addi"; }
-    @Override public RegMask regmap(int i) { return null; }
-    @Override public RegMask outregmap() { return null; }
-    @Override public void postSelect(CodeGen code) { code.link(this);  }
-
-    // Actual stack layout is up to each CPU.
-    // X86, with too many args & spills:
-    // | CALLER |
-    // |  argN  | // slot 1, required by callER
-    // +--------+
-    // |  RPC   | // slot 0, required by callER
-    // | callee | // slot 3, callEE
-    // | callee | // slot 2, callEE
-    // |  PAD16 |
-    // +--------+
-    @Override public void encoding( Encoding enc ) {
-        // Stack frame size: _maxSlot - max(arg), padded to 16.
-        _maxArgSlot = arm.maxSlot(enc._fun.sig());
-        _frameAdjust = (short) (_maxSlot+1 - _maxArgSlot);
-        if( _frameAdjust == 0 ) return; // Skip if no frame adjust
-        enc.add4(arm.imm_inst(arm.OPI_ADD, (_frameAdjust*8)&0xFFF, arm.RSP, arm.RSP));
+    @Override public void computeFrameAdjust(CodeGen code, int maxReg) {
+        super.computeFrameAdjust(code,maxReg);
+        if( _hasCalls )         // If non-leaf, pad to 16b
+            _frameAdjust = ((_frameAdjust+8) & -16);
     }
-
-    @Override public void asm(CodeGen code, SB sb) {
-        sb.p("rsp += #").p(_frameAdjust*8);
+    @Override public void encoding( Encoding enc ) {
+        int sz = _frameAdjust;
+        if( sz == 0 ) return;   // Skip if no frame adjust
+        if( sz >= 1L<<12 ) throw Utils.TODO();
+        enc.add4(arm.imm_inst(arm.OPI_ADD, -sz&0xFFF, arm.RSP, arm.RSP));
     }
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/IntARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/IntARM.java
index 495559b..9e00093 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/IntARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/IntARM.java
@@ -4,6 +4,7 @@
 import com.compilerprogramming.ezlang.compiler.codegen.*;
 import com.compilerprogramming.ezlang.compiler.nodes.ConstantNode;
 import com.compilerprogramming.ezlang.compiler.nodes.MachNode;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
 import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeInteger;
 
 // Integer constants
@@ -19,7 +20,7 @@ public class IntARM extends ConstantNode implements MachNode {
 
     @Override public void encoding( Encoding enc ) {
         short self = enc.reg(this);
-        long x = ((SONTypeInteger)_con).value();
+        long x = _con==SONType.NIL ? 0 : ((SONTypeInteger)_con).value();
         int nb0 = 0;
         int nb1 = 0;
         // Count number of 0000 and FFFF blocks
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LoadARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LoadARM.java
index 2700fdb..eff01e0 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LoadARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LoadARM.java
@@ -12,19 +12,25 @@
 // idx  = null
 // off  = off - offset added to base
 
-public class LoadARM extends MemOpARM{
-    LoadARM(LoadNode ld,Node base, Node idx, int off) {
+public class LoadARM extends MemOpARM {
+    LoadARM(LoadNode ld, Node base, Node idx, int off) {
         super(ld, base, idx, off, 0);
     }
     @Override public String op() { return "ld"+_sz; }
     @Override public RegMask outregmap() { return arm.MEM_MASK; }
+
+    private static final int[] OP_LOADS  = new int[]{ arm.OP_LOAD_IMM_8,  arm.OP_LOAD_IMM_16,  arm.OP_LOAD_IMM_32,  arm.OP_LOAD_IMM_64, };
+    private int imm_op() {
+        return _declaredType == SONTypeFloat.F32 ? arm.OPF_LOAD_IMM_32
+            :  _declaredType == SONTypeFloat.F64 ? arm.OPF_LOAD_IMM_64
+            :  OP_LOADS[_declaredType.log_size()];
+    }
+
+    private static final int[] OP_LOAD_RS  = new int[]{ arm.OP_LOAD_R_8 , arm.OP_LOAD_R_16 , arm.OP_LOAD_R_32 , arm.OP_LOAD_R_64,  };
+
     // ldr(immediate - unsigned offset) | ldr(register)
     @Override public void encoding( Encoding enc ) {
-        if(_declaredType == SONTypeFloat.F32 || _declaredType == SONTypeFloat.F64) {
-            ldst_encode(enc, arm.OPF_LOAD_IMM, arm.OPF_LOAD_R, this, true);
-        } else {
-            ldst_encode(enc, arm.OP_LOAD_IMM, arm.OP_LOAD_R, this, false);
-        }
+        ldst_encode(enc, imm_op(), OP_LOAD_RS[_declaredType.log_size()], this, size());
     }
     @Override public void asm(CodeGen code, SB sb) {
         sb.p(code.reg(this)).p(",");
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LslARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LslARM.java
index 594e0e4..d7630fb 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LslARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LslARM.java
@@ -9,7 +9,7 @@ public class LslARM extends MachConcreteNode implements MachNode {
     LslARM(Node asr) {super(asr);}
     @Override public String op() { return "shr"; }
     @Override public RegMask regmap(int i) { return arm.RMASK; }
-    @Override public RegMask outregmap() { return arm.RMASK; }
+    @Override public RegMask outregmap() { return arm.WMASK; }
 
     @Override public void encoding( Encoding enc ) { arm.shift_reg(enc,this,arm.OP_LSL); }
     @Override public void asm(CodeGen code, SB sb) {
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LslIARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LslIARM.java
index a82fbf4..65de773 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LslIARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LslIARM.java
@@ -17,7 +17,7 @@ public class LslIARM extends MachConcreteNode implements MachNode {
     }
     @Override public String op() { return "lsli"; }
     @Override public RegMask regmap(int i) { return arm.RMASK; }
-    @Override public RegMask outregmap() { return arm.RMASK; }
+    @Override public RegMask outregmap() { return arm.WMASK; }
     @Override public void encoding( Encoding enc ) {
         short rd = enc.reg(this);
         short rn = enc.reg(in(1));
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LsrARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LsrARM.java
index af4763b..34aaac4 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LsrARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LsrARM.java
@@ -9,7 +9,7 @@ public class LsrARM extends MachConcreteNode implements MachNode {
     LsrARM(Node asr) {super(asr);}
     @Override public String op() { return "shr"; }
     @Override public RegMask regmap(int i) { return arm.RMASK; }
-    @Override public RegMask outregmap() { return arm.RMASK; }
+    @Override public RegMask outregmap() { return arm.WMASK; }
 
     @Override public void encoding( Encoding enc ) { arm.shift_reg(enc,this,arm.OP_LSR); }
     @Override public void asm(CodeGen code, SB sb) {
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LsrIARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LsrIARM.java
index f1fff80..2251f74 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LsrIARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LsrIARM.java
@@ -15,7 +15,7 @@ public class LsrIARM extends MachConcreteNode implements MachNode {
     }
     @Override public String op() { return "lsri"; }
     @Override public RegMask regmap(int i) { return arm.RMASK; }
-    @Override public RegMask outregmap() { return arm.RMASK; }
+    @Override public RegMask outregmap() { return arm.WMASK; }
     @Override public void encoding( Encoding enc ) {
         short rd = enc.reg(this);
         short rn = enc.reg(in(1));
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/MemOpARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/MemOpARM.java
index 70dfdee..fcb5986 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/MemOpARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/MemOpARM.java
@@ -29,7 +29,7 @@ public abstract class MemOpARM extends MemOpNode implements MachNode {
         _inputs.setX(4,val);
     }
 
-    @Override public String label() { return op(); }
+    @Override public String label() { return op();}
     Node val() { return in(4); } // Only for stores
 
     @Override public  StringBuilder _printMach(StringBuilder sb, BitSet visited) { return sb.append(".").append(_name); }
@@ -37,6 +37,8 @@ public abstract class MemOpARM extends MemOpNode implements MachNode {
     @Override public SONType compute() { throw Utils.TODO(); }
     @Override public Node idealize() { throw Utils.TODO(); }
 
+    int size() { return 1<<_declaredType.log_size(); }
+
     // Wider mask to store both GPRs and FPRs
     @Override public RegMask regmap(int i) {
         // 0 - ctrl
@@ -44,16 +46,16 @@ public abstract class MemOpARM extends MemOpNode implements MachNode {
         if( i==2 ) return arm.RMASK; // ptr/base
         if( i==3 ) return arm.RMASK; // off/index
         if( i==4 ) return arm.RMASK; // value
-        throw Utils.TODO();
+        return null; // Anti-dependence
     }
 
     // Shared encoding for loads and stores(int and float)
-    public void ldst_encode( Encoding enc, int opcode_imm, int opcode_reg, Node xval, boolean float_type ) {
+    public void ldst_encode( Encoding enc, int opcode_imm, int opcode_reg, Node xval, int size) {
         short ptr = enc.reg(ptr());
         short off = enc.reg(off());
         short val = enc.reg(xval);
         int body = off() == null
-            ? arm.load_str_imm(opcode_imm, _off, ptr, val)
+            ? arm.load_str_imm(opcode_imm, _off, ptr, val, size)
             : arm.indr_adr(opcode_reg, off, arm.STORE_LOAD_OPTION.SXTX, 0, ptr, val);
         enc.add4(body);
     }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/MulARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/MulARM.java
index 1f69b83..82121df 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/MulARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/MulARM.java
@@ -9,7 +9,7 @@ public class MulARM extends MachConcreteNode implements MachNode {
     MulARM(Node mul) {super(mul);}
     @Override public String op() { return "mul"; }
     @Override public RegMask regmap(int i) { return arm.RMASK; }
-    @Override public RegMask outregmap() { return arm.RMASK; }
+    @Override public RegMask outregmap() { return arm.WMASK; }
 
     @Override public void encoding( Encoding enc ) { arm.madd(enc,this,arm.OP_MUL,31); }
     // General form: "rd = rs1 * rs2"
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/NegARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/NegARM.java
new file mode 100644
index 0000000..fa76b32
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/NegARM.java
@@ -0,0 +1,25 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.arm;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+import com.compilerprogramming.ezlang.compiler.nodes.MachConcreteNode;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeInteger;
+
+public class NegARM extends MachConcreteNode implements MachNode {
+    NegARM(Node sub) {
+        super(sub);
+        //_inputs.pop();
+    }
+    @Override public String op() { return "neg"; }
+    @Override public RegMask regmap(int i) { return arm.RMASK; }
+    @Override public RegMask outregmap()   { return arm.WMASK; }
+    @Override public void encoding( Encoding enc ) {
+        // reverse subtract with immediate 0
+        arm.imm_inst(enc,this, in(1), 0b00100110, 0);
+    }
+    // General form: "subi  rd = rs1 - imm"
+    @Override public void asm(CodeGen code, SB sb) {
+        sb.p(code.reg(this)).p(" = -").p(code.reg(in(1)));
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/NewARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/NewARM.java
index e867fed..5781c00 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/NewARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/NewARM.java
@@ -4,21 +4,33 @@
 import com.compilerprogramming.ezlang.compiler.codegen.*;
 import com.compilerprogramming.ezlang.compiler.nodes.*;
 
-public class NewARM extends NewNode implements MachNode {
+public class NewARM extends NewNode implements MachNode, RIPRelSize {
     // A pre-zeroed chunk of memory.
     NewARM(NewNode nnn) { super(nnn); }
-    @Override public String op() { return "alloc"; }
-    @Override public RegMask    regmap(int i) { return i == 1 ? arm. X0_MASK : null; }
-    @Override public RegMask outregmap(int i) { return i == 1 ? arm. X0_MASK : null; }
-    @Override public RegMask outregmap() { return null; }
-    @Override public RegMask killmap() { return arm.armCallerSave(); }
-
     @Override public void encoding( Encoding enc ) {
-        // bl
-        enc.external(this,"calloc").add4(arm.b(arm.OP_CALL, 0));
+        // BL(branch with link)
+        enc.external(this,"calloc").
+            add4(arm.mov(arm.OP_MOVZ, 0, 1, 0)).   // movz x0,#1
+            add4(arm.b(arm.OP_CALL, 0));
     }
 
+    // Patch is for running "new" in a JIT.
+    // Delta is from opcode start
+    @Override public byte encSize(int delta) { return 4*2; }
+
+    // Patch is for running "new" in a JIT.
+    // Delta is from opcode start
+    @Override public void patch(Encoding enc, int opStart, int opLen, int delta ) {
+        // Negative patches are JIT emulator targets.
+        if( opStart+delta < 0 ) {
+            enc.patch4(opStart+4, arm.b_calloc(arm.OP_CALL, delta-4));
+        } else {
+            throw Utils.TODO();
+        }
+    }
+    // General form: "alloc #bytes  PC"
     @Override public void asm(CodeGen code, SB sb) {
-        sb.p("#calloc, ").p(code.reg(size()));
+        sb.p("ldi   x0=#1\n");
+        sb.p("call  #calloc");
     }
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/NotARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/NotARM.java
index 19d0408..8fa9118 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/NotARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/NotARM.java
@@ -10,7 +10,7 @@ public class NotARM extends MachConcreteNode implements MachNode{
     NotARM(NotNode not) {super(not);}
     @Override public String op() { return "not"; }
     @Override public RegMask regmap(int i) { return arm.RMASK; }
-    @Override public RegMask outregmap() { return arm.RMASK;  }
+    @Override public RegMask outregmap() { return arm.WMASK;  }
     @Override public RegMask killmap() { return arm.FLAGS_MASK; }
     @Override public void encoding( Encoding enc ) {
         // subs xzr, rs, #0
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/OrARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/OrARM.java
index 7208ea9..9b0e1b2 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/OrARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/OrARM.java
@@ -10,7 +10,7 @@ public class OrARM extends MachConcreteNode implements MachNode {
     @Override public String op() { return "or"; }
     @Override public String glabel() { return "|"; }
     @Override public RegMask regmap(int i) { return arm.RMASK; }
-    @Override public RegMask outregmap() { return arm.RMASK; }
+    @Override public RegMask outregmap() { return arm.WMASK; }
     @Override public void encoding( Encoding enc ) { arm.r_reg(enc,this,arm.OP_OR); }
     // General form:  #rd = rs1 & rs2
     @Override public void asm(CodeGen code, SB sb) {
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/OrIARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/OrIARM.java
index c33de85..5f73fb5 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/OrIARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/OrIARM.java
@@ -13,7 +13,7 @@ public class OrIARM extends MachConcreteNode implements MachNode {
     }
     @Override public String op() { return "ori"; }
     @Override public RegMask regmap(int i) { return arm.RMASK; }
-    @Override public RegMask outregmap() { return arm.RMASK; }
+    @Override public RegMask outregmap() { return arm.WMASK; }
     @Override public void encoding( Encoding enc ) {
         arm.imm_inst_n(enc, this, in(1), arm.OPI_OR, _imm);
     }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/RetARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/RetARM.java
index 7497cb4..2c88f3b 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/RetARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/RetARM.java
@@ -6,38 +6,10 @@
 
 public class RetARM extends ReturnNode implements MachNode {
     RetARM(ReturnNode ret, FunNode fun) { super(ret, fun); fun.setRet(this); }
-    @Override public String op() {
-        return ((FunARM)fun())._frameAdjust > 0 ? "addi" : "ret";
-    }
-    // Correct Nodes outside the normal edges
-    @Override public void postSelect(CodeGen code) {
-        FunNode fun = (FunNode)rpc().in(0);
-        _fun = fun;
-        fun.setRet(this);
-    }
-    @Override public RegMask regmap(int i) { return arm.retMask(_fun.sig(),i); }
-    @Override public RegMask outregmap() { return null; }
-    // RET
     @Override public void encoding( Encoding enc ) {
         int frameAdjust = ((FunARM)fun())._frameAdjust;
         if( frameAdjust > 0 )
             enc.add4(arm.imm_inst(arm.OPI_ADD, (frameAdjust*-8)&0xFFF, arm.RSP, arm.RSP));
         enc.add4(arm.ret(arm.OP_RET));
     }
-
-    @Override public void asm(CodeGen code, SB sb) {
-        int frameAdjust = ((FunARM)fun())._frameAdjust;
-        if( frameAdjust>0 )
-            sb.p("rsp += #").p(frameAdjust*-8).p("\nret");
-        // Post code-gen, just print the "ret"
-        if( code._phase.ordinal() <= CodeGen.Phase.RegAlloc.ordinal() )
-            // Prints return reg (either X0 or D0), RPC (always R30) and
-            // then the callee-save registers.
-            for( int i=2; i<nIns(); i++ )
-                sb.p(code.reg(in(i))).p("  ");
-
-        // Post-allocation, if we did not get the expected return register, print what we got
-        else if( code._regAlloc.regnum(rpc()) != arm.X30 )
-            sb.p("[").p(code.reg(rpc())).p("]");
-    }
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SetARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SetARM.java
index d3806f9..136e9ea 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SetARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SetARM.java
@@ -17,7 +17,7 @@ public class SetARM extends MachConcreteNode implements MachNode {
     }
     @Override public String op() { return "set"+_bop; }
     @Override public RegMask regmap(int i) { assert i==1; return arm.FLAGS_MASK; }
-    @Override public RegMask outregmap() { return arm.RMASK; }
+    @Override public RegMask outregmap() { return arm.WMASK; }
 
     @Override public void encoding( Encoding enc ) {
         int body = arm.cset(arm.OP_CSET,0b11111, arm.make_condition(_bop), 0b011111, enc.reg(this));
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SplitARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SplitARM.java
index 1686008..66ae451 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SplitARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SplitARM.java
@@ -1,7 +1,9 @@
 package com.compilerprogramming.ezlang.compiler.nodes.cpus.arm;
 
+import com.compilerprogramming.ezlang.compiler.SB;
 import com.compilerprogramming.ezlang.compiler.Utils;
 import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.FunNode;
 import com.compilerprogramming.ezlang.compiler.nodes.Node;
 import com.compilerprogramming.ezlang.compiler.nodes.SplitNode;
 import com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv.riscv;
@@ -23,17 +25,17 @@ public class SplitARM extends SplitNode {
             if(src >= arm.MAX_REG) {
                 throw Utils.TODO();
             }
-            int off = enc._fun.computeStackSlot(dst - arm.MAX_REG)*8;
+            int off = enc._fun.computeStackOffset(enc._code,dst);
             if( srcX ) src -= arm.D_OFFSET;
-            enc.add4(arm.load_str_imm(arm.OP_STORE_IMM, off, arm.RSP, src));
+            enc.add4(arm.load_str_imm(arm.OP_STORE_IMM_64, off, arm.RSP, src, 8));
             return;
         }
 
         if(src >= arm.MAX_REG) {
             // Load from SP
-            int off = enc._fun.computeStackSlot(src - arm.MAX_REG) * 8;
+            int off = enc._fun.computeStackOffset(enc._code,src);
             if( dstX ) dst -= arm.D_OFFSET;
-            enc.add4(arm.load_str_imm(arm.OP_LOAD_IMM, off, arm.RSP, dst));
+            enc.add4(arm.load_str_imm(arm.OP_LOAD_IMM_64, off, arm.RSP, dst, 8));
             return;
         }
 
@@ -55,4 +57,10 @@ public class SplitARM extends SplitNode {
             enc.add4(arm.f_mov_general(arm.OP_FMOV, 0b01, 0, 0b110, src - arm.D_OFFSET, dst));
         }
     }
+
+    // General form: "mov  dst = src"
+    @Override public void asm(CodeGen code, SB sb) {
+        FunNode fun = code._encoding==null ? null : code._encoding._fun;
+        sb.p(code.reg(this,fun)).p(" = ").p(code.reg(in(1),fun));
+    }
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/StoreARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/StoreARM.java
index 2bab0ee..283263b 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/StoreARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/StoreARM.java
@@ -22,12 +22,18 @@ public class StoreARM extends MemOpARM {
     }
     @Override public String op() { return "st"+_sz; }
     @Override public RegMask outregmap() { return null; }
+
+    private static final int[] OP_STORES = new int[]{ arm.OP_STORE_IMM_8, arm.OP_STORE_IMM_16, arm.OP_STORE_IMM_32, arm.OP_STORE_IMM_64, };
+    private int imm_op() {
+        return _declaredType == SONTypeFloat.F32 ? arm.OPF_STORE_IMM_32
+            :  _declaredType == SONTypeFloat.F64 ? arm.OPF_STORE_IMM_64
+            :  OP_STORES[_declaredType.log_size()];
+    }
+
+    private static final int[] OP_STORE_RS = new int[]{ arm.OP_STORE_R_8, arm.OP_STORE_R_16, arm.OP_STORE_R_32, arm.OP_STORE_R_64, };
+
     @Override public void encoding( Encoding enc ) {
-        if(_declaredType == SONTypeFloat.F32 || _declaredType == SONTypeFloat.F64) {
-            ldst_encode(enc, arm.OPF_STORE_IMM,arm.OPF_STORE_R, val(), true);
-        } else {
-            ldst_encode(enc, arm.OP_STORE_IMM, arm.OP_STORE_R, val(), false);
-        }
+        ldst_encode(enc, imm_op(), OP_STORE_RS[_declaredType.log_size()], val(), size());
     }
     @Override public void asm(CodeGen code, SB sb) {
         asm_address(code,sb).p(",");
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SubARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SubARM.java
index 43e71fb..7cafadf 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SubARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SubARM.java
@@ -8,7 +8,7 @@ public class SubARM extends MachConcreteNode implements MachNode {
     SubARM(Node sub) { super(sub); }
     @Override public String op() { return "sub"; }
     @Override public RegMask regmap(int i) { return arm.RMASK; }
-    @Override public RegMask outregmap() { return arm.RMASK; }
+    @Override public RegMask outregmap() { return arm.WMASK; }
 
     @Override public void encoding( Encoding enc ) { arm.r_reg(enc,this,arm.OP_SUB); }
 
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SubIARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SubIARM.java
index b7ff3a9..2102c14 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SubIARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SubIARM.java
@@ -16,9 +16,11 @@ public class SubIARM extends MachConcreteNode implements MachNode {
     }
     @Override public String op() { return _imm == -1 ? "dec" : "subi"; }
     @Override public RegMask regmap(int i) { return arm.RMASK; }
-    @Override public RegMask outregmap() { return arm.RMASK; }
+    @Override public RegMask outregmap() { return arm.WMASK; }
 
-    @Override public void encoding( Encoding enc ) { arm.imm_inst(enc,this, in(1), 0b1101000100,_imm); }
+    @Override public void encoding( Encoding enc ) {
+        arm.imm_inst(enc,this, in(1), arm.OPI_SUB,_imm);
+    }
 
     // General form: "subi  rd = rs1 - imm"
     @Override public void asm(CodeGen code, SB sb) {
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/UJmpARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/UJmpARM.java
index 65e314f..6405475 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/UJmpARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/UJmpARM.java
@@ -42,7 +42,7 @@ public class UJmpARM extends CFGNode implements MachNode, RIPRelSize {
 
     @Override public void asm(CodeGen code, SB sb) {
         CFGNode target = uctrl();
-        assert target.nOuts() > 1; // Should optimize jmp to empty targets
+        //assert target.nOuts() > 1; // Should optimize jmp to empty targets
         sb.p(label(target));
     }
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/XorARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/XorARM.java
index 0132936..b8b5574 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/XorARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/XorARM.java
@@ -9,7 +9,7 @@ public class XorARM extends MachConcreteNode implements MachNode{
     @Override public String op() { return "xor"; }
     @Override public String glabel() { return "^"; }
     @Override public RegMask regmap(int i) { return arm.RMASK; }
-    @Override public RegMask outregmap() { return arm.RMASK; }
+    @Override public RegMask outregmap() { return arm.WMASK; }
     @Override public void encoding( Encoding enc ) { arm.r_reg(enc,this,arm.OP_XOR); }
     // General form: "rd = x1 ^ x2"
     @Override public void asm(CodeGen code, SB sb) {
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/XorIARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/XorIARM.java
index cb3a28d..cd72fbf 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/XorIARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/XorIARM.java
@@ -17,7 +17,7 @@ public class XorIARM extends MachConcreteNode implements MachNode {
 
     @Override public String op() { return "xori"; }
     @Override public RegMask regmap(int i) { return arm.RMASK; }
-    @Override public RegMask outregmap() { return arm.RMASK; }
+    @Override public RegMask outregmap() { return arm.WMASK; }
 
     // General form: "xori  rd = rs1 ^ imm"
     @Override public void encoding( Encoding enc ) { arm.imm_inst_n(enc,this, in(1), arm.OPI_XOR,_imm); }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/arm.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/arm.java
index 34db62e..928d2e6 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/arm.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/arm.java
@@ -4,7 +4,6 @@
 import com.compilerprogramming.ezlang.compiler.codegen.*;
 import com.compilerprogramming.ezlang.compiler.nodes.*;
 import com.compilerprogramming.ezlang.compiler.sontypes.*;
-import java.io.ByteArrayOutputStream;
 
 public class arm extends Machine {
     public arm( CodeGen code ) {
@@ -16,39 +15,33 @@ public arm( CodeGen code ) {
     @Override public String name() { return "arm"; }
 
     // GPR(S)
-    static final int X0  =  0,  X1  =  1,  X2  =  2,  X3  =  3,  X4  =  4,  X5  =  5,  X6  =  6,  X7  =  7;
-    static final int X8  =  8,  X9  =  9,  X10 = 10,  X11 = 11,  X12 = 12,  X13 = 13,  X14 = 14,  X15 = 15;
-    static final int X16 = 16,  X17 = 17,  X18 = 18,  X19 = 19,  X20 = 20,  X21 = 21,  X22 = 22,  X23 = 23;
-    static final int X24 = 24,  X25 = 25,  X26 = 26,  X27 = 27,  X28 = 28,  X29 = 29,  X30 = 30,  RSP = 31;
+    public static final int X0  =  0,  X1  =  1,  X2  =  2,  X3  =  3,  X4  =  4,  X5  =  5,  X6  =  6,  X7  =  7;
+    public static final int X8  =  8,  X9  =  9,  X10 = 10,  X11 = 11,  X12 = 12,  X13 = 13,  X14 = 14,  X15 = 15;
+    public static final int X16 = 16,  X17 = 17,  X18 = 18,  X19 = 19,  X20 = 20,  X21 = 21,  X22 = 22,  X23 = 23;
+    public static final int X24 = 24,  X25 = 25,  X26 = 26,  X27 = 27,  X28 = 28,  X29 = 29,  X30 = 30,  RSP = 31;
 
     // Floating point registers
-    static final int D0  = 32,  D1  = 33,  D2  = 34,  D3  = 35,  D4  = 36,  D5  = 37,  D6  = 38,  D7  = 39;
-    static final int D8  = 40,  D9  = 41,  D10 = 42,  D11 = 43,  D12 = 44,  D13 = 45,  D14 = 46,  D15 = 47;
-    static final int D16 = 48,  D17 = 49,  D18 = 50,  D19 = 51,  D20 = 52,  D21 = 53,  D22 = 54,  D23 = 55;
-    static final int D24 = 56,  D25 = 57,  D26 = 58,  D27 = 59,  D28 = 60,  D29 = 61,  D30 = 62,  D31 = 63;
+    public static final int D0  = 32,  D1  = 33,  D2  = 34,  D3  = 35,  D4  = 36,  D5  = 37,  D6  = 38,  D7  = 39;
+    public static final int D8  = 40,  D9  = 41,  D10 = 42,  D11 = 43,  D12 = 44,  D13 = 45,  D14 = 46,  D15 = 47;
+    public static final int D16 = 48,  D17 = 49,  D18 = 50,  D19 = 51,  D20 = 52,  D21 = 53,  D22 = 54,  D23 = 55;
+    public static final int D24 = 56,  D25 = 57,  D26 = 58,  D27 = 59,  D28 = 60,  D29 = 61,  D30 = 62,  D31 = 63;
 
-    static final int MAX_REG = 64;
-    static final int D_OFFSET = 31;
     static final int FLAGS = 64;
+    static final int MAX_REG = 65;
+    public static final int D_OFFSET = 32;
 
     static final String[] REGS = new String[] {
-            "X0",  "X1",  "X2",  "X3",  "X4",  "X5",  "X6",  "X7",
-            "X8",  "X9",  "X10", "X11", "X12", "X13", "X14", "X15",
-            "X16", "X17", "X18", "X19", "X20", "X21", "X22", "X23",
-            "X24", "X25", "X26", "X27", "X28", "X29", "RPC", "RSP",
-            "D0",  "D1",  "D2",  "D3",  "D4",  "D5",  "D6",  "D7",
-            "D8",  "D9",  "D10", "D11", "D12", "D13", "D14", "D15",
-            "D16", "D17", "D18", "D19", "D20", "D21", "D22", "D23",
-            "D24", "D25", "D26", "D27", "D28", "D29", "D30", "D31",
-            "flags"
+        "X0",  "X1",  "X2",  "X3",  "X4",  "X5",  "X6",  "X7",
+        "X8",  "X9",  "X10", "X11", "X12", "X13", "X14", "X15",
+        "X16", "X17", "X18", "X19", "X20", "X21", "X22", "X23",
+        "X24", "X25", "X26", "X27", "X28", "X29", "RPC", "RSP",
+        "D0",  "D1",  "D2",  "D3",  "D4",  "D5",  "D6",  "D7",
+        "D8",  "D9",  "D10", "D11", "D12", "D13", "D14", "D15",
+        "D16", "D17", "D18", "D19", "D20", "D21", "D22", "D23",
+        "D24", "D25", "D26", "D27", "D28", "D29", "D30", "D31",
+        "flags"
     };
-    @Override public String reg( int reg ) {
-        return reg < REGS.length ? REGS[reg] : "[rsp+"+(reg-REGS.length)*8+"]";
-    }
-    // Stack slots, in units of 8 bytes.
-    @Override public int stackSlot( int reg ) {
-        return reg < REGS.length ? -1 : reg-REGS.length;
-    }
+    @Override public String[] regs() { return REGS; }
 
     // from (x0-x30)
     // General purpose register mask: pointers and ints, not floats
@@ -64,11 +57,9 @@ public arm( CodeGen code ) {
     // Load/store mask; both GPR and FPR
     static final RegMask MEM_MASK = new RegMask(WR_BITS | FP_BITS);
 
-    static final RegMask SPLIT_MASK = new RegMask(WR_BITS | FP_BITS, -1L);
+    static final RegMask SPLIT_MASK = new RegMask(WR_BITS | FP_BITS, -2L/*skip flags*/);
 
     static final RegMask FLAGS_MASK = new RegMask(FLAGS);
-    //  x30 (LR): Procedure link register, used to return from subroutines.
-    static final RegMask RPC_MASK = new RegMask(1L << X30);
 
     // Arguments masks
     static final RegMask X0_MASK = new RegMask(X0);
@@ -93,9 +84,11 @@ public arm( CodeGen code ) {
     // major opcode: OP
     public static int OP_ADD       = 0b10_001_011;
     public static int OPF_ADD      = 0b00_011_110;
+    public static int OPF_OP_ADD   = 0b00_101_0;
     public static int OPI_ADD      = 0b10_010_00100;
 
-    public static int OP_UJMP      = 0b01010100;
+    public static int OPI_SUB      = 0b11_010_00100;
+    public static int OP_UJMP      = 0b000101;
 
     public static int OP_ADRP      = 0b10000;
 
@@ -105,7 +98,6 @@ public arm( CodeGen code ) {
     public static int OP_LSL       = 0b1000;
     public static int OPI_LSL       =0b1101001101;
 
-
     public static int OP_LSR        = 0b1001;
     public static int OPI_LSR       = 0b1101001101;
 
@@ -147,17 +139,39 @@ public arm( CodeGen code ) {
 
     public static int OP_RET       = 0b1101011001011111000000;
     // Store/load opcodes
-    public static int OP_LOAD_R    = 0b11111000011;
-    public static int OP_LOAD_IMM  = 0b1111100101;
+    public static int OP_LOAD_R_64    = 0b11111000011;
+    public static int OP_LOAD_R_32    = 0b10111000011;
+    public static int OP_LOAD_R_16    = 0b01111000010;
+    public static int OP_LOAD_R_8     = 0b00111000011;
+
+    public static int OP_LOAD_IMM_64  = 0b1111100101;
+    public static int OP_LOAD_IMM_32  = 0b1011100101;
+    public static int OP_LOAD_IMM_16  = 0b0111100101;
+    public static int OP_LOAD_IMM_8   = 0b0011100101;
+
+    // load
+    public static int OPF_LOAD_R_64    = 0b11111100011;
+    public static int OPF_LOAD_R_32    = 0b10111100011;
+
+    public static int OPF_LOAD_IMM_64  = 0b1111110101;
+    public static int OPF_LOAD_IMM_32  = 0b1011110101;
 
-    public static int OPF_LOAD_R    = 0b11111100011;
-    public static int OPF_LOAD_IMM  = 0b1111110101;
+    public static int OP_STORE_R_64    = 0b11111000001;
+    public static int OP_STORE_R_32    = 0b10111000001;
+    public static int OP_STORE_R_16    = 0b01111000001;
+    public static int OP_STORE_R_8     = 0b00111000001;
 
-    public static int OP_STORE_R    = 0b11111000001;
-    public static int OP_STORE_IMM  = 0b1111100100;
+    public static int OP_STORE_IMM_64  = 0b1111100100;
+    public static int OP_STORE_IMM_32  = 0b1011100100;
+    public static int OP_STORE_IMM_16  = 0b0111100100;
+    public static int OP_STORE_IMM_8   = 0b0011100100;
+
+    public static int OPF_STORE_R_64    = 0b11111100001;
+    public static int OPF_STORE_R_32    = 0b10111100001;
+
+    public static int OPF_STORE_IMM_32  = 0b1111110100;
+    public static int OPF_STORE_IMM_64  = 0b1111110100;
 
-    public static int OPF_STORE_R    = 0b11111100001;
-    public static int OPF_STORE_IMM  = 0b1111110100;
 
     public static int OP_FMOV        = 0b10011110;
     public static int OP_FMOV_REG    = 0b00011110;
@@ -167,24 +181,24 @@ public arm( CodeGen code ) {
     // for incoming argument idx.
     // index 0 for control, 1 for memory, real args start at index 2
     static final RegMask[] CALLINMASK = new RegMask[] {
-            X0_MASK,
-            X1_MASK,
-            X2_MASK,
-            X3_MASK,
-            X4_MASK,
-            X5_MASK,
-            X6_MASK,
-            X7_MASK,
+        X0_MASK,
+        X1_MASK,
+        X2_MASK,
+        X3_MASK,
+        X4_MASK,
+        X5_MASK,
+        X6_MASK,
+        X7_MASK,
     };
     static final RegMask[] XMMS = new RegMask[] {
-            D0_MASK,
-            D1_MASK,
-            D2_MASK,
-            D3_MASK,
-            D4_MASK,
-            D5_MASK,
-            D6_MASK,
-            D7_MASK,
+        D0_MASK,
+        D1_MASK,
+        D2_MASK,
+        D3_MASK,
+        D4_MASK,
+        D5_MASK,
+        D6_MASK,
+        D7_MASK,
     };
 
     // ARM ENCODING
@@ -218,17 +232,6 @@ public enum STORE_LOAD_OPTION {
         SXTX,  // base+ s64 index [<< logsize]
     }
 
-    static public String invert(String op) {
-        return switch (op) {
-            case "==" -> "!=";
-            case "!=" -> "==";
-            case ">" -> "<=";
-            case "<" -> ">=";
-            case ">=" -> "<";
-            case "<=" -> ">";
-            default -> throw new IllegalStateException("Unexpected value: " + op);
-        };
-    }
     public enum COND {
         EQ,
         NE,
@@ -269,10 +272,16 @@ private static int imm12Logical(SONTypeInteger ti) {
         if (val == 0 || val == -1) return -1; // Special cases are not allowed
         int immr = 0;
         // Rotate until we have 0[...]1
+
+        // Rotate until:
+        // The number is negative (MSB is 1)
+        // Or the LSB is not 1
         while (val < 0 || (val & 1)==0) {
+            // circular rotation
             val = (val >>> 63) | (val << 1);
             immr++;
         }
+        // Start by assuming that val might be made of two 32-bit chunks that repeat.
         int size = 32;
         long pattern = val;
         // Is upper half of pattern the same as the lower?
@@ -290,7 +299,7 @@ private static int imm12Logical(SONTypeInteger ti) {
         return (32-size)<<1 | immr << 6 | imms;
     }
 
-    // sh is encoded in opcdoe
+    // sh is encoded in opcode
     public static int imm_inst(int opcode, int imm12, int rn, int rd) {
         assert 0 <= rn && rn < 32;
         assert 0 <= rd && rd < 32;
@@ -307,10 +316,18 @@ public static int imm_shift(int opcode, int imm, int imms, int rn,  int rd)  {
     public static void imm_inst(Encoding enc, Node n,Node n2,  int opcode, int imm12) {
         short self = enc.reg(n);
         short reg1 = enc.reg(n2);
+
         int body = imm_inst(opcode, imm12&0xFFF, reg1, self);
         enc.add4(body);
     }
 
+    public static void imm_inst_subs(Encoding enc, Node n,Node n2,  int opcode, int imm12) {
+        short reg1 = enc.reg(n2);
+        // 31 = 11111
+        int body = imm_inst(opcode, imm12&0xFFF, reg1, 31);
+        enc.add4(body);
+    }
+
     // for cases where rs1 and dst are the same, eg add x0, x0, 1
     public static void imm_inst(Encoding enc, int opcode, int imm12, int self) {
         int body = imm_inst(opcode, imm12&0xFFF, self, self);
@@ -320,7 +337,6 @@ public static void imm_inst(Encoding enc, int opcode, int imm12, int self) {
     public static void imm_inst_n(Encoding enc, Node n, Node n2, int opcode, int imm13) {
         short self = enc.reg(n);
         short reg1 = enc.reg(n2);
-
         int body = imm_inst_n(opcode, imm13, reg1, self);
         enc.add4(body);
     }
@@ -336,7 +352,7 @@ public static int imm_inst_n(int opcode, int imm13, int rn, int rd) {
     public static int imm_inst_l(int opcode, int imm12, int self) {
         int body = imm_inst(opcode, imm12&0xFFF, self, self);
         return body;
-   }
+    }
 
     // for cases where rs1 and dst are the same, eg add x0, x0, 1
     public static int imm_inst_l(Encoding enc, Node n, int opcode, int imm12) {
@@ -352,14 +368,22 @@ public static int r_reg(int opcode, int shift, int rm, int imm6, int rn, int rd)
         assert 0 <= rm && rm < 32;
         assert 0 <= rn && rn < 32;
         assert 0 <= rd && rd < 32;
-        return (opcode << 24) | (shift << 21) | (rm << 16) | (imm6 << 10) << (rn << 5) | rd;
-   }
-   public static void r_reg(Encoding enc, Node n, int opcode) {
-       short self = enc.reg(n);
-       short reg1 = enc.reg(n.in(1));
-       short reg2 = enc.reg(n.in(2));
-       int body = r_reg(opcode, 0, reg2, 0,  reg1, self >= 32 ? reg1: self);
-       enc.add4(body);
+        return (opcode << 24) | (shift << 21) | (rm << 16) | (imm6 << 10) | (rn << 5) | rd;
+    }
+    public static void r_reg(Encoding enc, Node n, int opcode) {
+        short self = enc.reg(n);
+        short reg1 = enc.reg(n.in(1));
+        short reg2 = enc.reg(n.in(2));
+        int body = r_reg(opcode, 0, reg2, 0,  reg1, self >= 32 ? reg1: self);
+        enc.add4(body);
+    }
+
+    public static void r_reg_subs(Encoding enc, Node n, int opcode) {
+        short reg1 = enc.reg(n.in(1));
+        short reg2 = enc.reg(n.in(2));
+        // 31 = 11111
+        int body = r_reg(opcode, 0, reg2, 0,  reg1, 31);
+        enc.add4(body);
     }
 
     public static int shift_reg(int opcode, int rm, int op2, int rn, int rd) {
@@ -434,6 +458,7 @@ public static void f_scalar(Encoding enc, Node n, int op ) {
 
     // share same encoding with LDR (literal, SIMD&FP) flavour
     public static int load_pc(int opcode, int offset, int rt) {
+        offset>>=2;
         return (opcode << 24) | (offset << 5) | rt;
     }
     // int l
@@ -449,10 +474,15 @@ public static int indr_adr(int opcode, int off, STORE_LOAD_OPTION option, int s,
         return (opcode << 21) | (off << 16) | (option.ordinal() << 13) | (s << 12) | (2 << 10) | (ptr << 5) | rt;
     }
     // [Rptr+imm9]
-    public static int load_str_imm(int opcode, int imm12, int ptr, int rt) {
+    public static int load_str_imm(int opcode, int imm12, int ptr, int rt, int size) {
         assert 0 <= ptr && ptr < 32;
         assert 0 <= rt &&  rt  < 32;
-        return (opcode << 22) | (imm12 << 10)  |(ptr << 5) | rt;
+
+        if(size == 8) imm12 = imm12 >> 3;
+        if(size == 4) imm12 = imm12 >> 2;
+        if(size == 2) imm12 = imm12 >> 1;
+        // size == 1  imm12 = imm12
+        return (opcode << 22) | ((imm12) << 10)  | (ptr << 5) | rt;
     }
 
     // encoding for vcvt, size is encoded in operand
@@ -476,7 +506,8 @@ public static int float_cast(int opcode, int ftype, int rn, int rd) {
     public static int f_cmp(int opcode, int ftype, int rm, int rn) {
         assert 0 <= rn && rn  < 32;
         assert 0 <= rm && rm  < 32;
-        return (opcode  << 24) | (ftype << 21) | (rm << 16) | (8 << 10) | (rn << 5) | 8;
+        // Todo: |8 is not needed
+        return (opcode  << 24) | (ftype << 21) | (rm << 16) | (8 << 10) | (rn << 5);
     }
     public static void f_cmp(Encoding enc, Node n) {
         short reg1 = (short)(enc.reg(n.in(1))-D_OFFSET);
@@ -489,19 +520,21 @@ public static COND make_condition(String bop) {
         return switch (bop) {
         case "==" -> COND.EQ;
         case "!=" -> COND.NE;
-        case "<"  -> COND.LE;
-        case "<=" -> COND.LT;
-        case ">=" -> COND.GT;
-        case ">"  -> COND.GE;
+        case "<"  -> COND.LT;
+        case "<=" -> COND.LE;
+        case ">=" -> COND.GE;
+        case ">"  -> COND.GT;
         default   -> throw Utils.TODO();
         };
     }
-    // Todo: maybe missing zero here after delta << 5
+
     public static int b_cond(int opcode, int delta, COND cond) {
         // 24-5 == 19bits offset range
         assert -(1<<19) <= delta && delta < (1<<19);
+        assert (delta&3)==0;
+        delta>>=2;
         delta &= (1L<<19)-1;    // Zero extend
-        return (opcode << 24) | (delta << 5) | cond.ordinal();
+        return (opcode << 24) | ((delta)<< 5) | cond.ordinal();
     }
 
     public static int cond_set(int opcode, int rm, COND cond, int rn, int rd) {
@@ -517,12 +550,22 @@ public static int blr(int opcode, int rd) {
     }
     public static int b(int opcode, int delta) {
         assert -(1<<26) <= delta && delta < (1<<26);
+        assert (delta&3)==0;
+        delta>>=2;
+        delta &= (1L<<26)-1;    // Zero extend
+        return (opcode << 26) | delta;
+    }
+    // no aligned assert(assert (delta&3)==0;)
+    public static int b_calloc(int opcode, int delta) {
+        assert -(1<<26) <= delta && delta < (1<<26);
+        delta>>=2;
         delta &= (1L<<26)-1;    // Zero extend
         return (opcode << 26) | delta;
     }
 
+    @Override public RegMask callArgMask(SONTypeFunPtr tfp, int idx ) { return callInMask(tfp,idx); }
     static RegMask callInMask(SONTypeFunPtr tfp, int idx ) {
-        if( idx==0 ) return RPC_MASK;
+        if( idx==0 ) return CodeGen.CODE._rpcMask;
         if( idx==1 ) return null;
         // Count floats in signature up to index
         int fcnt=0;
@@ -540,63 +583,55 @@ static RegMask callInMask(SONTypeFunPtr tfp, int idx ) {
         }
         throw Utils.TODO(); // Pass on stack slot
     }
+
+    public static long decodeImm12(int imm12) {
+        int immr = (imm12 >> 6) & 0x3F;
+        int imms = imm12 & 0x3F;
+        int size;
+        if ((imm12 & 0x1000) != 0) {
+            size = 64;
+        } else {
+            size = 31-(imms >> 1);
+            size |= size >> 1;
+            size |= size >> 2;
+            size |= size >> 4;
+            size++;
+            imms &= ~((32-size) << 1);
+        }
+        long val = (2L << imms)-1;
+        while (size < 64) {
+            val |= val << size;
+            size <<= 1;
+        }
+        val = (val >>> immr) | val << (64-immr);
+        return val;
+    }
+
     // Return the max stack slot used by this signature, or 0
-    static short maxSlot( SONTypeFunPtr tfp ) {
-        // Count floats in signature up to index
-        int fcnt=0;
-        for( int i=0; i<tfp.nargs(); i++ )
-            if( tfp.arg(i) instanceof SONTypeFloat )
-                fcnt++;
-        if( fcnt >= XMMS.length )
-            throw Utils.TODO();
-        RegMask[] cargs = CALLINMASK;
-        if( tfp.nargs()-fcnt >= cargs.length )
-            throw Utils.TODO();
-        return 0;               // No stack args
-    }
-
-    static final long CALLEE_SAVE =
-            1L<<X19 |
-                    1L<<X20 | 1L<<X21 | 1L<<X22 | 1L<<X23 |
-                    1L<<X24 | 1L<<X25 | 1L<<X26 | 1L<<X27 |
-                    1L<<X28 |
-                    1L<<D9  | 1L<<D10 | 1L<<D11 |
-                    1L<<D12 | 1L<<D13 | 1L<<D14 | 1L<<D15;
-
-    static final RegMask CALLER_SAVE_MASK;
-    static {
-        long caller = ~CALLEE_SAVE;
-        caller &= ~(1L<<RSP);
-        CALLER_SAVE_MASK = new RegMask(caller,1L<<(FLAGS-64));
-    }
-    static RegMask armCallerSave() { return CALLER_SAVE_MASK; }
-    @Override public RegMask callerSave() { return armCallerSave(); }
-
-    static final RegMask CALLEE_SAVE_MASK = new RegMask(CALLEE_SAVE);
-    static RegMask armCalleeSave() { return CALLEE_SAVE_MASK; }
-    @Override public RegMask calleeSave() { return armCalleeSave(); }
-
-
-    // Return single int/ptr register.  Used by CallEnd output and Return input.
-    static final RegMask[] RET_MASKS;
-    static {
-        int nSaves = CALLEE_SAVE_MASK.size();
-        RET_MASKS = new RegMask[4 + nSaves];
-        RET_MASKS[0] = null;
-        RET_MASKS[1] = null;     // Memory
-        RET_MASKS[2] = null;     // Varies, either XMM0 or RAX
-        RET_MASKS[3] = RMASK;    // Expected R30 but could be any register
-        short reg = CALLEE_SAVE_MASK.firstReg();
-        for( int i=0; i<nSaves; i++ ) {
-            RET_MASKS[i+4] = new RegMask(reg);
-            reg = CALLEE_SAVE_MASK.nextReg(reg);
+    @Override public short maxArgSlot( SONTypeFunPtr tfp ) {
+        int icnt=0, fcnt=0;     // Count of ints, floats
+        for( int i=0; i<tfp.nargs(); i++ ) {
+            if( tfp.arg(i) instanceof SONTypeFloat ) fcnt++;
+            else icnt++;
         }
+        int nstk = Math.max(icnt-8,0)+Math.max(fcnt-8,0);
+        return (short)nstk;
     }
-    static RegMask retMask( SONTypeFunPtr tfp, int i ) {
-        return i==2
-                ? (tfp.ret() instanceof SONTypeFloat ? D0_MASK : X0_MASK)
-                : RET_MASKS[i];
+
+    private static final long CALLEE_SAVE =
+        1L<<X19 |
+        1L<<X20 | 1L<<X21 | 1L<<X22 | 1L<<X23 |
+        1L<<X24 | 1L<<X25 | 1L<<X26 | 1L<<X27 |
+        1L<<X28 |
+        1L<<D9  | 1L<<D10 | 1L<<D11 |
+        1L<<D12 | 1L<<D13 | 1L<<D14 | 1L<<D15;
+    static final long CALLER_SAVE = ~CALLEE_SAVE & ~(1L<<RSP);
+    @Override public long callerSave() { return CALLER_SAVE; }
+    @Override public long neverSave() { return 1L<<RSP; }
+    @Override public RegMask retMask( SONTypeFunPtr tfp ) {
+        return tfp.ret() instanceof SONTypeFloat ? D0_MASK : X0_MASK;
     }
+    @Override public int rpc() { return X30; }
 
     // Create a split op; any register to any register, including stack slots
     @Override public SplitNode split(String kind, byte round, LRG lrg) {  return new SplitARM(kind,round);  }
@@ -606,11 +641,6 @@ static RegMask retMask( SONTypeFunPtr tfp, int i ) {
         return new UJmpARM();
     }
 
-    // Break an infinite loop
-    @Override public NeverNode never(CFGNode ctrl ) {
-        throw Utils.TODO();
-    }
-
     // Instruction selection
     @Override public Node instSelect(Node n ) {
         return switch( n ) {
@@ -619,7 +649,7 @@ static RegMask retMask( SONTypeFunPtr tfp, int i ) {
         case AndNode and    -> and(and);
         case BoolNode bool  -> cmp(bool);
         case CallNode call  -> call(call);
-        case CastNode cast  -> new CastNode(cast);
+        case CastNode cast  -> new CastARM(cast);
         case CallEndNode cend -> new CallEndARM(cend);
         case CProjNode c    -> new CProjNode(c);
         case ConstantNode con -> con(con);
@@ -629,6 +659,7 @@ static RegMask retMask( SONTypeFunPtr tfp, int i ) {
         case IfNode iff     -> jmp(iff);
         case LoadNode ld    -> ld(ld);
         case MemMergeNode mem -> new MemMergeNode(mem);
+        case MinusNode neg  -> new NegARM(neg);
         case MulFNode mulf  -> new MulFARM(mulf);
         case MulNode mul    -> new MulARM(mul);
         case NewNode nnn    -> new NewARM(nnn);
@@ -658,16 +689,14 @@ static RegMask retMask( SONTypeFunPtr tfp, int i ) {
 
     private Node cmp(BoolNode bool){
         Node cmp = _cmp(bool);
-        return new SetARM(cmp, invert(bool.op()));
+        return new SetARM(cmp, IfNode.negate(bool.op()));
     }
     private Node _cmp(BoolNode bool) {
-        if( bool instanceof BoolNode.EQF ||
-            bool instanceof BoolNode.LTF ||
-            bool instanceof BoolNode.LEF )
+        if( bool.isFloat() )
             return new CmpFARM(bool);
         return bool.in(2) instanceof ConstantNode con && con._con instanceof SONTypeInteger ti
-            ? new CmpIARM(bool, (int)ti.value())
-            : new CmpARM(bool);
+                ? new CmpIARM(bool, (int)ti.value())
+                : new CmpARM(bool);
     }
 
     private Node ld(LoadNode ld) {
@@ -678,47 +707,49 @@ 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(SONTypeInteger.ZERO)));
-        return new BranchARM(iff, invert(((BoolNode)iff.in(1)).op()));
+        String op = "!=";
+        if( iff.in(1) instanceof BoolNode bool ) op = bool.op();
+        else if( iff.in(1)==null ) op = "=="; // Never-node cutout
+        else iff.setDef(1, new BoolNode.NE(iff.in(1), new ConstantNode(SONTypeInteger.ZERO)));
+        return new BranchARM(iff, op);
     }
 
     private Node add(AddNode add) {
         return add.in(2) instanceof ConstantNode off && off._con instanceof SONTypeInteger ti && imm12(ti)
-            ? new AddIARM(add, (int)ti.value())
-            : new AddARM(add);
+                ? new AddIARM(add, (int)ti.value())
+                : new AddARM(add);
     }
 
     private Node sub(SubNode sub) {
         return sub.in(2) instanceof ConstantNode con && con._con instanceof SONTypeInteger ti && imm12(ti)
-            ? new AddIARM(sub, (int)(-ti.value()))
-            : new SubARM(sub);
+                ? new SubIARM(sub, (int)(ti.value()))
+                : new SubARM(sub);
     }
 
-    private Node con(ConstantNode con) {
+    private Node con( ConstantNode con ) {
         if( !con._con.isConstant() ) return new ConstantNode( con ); // Default unknown caller inputs
         return switch( con._con ) {
-        case SONTypeInteger ti  -> new IntARM(con);
-        case SONTypeFloat   tf  -> new FloatARM(con);
-        case SONTypeFunPtr  tfp -> new TFPARM(con);
-        case SONTypeMemPtr tmp -> new ConstantNode(con);
-        case SONTypeNil tn  -> throw Utils.TODO();
-        // TOP, BOTTOM, XCtrl, Ctrl, etc.  Never any executable code.
-        case SONType t -> new ConstantNode(con);
+            case SONTypeInteger ti  -> new IntARM(con);
+            case SONTypeFloat   tf  -> new FloatARM(con);
+            case SONTypeFunPtr  tfp -> new TFPARM(con);
+            case SONTypeMemPtr tmp -> new ConstantNode(con);
+            case SONTypeNil tn  -> throw Utils.TODO();
+            // TOP, BOTTOM, XCtrl, Ctrl, etc.  Never any executable code.
+            case SONType t -> t==SONType.NIL ? new IntARM(con) : new ConstantNode(con);
         };
     }
 
     private Node call(CallNode call){
         return call.fptr() instanceof ConstantNode con && con._con instanceof SONTypeFunPtr tfp
-            ? new CallARM(call, tfp)
-            : new CallRRARM(call);
+                ? new CallARM(call, tfp)
+                : new CallRRARM(call);
     }
 
     private Node or(OrNode or) {
         int imm12;
         return or.in(2) instanceof ConstantNode off && off._con instanceof SONTypeInteger ti && (imm12 = imm12Logical(ti)) != -1
-            ? new OrIARM(or, imm12)
-            : new OrARM(or);
+                ? new OrIARM(or, imm12)
+                : new OrARM(or);
     }
 
     private Node xor(XorNode xor) {
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/BranchRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/BranchRISC.java
index 3fe6397..6c8c325 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/BranchRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/BranchRISC.java
@@ -21,11 +21,13 @@ public BranchRISC( IfNode iff, String bop, Node n1, Node n2 ) {
     @Override public String comment() { return "L"+cproj(1)._nid; }
     @Override public RegMask regmap(int i) { return riscv.RMASK; }
     @Override public RegMask outregmap() { return null; }
-    @Override public void invert() {
-        if( _bop.equals("<") || _bop.equals("<=") )
-            swap12();           // Cannot invert the test, so swap the operands
-        else
-            _bop = invert(_bop);
+    @Override public void negate() {
+        _bop = negate(_bop);
+        // Cannot encode ">" or "<=", so flip these
+        if( _bop.equals(">") || _bop.equals("<=") ) {
+            swap12();           // Cannot negate the test, so swap the operands
+            _bop = swap(_bop);  // Swap test to match
+        }
     }
 
     @Override public StringBuilder _print1(StringBuilder sb, BitSet visited) {
@@ -39,15 +41,21 @@ public BranchRISC( IfNode iff, String bop, Node n1, Node n2 ) {
     }
 
     @Override public void encoding( Encoding enc ) {
+        if( in(1)==null && _bop=="!=" ) return; // Inverted never-node, no code
         enc.jump(this,cproj(0));
-        // Todo: relocs (for offset - immf)
-        short src1 = enc.reg(in(1));
-        short src2 = in(2)==null ? (short)riscv.ZERO : enc.reg(in(2));
-        enc.add4(riscv.b_type(riscv.OP_BRANCH, riscv.jumpop(_bop), src1, src2, 0));
+        if( in(1)==null )       // Never node
+            enc.add4(riscv.j_type(riscv.OP_JAL, 0, 0));
+        else {
+            // Todo: relocs (for offset - immf)
+            short src1 = enc.reg(in(1));
+            short src2 = in(2)==null ? (short)riscv.ZERO : enc.reg(in(2));
+            enc.add4(riscv.b_type(riscv.OP_BRANCH, riscv.jumpop(_bop), src1, src2, 0));
+        }
     }
 
     // Delta is from opcode start
     @Override public byte encSize(int delta) {
+        if( in(1)==null && _bop=="!=" ) return 0; // Inverted never-node, no code
         if( -4*1024 <= delta && delta < 4*1024 ) return 4;
         // 2 word encoding needs a tmp register, must teach RA
         throw Utils.TODO();
@@ -55,16 +63,25 @@ public BranchRISC( IfNode iff, String bop, Node n1, Node n2 ) {
 
     // Delta is from opcode start
     @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) {
-        short src1 = enc.reg(in(1));
-        short src2 = in(2)==null ? (short)riscv.ZERO : enc.reg(in(2));
-        if( opLen==4 ) {
-            enc.patch4(opStart,riscv.b_type(riscv.OP_BRANCH, riscv.jumpop(_bop), src1, src2, delta));
+        assert !( in(1)==null && _bop=="!=" ); // Inverted never-node, no code no patch
+        if( in(1)==null ) {     // Never node
+            enc.patch4(opStart,riscv.j_type(riscv.OP_JAL, 0, delta));
         } else {
-            throw Utils.TODO();
+            short src1 = enc.reg(in(1));
+            short src2 = in(2)==null ? (short)riscv.ZERO : enc.reg(in(2));
+            if( opLen==4 ) {
+                enc.patch4(opStart,riscv.b_type(riscv.OP_BRANCH, riscv.jumpop(_bop), src1, src2, delta));
+            } else {
+                throw Utils.TODO();
+            }
         }
     }
 
     @Override public void asm(CodeGen code, SB sb) {
+        if( in(1)==null && _bop=="!=" ) {
+            sb.p("never");
+            return;
+        }
         String src1 = in(1)==null ? "#0" : code.reg(in(1));
         String src2 = in(2)==null ? "#0" : code.reg(in(2));
         sb.p(src1).p(" ").p(_bop).p(" ").p(src2).p(" ");
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CallEndRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CallEndRISC.java
index 32cbb5b..12f2b81 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CallEndRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CallEndRISC.java
@@ -1,23 +1,8 @@
 package com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv;
 
-import com.compilerprogramming.ezlang.compiler.SB;
-import com.compilerprogramming.ezlang.compiler.codegen.*;
 import com.compilerprogramming.ezlang.compiler.nodes.CallEndNode;
 import com.compilerprogramming.ezlang.compiler.nodes.MachNode;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFunPtr;
 
 public class CallEndRISC extends CallEndNode implements MachNode {
-    final SONTypeFunPtr _tfp;
-    CallEndRISC( CallEndNode cend ) {
-        super(cend);
-        _tfp = (SONTypeFunPtr)(cend.call().fptr()._type);
-    }
-    @Override public String op() { return "cend"; }
-    @Override public String label() { return op(); }
-    @Override public RegMask regmap(int i) { return null; }
-    @Override public RegMask outregmap() { return null; }
-    @Override public RegMask outregmap(int idx) { return idx == 2 ? riscv.retMask(_tfp,2) : null; }
-    @Override public RegMask killmap() { return riscv.riscCallerSave(); }
-    @Override public void encoding( Encoding enc ) { }
-    @Override public void asm(CodeGen code, SB sb) {  }
+    CallEndRISC( CallEndNode cend ) { super(cend); }
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CallRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CallRISC.java
index 5b4512f..292cf6a 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CallRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CallRISC.java
@@ -19,8 +19,6 @@ public class CallRISC extends CallNode implements MachNode, RIPRelSize {
     @Override public String op() { return "call"; }
     @Override public String label() { return op(); }
     @Override public RegMask regmap(int i) {
-        // Last call input is AUIPC
-        if( i == nIns()-1 ) return riscv.RMASK;
         return riscv.callInMask(_tfp,i);
     }
     @Override public RegMask outregmap() { return riscv.RPC_MASK; }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/FltRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/FltRISC.java
index 32e205d..f9dd6e6 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/FltRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/FltRISC.java
@@ -22,7 +22,7 @@ public class FltRISC extends ConstantNode implements MachNode, RIPRelSize {
         // AUIPC dst,#hi20_constant_pool
         enc.add4(riscv.u_type(riscv.OP_AUIPC, tmp, 0));
         // Load dst,[dst+#low12_constant_pool]
-        enc.add4(riscv.i_type(riscv.OP_LOAD, dst, 0b11, tmp, 0));
+        enc.add4(riscv.i_type(riscv.OP_LOADFP, dst, 0b11, tmp, 0));
     }
     @Override public RegMask killmap() { return new RegMask(riscv.T6); }
 
@@ -35,9 +35,9 @@ public class FltRISC extends ConstantNode implements MachNode, RIPRelSize {
         short dst = (short)(enc.reg(this) - riscv.F_OFFSET);
         short tmp = (short)riscv.T6;
         // AUIPC dst,#hi20_constant_pool
-        enc.patch4(opStart  , riscv.u_type(riscv.OP_AUIPC, tmp, delta));
+        enc.patch4(opStart  , riscv.u_type(riscv.OP_AUIPC, tmp, delta>>12));
         // Load dst,[dst+#low12_constant_pool]
-        enc.patch4(opStart+4, riscv.i_type(riscv.OP_LOAD, dst, 0b11, tmp, delta));
+        enc.patch4(opStart+4, riscv.i_type(riscv.OP_LOADFP, dst, 0b11, tmp, delta & 0xFFF));
     }
 
     @Override public void asm(CodeGen code, SB sb) {
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/FunRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/FunRISC.java
index c06a40c..b9c50ec 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/FunRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/FunRISC.java
@@ -1,36 +1,22 @@
 package com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv;
 
-import com.compilerprogramming.ezlang.compiler.codegen.*;
 import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.Utils;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
 import com.compilerprogramming.ezlang.compiler.nodes.FunNode;
 import com.compilerprogramming.ezlang.compiler.nodes.MachNode;
 
 public class FunRISC extends FunNode implements MachNode {
     FunRISC(FunNode fun) {super(fun);}
-    @Override public String op() { return "addi"; }
-    @Override public RegMask regmap(int i) { return null; }
-    @Override public RegMask outregmap() { return null; }
-    @Override public void postSelect(CodeGen code) { code.link(this);  }
-
-    // Actual stack layout is up to each CPU.
-    // X86, with too many args & spills:
-    // | CALLER |
-    // |  argN  | // slot 1, required by callER
-    // +--------+
-    // |  RPC   | // slot 0, required by callER
-    // | callee | // slot 3, callEE
-    // | callee | // slot 2, callEE
-    // |  PAD16 |
-    // +--------+
-    @Override public void encoding( Encoding enc ) {
-        // Stack frame size: _maxSlot - max(arg), padded to 16.
-        _maxArgSlot = riscv.maxSlot(enc._fun.sig());
-        _frameAdjust = (short) (_maxSlot+1 - _maxArgSlot);
-        if( _frameAdjust == 0 ) return; // Skip if no frame adjust
-        enc.add4(riscv.i_type(riscv.OP_IMM, riscv.SP, 0, riscv.SP, (_frameAdjust*8) & 0xFFF));
+    @Override public void computeFrameAdjust(CodeGen code, int maxReg) {
+        super.computeFrameAdjust(code,maxReg);
+        if( _hasCalls )         // If non-leaf, pad to 16b
+            _frameAdjust = ((_frameAdjust+8) & -16);
     }
-
-    @Override public void asm(CodeGen code, SB sb) {
-        sb.p("rsp += #").p(_frameAdjust*8);
+    @Override public void encoding( Encoding enc ) {
+        int sz = _frameAdjust;
+        if( sz == 0 ) return; // Skip if no frame adjust
+        if( sz >= 1L<<12 ) throw Utils.TODO();
+        enc.add4(riscv.i_type(riscv.OP_IMM, riscv.RSP, 0, riscv.RSP, -sz & 0xFFF));
     }
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/ImmRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/ImmRISC.java
index 9257b4d..150a8b9 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/ImmRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/ImmRISC.java
@@ -11,7 +11,7 @@ abstract public class ImmRISC extends MachConcreteNode implements MachNode {
     ImmRISC( Node add, int imm12, boolean pop ) {
         super(add);
         _imm12 = imm12;
-        // If pop, we are moving from a ideal Node to MachNode; the ideal Node
+        // If pop, we are moving from an ideal Node to MachNode; the ideal Node
         // inputs got copied here, but we are absorbing the immediate into the
         // MachNode and so do not want the constant input.
         if( pop ) _inputs.pop();
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/IntRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/IntRISC.java
index c230467..d01a11d 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/IntRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/IntRISC.java
@@ -4,6 +4,7 @@
 import com.compilerprogramming.ezlang.compiler.codegen.*;
 import com.compilerprogramming.ezlang.compiler.nodes.ConstantNode;
 import com.compilerprogramming.ezlang.compiler.nodes.MachNode;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
 import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeInteger;
 
 // 12-bit integer constant.  Larger constants are made up in the instruction
@@ -17,10 +18,10 @@ public class IntRISC extends ConstantNode implements MachNode {
     @Override public IntRISC copy() { return new IntRISC(this); }
     @Override public void encoding( Encoding enc ) {
         short dst  = enc.reg(this);
-        SONTypeInteger ti = (SONTypeInteger)_con;
+        int val = _con==SONType.NIL ? 0 : (int)(((SONTypeInteger)_con).value() & 0xFFF);
         // Explicit truncation of larger immediates; this will sign-extend on
         // load and this is handled during instruction selection.
-        enc.add4(riscv.i_type(riscv.OP_IMM, dst, 0, riscv.ZERO, (int)(ti.value() & 0xFFF)));
+        enc.add4(riscv.i_type(riscv.OP_IMM, dst, 0, riscv.ZERO, val));
     }
     @Override public void asm(CodeGen code, SB sb) {
         String reg = code.reg(this);
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/MemOpRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/MemOpRISC.java
index c66f079..90b6a49 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/MemOpRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/MemOpRISC.java
@@ -52,7 +52,7 @@ int func3() {
         // float
         if(_declaredType == SONTypeFloat.F32) func3 = 2; // fLW   fSW
         if(_declaredType == SONTypeFloat.F64) func3 = 3; // fLD   fSD
-        if( _declaredType instanceof SONTypeMemPtr ) func3=6; // 4 byte pointers, assumed unsigned?
+        if( _declaredType instanceof SONTypeMemPtr ) func3=3; // 8 byte pointers
         if( func3 == -1 ) throw Utils.TODO();
         return func3;
     }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/NJmpRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/NJmpRISC.java
deleted file mode 100644
index d7d6e2e..0000000
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/NJmpRISC.java
+++ /dev/null
@@ -1,46 +0,0 @@
-package com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv;
-
-import com.compilerprogramming.ezlang.compiler.*;
-import com.compilerprogramming.ezlang.compiler.codegen.*;
-import com.compilerprogramming.ezlang.compiler.nodes.*;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFunPtr;
-import java.util.BitSet;
-
-public class NJmpRISC extends NeverNode implements MachNode, RIPRelSize {
-    // label is obtained implicitly
-    public NJmpRISC( CFGNode cfg ) { super(cfg); }
-
-    @Override public String op() { return "jmp"; }
-    @Override public String label() { return op(); }
-    @Override public String comment() { return "L"+cproj(1)._nid; }
-    @Override public StringBuilder _print1( StringBuilder sb, BitSet visited ) { return sb.append("jmp "); }
-    @Override public RegMask regmap(int i) { throw Utils.TODO(); }
-    @Override public RegMask outregmap() { return null; }
-
-    @Override public void encoding( Encoding enc ) {
-        // Short form +/-4K:  beq r0,r0,imm12
-        // Long form:  auipc rX,imm20/32; jal r0,[rX+imm12/32]
-        enc.jump(this,cproj(0));
-        enc.add4(riscv.j_type(riscv.OP_JAL, 0, 0));
-    }
-
-    // Delta is from opcode start
-    @Override public byte encSize(int delta) {
-        if( -(1L<<20) <= delta && delta < (1L<<20) ) return 4;
-        // 2 word encoding needs a tmp register, must teach RA
-        throw Utils.TODO();
-    }
-
-    // Delta is from opcode start
-    @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) {
-        if( opLen==4 ) {
-            enc.patch4(opStart,riscv.j_type(riscv.OP_JAL, 0, delta));
-        } else {
-            throw Utils.TODO();
-        }
-    }
-
-    @Override public void asm(CodeGen code, SB sb) {
-        sb.p(label(cproj(0).uctrl()));
-    }
-}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/NegRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/NegRISC.java
new file mode 100644
index 0000000..32178d6
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/NegRISC.java
@@ -0,0 +1,20 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv;
+
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+public class NegRISC extends MachConcreteNode implements MachNode {
+    public NegRISC( Node neg ) { super(neg); }
+    @Override public String op() { return "neg"; }
+    @Override public RegMask regmap(int i) { assert i==1; return riscv.RMASK; }
+    @Override public RegMask outregmap() { return riscv.WMASK; }
+    @Override public void encoding( Encoding enc ) {
+        short dst = enc.reg(this );
+        short src = enc.reg(in(1));
+        riscv.r_type(riscv.OP,dst,0,0,src,0x20);
+    }
+    @Override public void asm(CodeGen code, SB sb) {
+        sb.p(code.reg(this)).p(" = -").p(code.reg(in(1)));
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/NewRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/NewRISC.java
index 8c3fbeb..71a9bce 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/NewRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/NewRISC.java
@@ -5,29 +5,42 @@
 import com.compilerprogramming.ezlang.compiler.nodes.*;
 import com.compilerprogramming.ezlang.compiler.print.ASMPrinter;
 
-public class NewRISC extends NewNode implements MachNode {
+public class NewRISC extends NewNode implements MachNode, RIPRelSize {
     // A pre-zeroed chunk of memory.
     NewRISC( NewNode nnn ) { super(nnn); }
-    @Override public String op() { return "alloc"; }
-    @Override public RegMask regmap(int i) {
-        return i==1 ? riscv.A0_MASK : null; // Size input or mem aliases
-    }
-    @Override public RegMask outregmap(int i) { return i == 1 ? riscv.A0_MASK : null; }
-    @Override public RegMask outregmap() { return null; }
-    @Override public RegMask killmap() { return riscv.riscCallerSave(); }
-
     @Override public void encoding( Encoding enc ) {
         // Generic external encoding; 2 ops.
         enc.external(this,"calloc");
         // A1 is a caller-save, allowed to crush building external address
         // auipc
-        enc.add4(riscv.u_type(riscv.OP_AUIPC, riscv.A1, 0));
-        enc.add4(riscv.i_type(riscv.OP_JALR, riscv.RPC, 0, riscv.A1, 0));
+        enc.add4(riscv.i_type(riscv.OP_IMM  , _arg2Reg , 0, riscv.ZERO, 1));
+        enc.add4(riscv.u_type(riscv.OP_AUIPC, riscv.A2 , 0));
+        enc.add4(riscv.i_type(riscv.OP_JALR , riscv.RPC, 0, riscv.A2, 0));
+    }
+
+    // Patch is for running "new" in a JIT.
+    // Delta is from opcode start
+    @Override public byte encSize(int delta) { return 4*3; }
+
+    // Patch is for running "new" in a JIT.
+    // Delta is from opcode start
+    @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) {
+        // Negative patches are JIT emulator targets.
+        if( opStart+delta < 0 ) {
+            delta -= 4;         // Offset from the AUIPC start
+            int imm20 = delta>>12;
+            if( ((delta>>11)&1)==1 ) imm20++; // Correct accidental sign extension
+            int imm12 = delta&0xFFF;
+            enc.patch4(opStart+4,riscv.u_type(riscv.OP_AUIPC, riscv.A2 , imm20));
+            enc.patch4(opStart+8,riscv.i_type(riscv.OP_JALR , riscv.RPC, 0, riscv.A2, imm12));
+        } else
+            throw Utils.TODO();
     }
 
     // General form: "alloc #bytes  PC"
     @Override public void asm(CodeGen code, SB sb) {
-        sb.p("auipc a1=#calloc\n");
-        sb.p("call  a1+#calloc, a0");
+        sb.p("ldi   a1=#1\n");
+        sb.p("auipc a2=#calloc\n");
+        sb.p("call  a2+#calloc, a0");
     }
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/NotRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/NotRISC.java
index 81b568a..5b705dc 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/NotRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/NotRISC.java
@@ -2,15 +2,19 @@
 
 import com.compilerprogramming.ezlang.compiler.SB;
 import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.MachConcreteNode;
+import com.compilerprogramming.ezlang.compiler.nodes.MachNode;
 import com.compilerprogramming.ezlang.compiler.nodes.NotNode;
 
-public class NotRISC extends ImmRISC {
-    NotRISC(NotNode not) { super(not,1); }
-    @Override public RegMask regmap(int i) { return riscv.RMASK; }
-    @Override public RegMask outregmap() { return riscv.RMASK;  }
+public class NotRISC extends MachConcreteNode implements MachNode {
+    public NotRISC(NotNode not) { super(not); }
     @Override public String op() { return "not"; }
-    // sltiu: x <u 1 ? 1 : 0;  only zero is unsigned-less-than 1
-    @Override int opcode() { return riscv.OP_IMM; }
-    @Override int func3() { return 2; }
+    @Override public RegMask regmap(int i) { return riscv.RMASK; }
+    @Override public RegMask outregmap()   { return riscv.WMASK; }
+    @Override public void encoding( Encoding enc ) {
+        short dst = enc.reg(this );
+        short src = enc.reg(in(1));
+        enc.add4(riscv.i_type(riscv.OP_IMM, dst, 2, src, 1));
+    }
     @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(this)); }
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/RetRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/RetRISC.java
index 0cf0e8d..f51c002 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/RetRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/RetRISC.java
@@ -9,42 +9,13 @@
 import com.compilerprogramming.ezlang.compiler.nodes.MachNode;
 
 public class RetRISC extends ReturnNode implements MachNode {
-    public RetRISC(ReturnNode ret, FunNode fun) {
-        super(ret, fun);
-        if( fun != null ) fun.setRet(this);
-    }
-    @Override public String op() {
-        return fun()._frameAdjust > 0 ? "addi" : "ret";
-    }
-    // Correct Nodes outside the normal edges
-    @Override public void postSelect(CodeGen code) {
-        FunNode fun = (FunNode)rpc().in(0);
-        _fun = fun;
-        fun.setRet(this);
-    }
-    @Override public RegMask regmap(int i) { return riscv.retMask(_fun.sig(),i); }
-    @Override public RegMask outregmap() { return null; }
+    public RetRISC(ReturnNode ret, FunNode fun) { super(ret, fun); fun.setRet(this); }
     @Override public void encoding( Encoding enc ) {
-        int frameAdjust = fun()._frameAdjust;
-        if( frameAdjust > 0 )
-            enc.add4(riscv.i_type(riscv.OP_IMM, riscv.SP, 0, riscv.SP, (frameAdjust*-8) & 0xFFF));
+        int sz = fun()._frameAdjust;
+        if( sz >= 1L<<12 ) throw Utils.TODO();
+        if( sz != 0 )
+            enc.add4(riscv.i_type(riscv.OP_IMM, riscv.RSP, 0, riscv.RSP, sz & 0xFFF));
         short rpc = enc.reg(rpc());
         enc.add4(riscv.i_type(riscv.OP_JALR, riscv.ZERO, 0, rpc, 0));
     }
-
-    @Override public void asm(CodeGen code, SB sb) {
-        int frameAdjust = fun()._frameAdjust;
-        if( frameAdjust>0 )
-            sb.p("rsp += #").p(frameAdjust*-8).p("\nret");
-        // Post code-gen, just print the "ret"
-        if( code._phase.ordinal() <= CodeGen.Phase.RegAlloc.ordinal() )
-            // Prints return reg (either A0 or FA0), RPC (always R1) and then
-            // the callee-save registers.
-            for( int i=2; i<nIns(); i++ )
-                sb.p(in(i)==null ? "---" : code.reg(in(i))).p("  ");
-
-        // Post-allocation, if we did not get the expected return register, print what we got
-        else if( code._regAlloc.regnum(rpc()) != riscv.RPC )
-            sb.p("[").p(code.reg(rpc())).p("]");
-    }
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SetFRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SetFRISC.java
index a56b1a2..1f5be13 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SetFRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SetFRISC.java
@@ -23,7 +23,7 @@ public class SetFRISC extends MachConcreteNode implements MachNode {
         short dst  =         enc.reg(this );
         short src1 = (short)(enc.reg(in(1))-riscv.F_OFFSET);
         short src2 = (short)(enc.reg(in(2))-riscv.F_OFFSET);
-        int body = riscv.r_type(riscv.OP_FP,dst,riscv.fsetop(_bop),src1,src2,0);
+        int body = riscv.r_type(riscv.OP_FP,dst,riscv.fsetop(_bop),src1,src2,0b1010001/*FCMP*/);
         enc.add4(body);
     }
 
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SplitRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SplitRISC.java
index 2c64a2e..a20a08f 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SplitRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SplitRISC.java
@@ -1,7 +1,9 @@
 package com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv;
 
+import com.compilerprogramming.ezlang.compiler.SB;
 import com.compilerprogramming.ezlang.compiler.Utils;
 import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.FunNode;
 import com.compilerprogramming.ezlang.compiler.nodes.Node;
 import com.compilerprogramming.ezlang.compiler.nodes.SplitNode;
 
@@ -22,18 +24,18 @@ public class SplitRISC extends SplitNode {
             if( src >= riscv.MAX_REG ) {
                 throw Utils.TODO(); // Very rare stack-stack move
             }
-            int off = enc._fun.computeStackSlot(dst - riscv.MAX_REG)*8;
+            int off = enc._fun.computeStackOffset(enc._code,dst);
             int op = srcX ? riscv.OP_STOREFP : riscv.OP_STORE;
             if( srcX ) src -= riscv.F_OFFSET;
-            enc.add4(riscv.s_type(op, 0b011, riscv.SP, src, off));
+            enc.add4(riscv.s_type(op, 0b011, riscv.RSP, src, off));
             return;
         }
         if( src >= riscv.MAX_REG ) {
             // Load from SP
-            int off = enc._fun.computeStackSlot(src - riscv.MAX_REG)*8;
+            int off = enc._fun.computeStackOffset(enc._code,src);
             int op = dstX ? riscv.OP_LOADFP : riscv.OP_LOAD;
             if( dstX ) dst -= riscv.F_OFFSET;
-            enc.add4(riscv.i_type(op, dst, 0b011, riscv.SP, off));
+            enc.add4(riscv.i_type(op, dst, 0b011, riscv.RSP, off));
             return;
         }
         // pick opcode based on regs
@@ -54,6 +56,11 @@ public class SplitRISC extends SplitNode {
             //fmv.x.d
             enc.add4(riscv.r_type(riscv.OP_FP, dst, 0, src - riscv.F_OFFSET, 0, 0b1110001));
         }
+    }
 
+    // General form: "mov  dst = src"
+    @Override public void asm(CodeGen code, SB sb) {
+        FunNode fun = code._encoding==null ? null : code._encoding._fun;
+        sb.p(code.reg(this,fun)).p(" = ").p(code.reg(in(1),fun));
     }
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/StoreRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/StoreRISC.java
index 7a76f55..d732933 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/StoreRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/StoreRISC.java
@@ -27,7 +27,7 @@ public class StoreRISC extends MemOpRISC {
         if( i==2 ) return riscv.RMASK; // ptr
         // 2 - index
         if( i==4 ) return riscv.MEM_MASK; // Wide mask to store GPR and FPR
-        throw Utils.TODO();
+        return null; // Anti-dependence
     }
     @Override public RegMask outregmap() { return null; }
 
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/UJmpRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/UJmpRISC.java
index 07959cf..7ddb3aa 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/UJmpRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/UJmpRISC.java
@@ -40,7 +40,7 @@ public class UJmpRISC extends CFGNode implements MachNode, RIPRelSize {
 
     @Override public void asm(CodeGen code, SB sb) {
         CFGNode target = uctrl();
-        assert target.nOuts() > 1; // Should optimize jmp to empty targets
+        //assert target.nOuts() > 1; // Should optimize jmp to empty targets
         sb.p(label(target));
     }
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/riscv.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/riscv.java
index c9adfeb..eb10fab 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/riscv.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/riscv.java
@@ -13,39 +13,51 @@ public riscv(CodeGen code) {
     }
 
     @Override public String name() {return "riscv";}
+    //@Override public int maxReg() { return MAX_REG; }
 
     // Using ABI names instead of register names
-    public static int ZERO =  0,  RPC=  1,  SP =  2,  S12=  3,  S13=  4,  T0 =  5,  T1 =  6,  T2 =  7;
+    public static int ZERO =  0,  RPC=  1,  RSP=  2,  S12=  3,  S13=  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
-    static int F0   = 32,  F1  = 33,  F2  = 34,  F3  = 35,  F4  = 36,  F5  = 37,  F6  = 38,  F7   = 39;
-    static int FS0  = 40,  FS1 = 41,  FA0 = 42,  FA1 = 43,  FA2 = 44,  FA3 = 45,  FA4 = 46,  FA5  = 47;
-    static int FA6  = 48,  FA7 = 49,  FS2 = 50,  FS3 = 51,  FS4 = 52,  FS5 = 53,  FS6 = 54,  FS7  = 55;
-    static int FS8  = 56,  FS9 = 57,  FS10 = 58, FS11 = 59, FT8 = 60,  FT9 = 61,  FT10 = 62, FT11 = 63;
+    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;
 
     static final int MAX_REG = 64;
 
-    static final int F_OFFSET = 32;
+    public static final int F_OFFSET = 32;
 
     static final String[] REGS = new String[] {
-            "zero","rpc"  , "sp"  , "s12" , "s13" , "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"
+        "zero","rpc"  , "rsp" , "s12" , "s13" , "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"
+        // Register-based naming hella easier to debug than "official" register
+        // names when single-stepping in debugger.
+        //"zero", "r1"  , "rsp" , "r3" , "r4" , "r5" , "r6" , "r7" ,
+        //"r8"  , "r9"  , "r10" , "r11", "r12", "r13", "r14", "r15",
+        //"r16" , "r17" , "r18" , "r19", "r20", "r21", "r22", "r23",
+        //"r24" , "r25" , "r26" , "r27", "r28", "r29", "r30", "r31",
+        //"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"
     };
+    @Override public String[] regs() { return REGS; }
 
     // General purpose register mask: pointers and ints, not floats
     static final long RD_BITS = 0b11111111111111111111111111111111L; // All the GPRs
     static final RegMask RMASK = new RegMask(RD_BITS);
 
-    static final long WR_BITS = 0b11111111111111111111111111111010L; // All the GPRs, minus ZERO and SP
+    static final long WR_BITS = 0b11111111111111111111111111111010L; // All the GPRs, minus ZERO and RSP
     static final RegMask WMASK = new RegMask(WR_BITS);
     // Float mask from(f0-ft10).  TODO: ft10,ft11 needs a larger RegMask
     static final long FP_BITS = 0b11111111111111111111111111111111L<<F0;
@@ -92,14 +104,14 @@ public riscv(CodeGen code) {
     };
 
     static RegMask[] XMMS = new RegMask[] {
-            FA0_MASK,
-            FA1_MASK,
-            FA2_MASK,
-            FA3_MASK,
-            FA4_MASK,
-            FA5_MASK,
-            FA6_MASK,
-            FA7_MASK
+        FA0_MASK,
+        FA1_MASK,
+        FA2_MASK,
+        FA3_MASK,
+        FA4_MASK,
+        FA5_MASK,
+        FA6_MASK,
+        FA7_MASK
     };
 
     // major opcode: OP
@@ -124,7 +136,7 @@ public riscv(CodeGen code) {
     public static int OP_JAL     = 0b11_011_11;
 
 
-    // Since riscv instructions are fixed we can just or them togehter
+    // Since riscv instructions are fixed we can just or them together
     public static int r_type(int opcode, int rd, int func3, int rs1, int rs2, int func7) {
         assert 0 <= rs1 && rs1 < 32;
         assert 0 <= rs2 && rs2 < 32;
@@ -224,7 +236,7 @@ static public int jumpop(String op) {
         case "=="  -> 0x0;
         case "!="  -> 0x1;
         case "<"   -> 0x4;
-        case "<="  -> 0x5;
+        case ">="  -> 0x5;
         case "u<"  -> 0x6;
         case "u<=" -> 0x7;
         default  ->  throw Utils.TODO();
@@ -241,6 +253,7 @@ static public int fsetop(String op) {
         };
     }
 
+    @Override public RegMask callArgMask( SONTypeFunPtr tfp, int idx ) { return callInMask(tfp,idx); }
     static RegMask callInMask( SONTypeFunPtr tfp, int idx ) {
         if( idx==0 ) return RPC_MASK;
         if( idx==1 ) return null;
@@ -258,90 +271,42 @@ static RegMask callInMask( SONTypeFunPtr tfp, int idx ) {
             if( idx-2-fcnt < cargs.length )
                 return cargs[idx-2-fcnt];
         }
-        throw Utils.TODO(); // Pass on stack slot
+        // Pass on stack slot(8 and higher)
+        return new RegMask(MAX_REG + 1 + (idx - 2));
     }
 
-    // Return the max stack slot used by this signature, or 0
-    static short maxSlot( SONTypeFunPtr tfp ) {
-        // Count floats in signature up to index
-        int fcnt=0;
-        for( int i=0; i<tfp.nargs(); i++ )
-            if( tfp.arg(i) instanceof SONTypeFloat )
-                fcnt++;
-        if( fcnt >= XMMS.length )
-            throw Utils.TODO();
-        RegMask[] cargs = CALLINMASK;
-        if( tfp.nargs()-fcnt >= cargs.length )
-            throw Utils.TODO();
-        return 0;               // No stack args
-    }
-
-    // callee saved(riscv)
-    static final long CALLEE_SAVE =
-        (1L<< S0) | (1L<< S1) | (1L<< S2 ) | (1L<< S3 ) |
-        (1L<< S4) | (1L<< S5) | (1L<< S6 ) | (1L<< S7 ) |
-        (1L<< S8) | (1L<< S9) | (1L<< S10) | (1L<< S11) |
-        (1L<<S12) | (1L<<S13) |
-        (1L<<FS0) | (1L<<FS1) | (1L<<FS2 ) | (1L<<FS3 ) |
-        (1L<<FS4) | (1L<<FS5) | (1L<<FS6 ) | (1L<<FS7 ) |
-        (1L<<FS8) | (1L<<FS9) | (1L<<FS10) | (1L<<FS11);
-
-    static final RegMask CALLER_SAVE_MASK;
-    static {
-        long caller = ~CALLEE_SAVE;
-        caller &= ~(1L<<SP);
-        CALLER_SAVE_MASK = new RegMask(caller,0);
-    }
-    static RegMask riscCallerSave() { return CALLER_SAVE_MASK; }
-    @Override public RegMask callerSave() { return riscCallerSave(); }
-
-    static final RegMask CALLEE_SAVE_MASK = new RegMask(CALLEE_SAVE);
-    static RegMask riscCalleeSave() { return CALLEE_SAVE_MASK; }
-    @Override public RegMask calleeSave() { return riscCalleeSave(); }
-
-    // Return single int/ptr register.  Used by CallEnd output and Return input.
-    static final RegMask[] RET_MASKS;
-    static {
-        int nSaves = CALLEE_SAVE_MASK.size();
-        RET_MASKS = new RegMask[4 + nSaves];
-        RET_MASKS[0] = null;
-        RET_MASKS[1] = null;     // Memory
-        RET_MASKS[2] = null;     // Varies, either XMM0 or RAX
-        RET_MASKS[3] = RMASK;    // Expected R1 but could be any register really
-        short reg = CALLEE_SAVE_MASK.firstReg();
-        for( int i=0; i<nSaves; i++ ) {
-            RET_MASKS[i+4] = new RegMask(reg);
-            reg = CALLEE_SAVE_MASK.nextReg(reg);
+    @Override public short maxArgSlot( SONTypeFunPtr tfp ) {
+        int icnt=0, fcnt=0;     // Count of ints, floats
+        for( int i=0; i<tfp.nargs(); i++ ) {
+            if( tfp.arg(i) instanceof SONTypeFloat ) fcnt++;
+            else icnt++;
         }
-    }
-    static RegMask retMask( SONTypeFunPtr tfp, int i ) {
-        return i==2
-            ? (tfp.ret() instanceof SONTypeFloat ? FA0_MASK : A0_MASK)
-            : RET_MASKS[i];
+        int nstk = Math.max(icnt-8,0)+Math.max(fcnt-8,0);
+        return (short)nstk;
     }
 
-    @Override public String reg( int reg ) {
-        return reg < REGS.length ? REGS[reg] : "[rsp+"+(reg-REGS.length)*8+"]";
-    }
+    static final long CALLER_SAVE =
+        (1L<<RPC) |
+        (1L<<A0 ) | (1L<<A1 ) | (1L<<A2 ) | (1L<<A3 ) | (1L<<A4 ) | (1L<<A5 ) | (1L<<A6 ) | (1L<<A7 ) |
+        (1L<<T0 ) | (1L<<T1 ) | (1L<<T2 ) | (1L<<T3 ) | (1L<<T4 ) | (1L<<T5 ) | (1L<<T6 ) |
+        (1L<<FA0) | (1L<<FA1) | (1L<<FA2) | (1L<<FA3) | (1L<<FA4) | (1L<<FA5) | (1L<<FA6) | (1L<<FA7) |
+        (1L<<F0 ) | (1L<<F1 ) | (1L<<F2 ) | (1L<<F3 ) | (1L<<F4 ) | (1L<<F5 ) | (1L<<F6 ) | (1L<<F7 ) |
+        (1L<<FT8) | (1L<<FT9) | (1L<<FT10)| (1L<<FT11);
 
-    // Stack slots, in units of 8 bytes.
-    @Override public int stackSlot( int reg ) {
-        return reg < REGS.length ? -1 : reg-REGS.length;
+    @Override public long callerSave() { return CALLER_SAVE; }
+    @Override public long  neverSave() { return (1L<<RSP) | (1L<<ZERO); }
+    @Override public RegMask retMask( SONTypeFunPtr tfp ) {
+        return tfp.ret() instanceof SONTypeFloat ? FA0_MASK : A0_MASK;
     }
-
+    @Override public int rpc() { return RPC; }
 
 
     // Return a MachNode unconditional branch
-    @Override public CFGNode jump() {
-        return new UJmpRISC();
-    }
+    @Override public CFGNode jump() { return new UJmpRISC(); }
 
     // Create a split op; any register to any register, including stack slots
     @Override  public SplitNode split(String kind, byte round, LRG lrg) { return new SplitRISC(kind,round);  }
 
-    // Break an infinite loop
-    @Override public NeverNode never( CFGNode ctrl ) { return new NJmpRISC(ctrl);}
-
     // True if signed 12-bit immediate
     public static boolean imm12(SONTypeInteger ti) {
         // 52 = 64-12
@@ -357,41 +322,42 @@ public static boolean imm20Exact(SONTypeInteger ti) {
 
     @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 CallNode call -> call(call);
-        case CastNode cast  -> new CastRISC(cast);
+        case AddFNode    addf -> addf(addf);
+        case AddNode      add -> add(add);
+        case AndNode      and -> and(and);
+        case BoolNode    bool -> cmp(bool);
+        case CallNode    call -> call(call);
+        case CastNode   cast  -> new CastRISC(cast);
         case CallEndNode cend -> new CallEndRISC(cend);
-        case CProjNode c -> new CProjNode(c);
+        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 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 -> new MulRISC(mul);
-        case NewNode nnn -> nnn(nnn);
-        case NotNode not -> new NotRISC(not);
-        case OrNode or -> or(or);
-        case ParmNode parm -> new ParmRISC(parm);
-        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 XorNode xor -> xor(xor);
+        case MinusNode    neg -> new NegRISC(neg);
+        case MulFNode    mulf -> new MulFRISC(mulf);
+        case MulNode      mul -> new MulRISC(mul);
+        case NewNode      nnn -> nnn(nnn);
+        case NotNode      not -> new NotRISC(not);
+        case OrNode        or -> or(or);
+        case ParmNode    parm -> new ParmRISC(parm);
+        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 XorNode      xor -> xor(xor);
 
         case LoopNode loop -> new LoopNode(loop);
         case RegionNode region -> new RegionNode(region);
@@ -404,7 +370,7 @@ private Node addf(AddFNode addf) {
     }
 
     private Node add(AddNode add) {
-        if( add.in(2) instanceof ConstantNode off && off._con instanceof SONTypeInteger ti && imm12(ti) )
+        if( add.in(2) instanceof ConstantNode off2 && off2._con instanceof SONTypeInteger ti && imm12(ti) )
             return new AddIRISC(add, (int)ti.value(),true);
         return new AddRISC(add);
     }
@@ -437,11 +403,11 @@ private Node cmp(BoolNode bool) {
 
         // Only < and <u are implemented in hardware.
         // x <  y - as-is
-        // x <= y - flip and invert; !(y < x); `slt tmp=y,x; xori dst=tmp,#1`
+        // x <= y - flip and negate; !(y < x); `slt tmp=y,x; xori dst=tmp,#1`
         // x == y - sub and vs0 == `sub tmp=x-y; sltu dst=tmp,#1`
 
         // x >  y - swap; y < x
-        // x >= y - swap and invert; !(x < y); `slt tmp=y,x;` then NOT.
+        // x >= y - swap and negate; !(x < y); `slt tmp=y,x;` then NOT.
         // x != y - sub and vs0 == `sub tmp=x-y; sltu dst=tmp,#1` then NOT.
 
         // The ">", ">=" and "!=" in Simple include a NotNode, which can be
@@ -451,8 +417,8 @@ private Node cmp(BoolNode bool) {
         return switch( bool.op() ) {
         case "<" -> bool.in(2) instanceof ConstantNode con && con._con instanceof SONTypeInteger ti && imm12(ti)
             ? new SetIRISC(bool, (int)ti.value(),false)
-            : new SetRISC(bool, false);
-        // x <= y - flip and invert; !(y < x); `slt tmp=y,x; xori dst=tmp,#1`
+            : new  SetRISC(bool, false);
+        // x <= y - flip and negate; !(y < x); `slt tmp=y,x; xori dst=tmp,#1`
         case "<=" -> new XorIRISC(new SetRISC(bool.swap12(), false),1);
         // x == y - sub and vs0 == `sub tmp=x-y; sltu dst=tmp,#1`
         case "==" -> new SetIRISC(new SubRISC(bool),1,true);
@@ -492,12 +458,12 @@ private Node jmp( IfNode iff ) {
         if( iff.in(1) instanceof BoolNode bool && !bool.isFloat() ) {
             // if less than or equal switch inputs
             String bop = bool.op();
-            if( bop.equals(">=") || bop.equals(">") )
-                return new BranchRISC(iff, IfNode.invert(bop), bool.in(2), bool.in(1));
+            if( bop.equals("<=") || bop.equals(">") )
+                return new BranchRISC(iff, IfNode.swap(bop), bool.in(2), bool.in(1));
             return new BranchRISC(iff, bop, bool.in(1), bool.in(2));
         }
         // Vs zero
-        return new BranchRISC(iff, "==", iff.in(1), null);
+        return new BranchRISC(iff, iff.in(1)==null ? "==" : "!=", iff.in(1), null);
     }
 
     private Node or(OrNode or) {
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/AddMemX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/AddMemX86.java
index 89dbcd9..3d51d5a 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/AddMemX86.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/AddMemX86.java
@@ -26,10 +26,9 @@ public class AddMemX86 extends MemOpX86 {
         x86_64_v2.indirectAdr(_scale, idx, ptr, _off, dst, enc);
     }
 
-    // General form: "add  dst = src + [base + idx<<2 + 12]"
+    // General form: "add  dst += [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(" + ");
+        sb.p(code.reg(this)).p(" += ");
         asm_address(code,sb);
     }
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CallEndX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CallEndX86.java
index 4cf1f22..1dfff37 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CallEndX86.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CallEndX86.java
@@ -1,23 +1,8 @@
 package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2;
 
-import com.compilerprogramming.ezlang.compiler.SB;
-import com.compilerprogramming.ezlang.compiler.codegen.*;
 import com.compilerprogramming.ezlang.compiler.nodes.CallEndNode;
 import com.compilerprogramming.ezlang.compiler.nodes.MachNode;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFunPtr;
 
 public class CallEndX86 extends CallEndNode implements MachNode {
-    final SONTypeFunPtr _tfp;
-    CallEndX86( CallEndNode cend ) {
-        super(cend);
-        _tfp = (SONTypeFunPtr)(cend.call().fptr()._type);
-    }
-    @Override public String op() { return "cend"; }
-    @Override public String label() { return op(); }
-    @Override public RegMask regmap(int i) { return null; }
-    @Override public RegMask outregmap() { return null; }
-    @Override public RegMask outregmap(int idx) { return idx == 2 ? x86_64_v2.retMask(_tfp,2) : null; }
-    @Override public RegMask killmap() { return x86_64_v2.x86CallerSave(); }
-    @Override public void encoding( Encoding enc ) { }
-    @Override public void asm(CodeGen code, SB sb) {  }
+    CallEndX86( CallEndNode cend ) { super(cend); }
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CallX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CallX86.java
index c18e77f..362f54e 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CallX86.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CallX86.java
@@ -36,7 +36,6 @@ public class CallX86 extends CallNode implements MachNode, RIPRelSize {
         enc.patch4(opStart+1, delta-5);
     }
 
-
     @Override public void asm(CodeGen code, SB sb) {
         sb.p(_name).p("  ");
         for( int i=0; i<nargs(); i++ )
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CmpIX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CmpIX86.java
index 02d4e55..b3f8f47 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CmpIX86.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CmpIX86.java
@@ -7,9 +7,9 @@
 // Compare immediate.  Sets flags.
 public class CmpIX86 extends MachConcreteNode implements MachNode {
     final int _imm;
-    CmpIX86( Node cmp, int imm ) {
+    CmpIX86( Node cmp, int imm, boolean swap ) {
         super(cmp);
-        _inputs.pop();
+        _inputs.del(swap ? 1 : 2);
         _imm = imm;
     }
     CmpIX86( CmpIX86 cmp ) {
@@ -18,11 +18,6 @@ public class CmpIX86 extends MachConcreteNode implements MachNode {
         addDef(cmp.in(1));
         _imm = cmp._imm;
     }
-    CmpIX86( Node lhs, int imm, double ignore ) {
-        super(lhs);
-        _inputs.del(1);
-        _imm = imm;
-    }
 
     @Override public String op() { return _imm==0 ? "test" : "cmp"; }
     @Override public RegMask regmap(int i) { return x86_64_v2.RMASK; }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CmpMemX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CmpMemX86.java
index 4744dc2..d2bd42a 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CmpMemX86.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CmpMemX86.java
@@ -3,12 +3,15 @@
 import com.compilerprogramming.ezlang.compiler.SB;
 import com.compilerprogramming.ezlang.compiler.codegen.*;
 import com.compilerprogramming.ezlang.compiler.nodes.*;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFloat;
 
 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 ) {
+    final boolean _swap;      // Op switched LHS, RHS
+    CmpMemX86( BoolNode bool, LoadNode ld, Node base, Node idx, int off, int scale, int imm, Node val, boolean swap ) {
         super(bool,ld, base, idx, off, scale, imm, val );
-        _invert = invert;
+        _swap = swap;
+        assert !_swap || val() == null || bool.op()=="==" || bool.op()=="!="; // Cannot swap with immediate
     }
     @Override public String op() { return ((val()==null && _imm==0) ? "test" : "cmp") + _sz; }
     @Override public RegMask outregmap() { return x86_64_v2.FLAGS_MASK; }
@@ -20,26 +23,56 @@ public class CmpMemX86 extends MemOpX86 {
         short idx = enc.reg(idx());
         short src = enc.reg(val());
 
-        enc.add1(x86_64_v2.rex(src, ptr, idx));
-        // opcode varies by immediate
-        enc.add1( src == -1 ? 0x81 : 0x39);
-
-        // includes modrm
-        x86_64_v2.indirectAdr(_scale, idx, ptr, _off, src == -1 ? 7 : src, enc);
+        if(src == -1) {
+            assert !_swap;
+            int log = _declaredType.log_size();
+            x86_64_v2.rexF(-1, ptr, idx, log==3, enc);
+            switch(log) {
+            case 0: enc.add1(0x80); break;
+            case 1: enc.add1(0x66).add1(0x81); break;
+            case 2: enc.add1(0x81); break;
+            case 3: enc.add1(0x81); break;
+            }
+            x86_64_v2.indirectAdr(_scale, idx, ptr, _off,  7, enc);
+            switch(log) {
+            case 0: enc.add1(_imm); break;
+            case 1: enc.add2(_imm); break;
+            case 2: enc.add4(_imm); break;
+            case 3: enc.add8(_imm); break;
+            }
+        } else {
+            encVal(enc, _declaredType, ptr, idx, src, _off, _scale, _swap);
+        }
+    }
 
-        if( src == -1 ) enc.add4(_imm);
+    static void encVal(Encoding enc, SONType decl, short ptr, short idx, short src, int off, int scale, boolean _swap) {
+        if(decl instanceof SONTypeFloat) {
+            src -= (short)x86_64_v2.XMM_OFFSET;
+            enc.add1(decl==SONTypeFloat.F32 ? 0xF3 : 0xF2).add1(0x0F).add1(0xC2);
+        } else {
+            int log = decl.log_size();
+            x86_64_v2.rexF(src, ptr, idx, log == 3, enc);
+            int swap = _swap ? 2 : 0;
+            switch(log) {
+            case 0: enc.add1(0x38+swap); break;
+            case 1: enc.add1(0x66).add1(0x39+swap); break;
+            case 2: enc.add1(0x39+swap); break;
+            case 3: enc.add1(0x39+swap); break;
+            }
+        }
+        x86_64_v2.indirectAdr(scale, idx, ptr, off, src, enc);
     }
 
     // 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( _swap ) 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(", "));
+        if( !_swap ) asm_address(code,sb.p(", "));
     }
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/FunX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/FunX86.java
index 27da886..0ab8794 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/FunX86.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/FunX86.java
@@ -1,44 +1,34 @@
 package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2;
 
 import com.compilerprogramming.ezlang.compiler.SB;
-import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.codegen.CodeGen;
+import com.compilerprogramming.ezlang.compiler.codegen.Encoding;
 import com.compilerprogramming.ezlang.compiler.nodes.FunNode;
 import com.compilerprogramming.ezlang.compiler.nodes.MachNode;
 
 public class FunX86 extends FunNode implements MachNode {
     FunX86( FunNode fun ) { super(fun); }
-    @Override public String op() { return "addi"; }
-    @Override public RegMask regmap(int i) { return null; }
-    @Override public RegMask outregmap() { return null; }
-    @Override public void postSelect(CodeGen code) { code.link(this);  }
+    @Override public void postSelect(CodeGen code) {
+        super.postSelect(code);
+        // One more for RPC pre-pushed; X86 special
+        _maxArgSlot++;
+    }
+
+    @Override public void computeFrameAdjust(CodeGen code, int maxReg) {
+        super.computeFrameAdjust(code,maxReg);
+        // Alignment, but off by RPC
+        if( _hasCalls )         // If non-leaf, pad to 16b
+            _frameAdjust = ((_frameAdjust+8+8) & -16)-8;
+    }
 
-    // Actual stack layout is up to each CPU.
-    // X86, with too many args & spills:
-    // | CALLER |
-    // |  argN  | // slot 1, required by callER
-    // +--------+
-    // |  RPC   | // slot 0, required by callER
-    // | callee | // slot 3, callEE
-    // | callee | // slot 2, callEE
-    // |  PAD16 |
-    // +--------+
     @Override public void encoding( Encoding enc ) {
-        // Stack frame size: _maxSlot - max(arg), padded to 16.
-        _maxArgSlot = x86_64_v2.maxSlot(enc._fun.sig());
-        int sz = _maxSlot - _maxArgSlot;
-        // If non-leaf function, pad to 16b
-        if( _hasCalls ) sz = ((sz+1) & -2)+1;
-        assert x86_64_v2.imm8(sz*8);
-        _frameAdjust = (short)sz;
+        int sz = _frameAdjust;
         if( sz == 0 ) return; // Skip if no frame adjust
-
         // opcode: 0x83, addi rsp with immediate 8
-        enc.add1( 0x83 );
-        enc.add1( x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, 0, x86_64_v2.RSP) );
-        enc.add1(sz*8);
+        enc.add1( x86_64_v2.REX_W ).add1( x86_64_v2.imm8(sz) ? 0x83 : 0x81 );
+        enc.add1( x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, 0b101, x86_64_v2.RSP) );
+        if( x86_64_v2.imm8(sz) )  enc.add1(sz);
+        else                      enc.add4(sz);
     }
 
-    @Override public void asm(CodeGen code, SB sb) {
-        sb.p("rsp += #").p(_frameAdjust*8);
-    }
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/JmpX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/JmpX86.java
index d6947bf..882b9a5 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/JmpX86.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/JmpX86.java
@@ -15,6 +15,7 @@ public class JmpX86 extends IfNode implements MachNode, RIPRelSize {
     @Override public String label() { return op(); }
     @Override public void postSelect(CodeGen code) {
         Node set = in(1);
+        if( set==null ) return; // Never-node cutout
         Node cmp = set.in(1);
         // Bypass an expected Set and just reference the cmp directly
         if( set instanceof SetX86 setx && (cmp instanceof CmpX86 || cmp instanceof CmpIX86 || cmp instanceof CmpMemX86 || cmp instanceof CmpFX86) ) {
@@ -25,38 +26,51 @@ public class JmpX86 extends IfNode implements MachNode, RIPRelSize {
     }
     @Override public RegMask regmap(int i) { assert i==1; return x86_64_v2.FLAGS_MASK; }
     @Override public RegMask outregmap() { return null; }
-    @Override public void invert() { _bop = invert(_bop);  }
+    @Override public void negate() { _bop = negate(_bop);  }
 
     @Override public void encoding( Encoding enc ) {
+        if( in(1)!=null ) {
+            int op = x86_64_v2.jumpop(_bop);
+            enc.add1(op-16).add1(0); // Short form jump
+        } else {
+            if( _bop=="!=" ) return; // Inverted, no code
+            enc.add1(0xEB).add1(0);  // Never-node
+        }
         enc.jump(this,cproj(0));
-        int op = x86_64_v2.jumpop(_bop);
-        enc.add1(op-16);      // Short form jump
-        enc.add1(0);          // Offset
     }
 
     // Delta is from opcode start, but X86 measures from the end of the 2-byte encoding
-    @Override public byte encSize(int delta) { return (byte)(x86_64_v2.imm8(delta-2) ? 2 : 6); }
+    @Override public byte encSize(int delta) {
+        if( in(1)==null && _bop=="!=" ) return 0; // Inverted never-node, no code
+        return (byte)(x86_64_v2.imm8(delta-2) ? 2 : 6);
+    }
 
     // Delta is from opcode start
     @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) {
+        assert !( in(1)==null && _bop=="!=" ); // Inverted never-node, no code no patch
         byte[] bits = enc.bits();
         if( opLen==2 ) {
-            assert bits[opStart] == x86_64_v2.jumpop(_bop)-16;
+            assert in(1)==null || bits[opStart] == x86_64_v2.jumpop(_bop)-16;
             delta -= 2;         // Offset from opcode END
             assert (byte)delta==delta;
             bits[opStart+1] = (byte)delta;
         } else {
-            assert bits[opStart] == x86_64_v2.jumpop(_bop)-16;
+            assert in(1)==null || bits[opStart] == x86_64_v2.jumpop(_bop)-16;
             delta -= 6;         // Offset from opcode END
             bits[opStart] = 0x0F;
-            bits[opStart+1] = (byte)x86_64_v2.jumpop(_bop);
+            bits[opStart+1] = (byte)(in(1)==null ? 0xE9 : x86_64_v2.jumpop(_bop));
             enc.patch4(opStart+2,delta);
         }
     }
 
     @Override public void asm(CodeGen code, SB sb) {
-        String src = code.reg(in(1));
-        if( src!="flags" ) sb.p(src).p(" ");
+        if( in(1)!=null ) {     // Never-node
+            String src = code.reg(in(1));
+            if( src!="flags" ) sb.p(src).p(" ");
+        } else if( _bop=="!=" ) {
+            sb.p("never");
+            return;
+        }
         CFGNode prj = cproj(0).uctrlSkipEmpty(); // 0 is True is jump target
         if( !prj.blockHead() ) prj = prj.cfg0();
         sb.p(label(prj));
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/LoadX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/LoadX86.java
index 0412888..c35e176 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/LoadX86.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/LoadX86.java
@@ -27,20 +27,22 @@ public class LoadX86 extends MemOpX86 {
     }
 
     static void enc( Encoding enc, SONType decl, short dst, short ptr, short idx, int off, int scale ) {
-        if( decl != SONTypeInteger.U32 && decl != SONTypeFloat.F32 && decl != SONTypeFloat.F64 )
-            enc.add1(x86_64_v2.rex(dst, ptr, idx));
+        if( decl == SONTypeFloat.F32) enc.add1(0xF3);
+        if( decl == SONTypeFloat.F64) enc.add1(0xF2);
 
         if( decl.isa(SONTypeFloat.F64) )
             dst -= (short)x86_64_v2.XMM_OFFSET;
 
+        x86_64_v2.rexF(dst, ptr, idx, decl != SONTypeInteger.U32 && decl != SONTypeFloat.F32 && decl != SONTypeFloat.F64, enc);
+
         if( false ) ;
-        else if( decl == SONTypeFloat.F32  ) enc.add1(0xF3).add1(0x0F).add1(0x10); // F3 0F 10 /r MOVSS xmm1, m32
-        else if( decl == SONTypeFloat.F64  ) enc.add1(0xF2).add1(0x0F).add1(0x10); // F2 0F 10 /r MOVSD xmm1, m64
-        else if( decl == SONTypeInteger.I8 ) enc.add1(0x0F).add1(0xBE); // sign extend: REX.W + 0F BE /r MOVSX r64, r/m8
-        else if( decl == SONTypeInteger.I16) enc.add1(0x0F).add1(0xBF); // sign extend: REX.W + 0F BF /r MOVSX r64, r/m16
-        else if( decl == SONTypeInteger.I32) enc.add1(0x63);            // sign extend: REX.W + 63 /r    MOVSXD r64, r/m32
-        else if( decl == SONTypeInteger.U8 ) enc.add1(0x0F).add1(0xB6); // zero extend: REX.W + 0F B6 /r MOVZX r64, r/m8
-        else if( decl == SONTypeInteger.U16) enc.add1(0x0F).add1(0xB7); // zero extend: REX.W + 0F B7 /r MOVZX r64, r/m16
+        else if( decl == SONTypeFloat.F32   ) enc.add1(0x0F).add1(0x10); // F3 0F 10 /r MOVSS xmm1, m32
+        else if( decl == SONTypeFloat.F64   ) enc.add1(0x0F).add1(0x10); // F2 0F 10 /r MOVSD xmm1, m64
+        else if( decl.isa(SONTypeInteger.I8)) enc.add1(0x0F).add1(0xBE); // sign extend: REX.W + 0F BE /r MOVSX r64, r/m8
+        else if( decl == SONTypeInteger.I16 ) enc.add1(0x0F).add1(0xBF); // sign extend: REX.W + 0F BF /r MOVSX r64, r/m16
+        else if( decl == SONTypeInteger.I32 ) enc.add1(0x63);            // sign extend: REX.W + 63 /r    MOVSXD r64, r/m32
+        else if( decl == SONTypeInteger.U8  ) enc.add1(0x0F).add1(0xB6); // zero extend: REX.W + 0F B6 /r MOVZX r64, r/m8
+        else if( decl == SONTypeInteger.U16 ) enc.add1(0x0F).add1(0xB7); // zero extend: REX.W + 0F B7 /r MOVZX r64, r/m16
         // Covers U32, I64/BOT, TMP
         else if( decl.log_size()>=2 )     enc.add1(0x8B);            // zero extend:         8B /r    MOV r32, r/m32
         else throw Utils.TODO();
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/MemAddX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/MemAddX86.java
index 1096341..a0edf38 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/MemAddX86.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/MemAddX86.java
@@ -23,7 +23,7 @@ public class MemAddX86 extends MemOpX86 {
 
         enc.add1(x86_64_v2.rex(src, ptr, idx));
         // opcode
-        enc.add1( src == -1 ? 0x01 : 0x81 );
+        enc.add1( src == -1 ? 0x81: 0x01);
 
         // includes modrm
         x86_64_v2.indirectAdr(_scale, idx, ptr, _off, src == -1 ? 0 : src, enc);
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/MemOpX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/MemOpX86.java
index dde6bca..e6e064d 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/MemOpX86.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/MemOpX86.java
@@ -23,13 +23,13 @@ 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()));
+    final char _sz;             // Handy print name
     MemOpX86( Node op, MemOpNode mop, Node base, Node idx, int off, int scale, int imm ) {
         super(op,mop);
         assert base._type instanceof SONTypeMemPtr && !(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
+        // Copy memory parts from e.g. 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
@@ -41,6 +41,7 @@ public abstract class MemOpX86 extends MemOpNode implements MachNode {
         _off = off;
         _scale = scale;
         _imm = imm;
+        _sz = (char)('0'+(1<<_declaredType.log_size()));
     }
 
     // Store-based flavors have a value edge
@@ -65,8 +66,10 @@ public abstract class MemOpX86 extends MemOpNode implements MachNode {
         if( i==1 ) return null;               // Memory
         if( i==2 ) return x86_64_v2.RMASK;    // base  in GPR
         if( i==3 ) return x86_64_v2.RMASK;    // index in GPR
-        if( i==4 ) return x86_64_v2.MEM_MASK; // value in GPR or XMM
-        throw Utils.TODO();
+        if( i==4 ) return _sz >= 2
+                       ? x86_64_v2.MEM_MASK   // value in GPR or XMM
+                       : x86_64_v2.RMASK;     // Bytes and shorts in GPR only
+        return null; // Anti-dependence
     }
 
     // "[base + idx<<2 + 12]"
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/NegX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/NegX86.java
new file mode 100644
index 0000000..68be129
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/NegX86.java
@@ -0,0 +1,21 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2;
+
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+
+public class NegX86 extends MachConcreteNode implements MachNode {
+    NegX86(MinusNode not) { super(not); }
+    @Override public String op() { return "neg"; }
+    @Override public RegMask regmap(int i) { return x86_64_v2.RMASK; }
+    @Override public RegMask outregmap()   { return x86_64_v2.RMASK; }
+    @Override public RegMask killmap()     { return x86_64_v2.FLAGS_MASK; }
+
+    @Override public void encoding( Encoding enc ) {
+        short dst = enc.reg(this );
+        enc.add1(x86_64_v2.rex(0, dst, 0));
+        enc.add1(0xF7); // opcode
+        enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, 3, dst));
+    }
+    @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(this)); }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/NewX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/NewX86.java
index 6d5639b..aa81518 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/NewX86.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/NewX86.java
@@ -3,28 +3,23 @@
 import com.compilerprogramming.ezlang.compiler.*;
 import com.compilerprogramming.ezlang.compiler.codegen.*;
 import com.compilerprogramming.ezlang.compiler.nodes.*;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFunPtr;
 
 public class NewX86 extends NewNode implements MachNode {
     // A pre-zeroed chunk of memory.
     NewX86( NewNode nnn ) { super(nnn); }
-    @Override public String op() { return "alloc"; }
-    // Size and pointer result in standard calling convention; null for all the
-    // memory aliases edges
-    @Override public RegMask    regmap(int i) { return i == 1 ? x86_64_v2.RDI_MASK : null; }
-    @Override public RegMask outregmap(int i) { return i == 1 ? x86_64_v2.RAX_MASK : null; }
-    @Override public RegMask outregmap() { return null; }
-    @Override public RegMask killmap() { return x86_64_v2.x86CallerSave(); }
-
     @Override public void encoding( Encoding enc ) {
         enc.external(this,"calloc");
+        // ldi rcx,#1 // number of elements to calloc
+        enc.add1(0xB8 + _arg2Reg).add4(1);
         // E8 cd    CALL rel32;
         enc.add1(0xE8);
         enc.add4(0);            // offset
     }
-
-    // General form: "alloc #bytes"
+    // General form: "alloc #bytes  PC"
     @Override public void asm(CodeGen code, SB sb) {
-        sb.p("#calloc, ").p(code.reg(size()));
+        sb.p("ldi   rcx = #1\n");
+        sb.p("call  #calloc");
     }
 
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/RetX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/RetX86.java
index ce4d36f..5591405 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/RetX86.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/RetX86.java
@@ -6,36 +6,14 @@
 
 public class RetX86 extends ReturnNode implements MachNode {
     RetX86( ReturnNode ret, FunNode fun ) { super(ret, fun); fun.setRet(this); }
-    @Override public String op() {
-        return ((FunX86)fun())._frameAdjust > 0 ? "addi" : "ret";
-    }
-    // Correct Nodes outside the normal edges
-    @Override public void postSelect(CodeGen code) {
-        FunNode fun = (FunNode)rpc().in(0);
-        _fun = fun;
-        fun.setRet(this);
-    }
-    @Override public RegMask regmap(int i) { return x86_64_v2.retMask(_fun.sig(),i); }
-    @Override public RegMask outregmap() { return null; }
     @Override public void encoding( Encoding enc ) {
-        int frameAdjust = ((FunX86)fun())._frameAdjust;
-        if( frameAdjust > 0 ) {
-            enc.add1( 0x83 );
-            enc.add1( x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, 0, x86_64_v2.RSP) );
-            enc.add1(frameAdjust*-8);
+        int sz = fun()._frameAdjust;
+        if( sz != 0 ) {
+            enc.add1( x86_64_v2.REX_W ).add1( x86_64_v2.imm8(sz) ? 0x83 : 0x81 );
+            enc.add1( x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, 0b000, x86_64_v2.RSP) );
+            if( x86_64_v2.imm8(sz) )  enc.add1(sz);
+            else                      enc.add4(sz);
         }
         enc.add1(0xC3);
     }
-
-    @Override public void asm(CodeGen code, SB sb) {
-        int frameAdjust = ((FunX86)fun())._frameAdjust;
-        if( frameAdjust>0 )
-            sb.p("rsp += #").p(frameAdjust*-8).p("\nret");
-        // Post code-gen, just print the "ret"
-        if( code._phase.ordinal() <= CodeGen.Phase.RegAlloc.ordinal() )
-            // Prints return reg (either RAX or XMM0), RPC (always [rsp-4]) and
-            // then the callee-save registers.
-            for( int i=2; i<nIns(); i++ )
-                sb.p(code.reg(in(i))).p("  ");
-    }
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/SarIX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/SarIX86.java
index 656e84a..b6481f6 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/SarIX86.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/SarIX86.java
@@ -20,6 +20,7 @@ public class SarIX86 extends MachConcreteNode implements MachNode {
 
     @Override public RegMask regmap(int i) { return x86_64_v2.RMASK; }
     @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
+    @Override public int twoAddress() { return 1; }
 
     @Override public void encoding(Encoding enc) {
         short dst = enc.reg(this); // Also src1
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/SetX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/SetX86.java
index df76f45..ae9ff53 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/SetX86.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/SetX86.java
@@ -33,6 +33,7 @@ public class SetX86 extends MachConcreteNode implements MachNode {
             case "==" -> 0x94;  // SETE
             case "<"  -> _unsigned ? 0x92 : 0x9C;  // SETB /SETL
             case "<=" -> _unsigned ? 0x96 : 0x9E;  // SETBE/SETLE
+            case ">=" -> _unsigned ? 0x93 : 0x9D;  // SETAE/SETGE
             default -> throw Utils.TODO();
             });
         enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, 0, dst));
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/ShlIX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/ShlIX86.java
index 319a303..26c5c6e 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/ShlIX86.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/ShlIX86.java
@@ -19,6 +19,7 @@ public class ShlIX86 extends MachConcreteNode implements MachNode {
 
     @Override public RegMask regmap(int i) { return x86_64_v2.RMASK; }
     @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
+    @Override public int twoAddress() { return 1; }
 
     @Override public String op() { return "shli"; }
     @Override public String glabel() { return "<<"; }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/ShrIX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/ShrIX86.java
index dd0f9cb..44d0caa 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/ShrIX86.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/ShrIX86.java
@@ -16,6 +16,7 @@ public class ShrIX86 extends MachConcreteNode implements MachNode {
 
     @Override public RegMask regmap(int i) { return x86_64_v2.RMASK; }
     @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
+    @Override public int twoAddress() { return 1; }
 
     @Override public void encoding(Encoding enc) {
         short dst = enc.reg(this); // Also src1
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/SplitX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/SplitX86.java
index fefefc3..058df1e 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/SplitX86.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/SplitX86.java
@@ -3,6 +3,7 @@
 import com.compilerprogramming.ezlang.compiler.SB;
 import com.compilerprogramming.ezlang.compiler.Utils;
 import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.FunNode;
 import com.compilerprogramming.ezlang.compiler.nodes.Node;
 import com.compilerprogramming.ezlang.compiler.nodes.SplitNode;
 import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
@@ -26,7 +27,7 @@ public class SplitX86 extends SplitNode {
             // push rcx
             // popf (Pop the top of the stack into the FLAGS register)
             // 50+rd	PUSH r64
-            if( src >= 8 ) x86_64_v2.rex(0,src,0,true);
+            x86_64_v2.rexF(0,src,0,false,enc);
             enc.add1(0x50 + src);
             // popf
             enc.add1(0x9D);
@@ -37,7 +38,7 @@ public class SplitX86 extends SplitNode {
             // pushf; pop reg
             enc.add1(0x9C);
             // 58+ rd	POP r64
-            if( dst >= 8 ) x86_64_v2.rex(0,dst,0,true);
+            x86_64_v2.rexF(0,dst,0,false, enc);
             enc.add1(0x58 + dst);
             return;
         }
@@ -50,12 +51,12 @@ public class SplitX86 extends SplitNode {
         if( dst >= x86_64_v2.MAX_REG ) {
             if( src >= x86_64_v2.MAX_REG )
                 throw Utils.TODO(); // Very rare stack-stack move
-            int off = enc._fun.computeStackSlot(dst - x86_64_v2.MAX_REG)*8;
+            int off = enc._fun.computeStackOffset(enc._code,dst);
             StoreX86.encVal(enc, srcX ? SONTypeFloat.F64 : SONTypeInteger.BOT, (short)x86_64_v2.RSP, (short)-1/*index*/, src, off, 0);
             return;
         }
         if( src >= x86_64_v2.MAX_REG ) {
-            int off = enc._fun.computeStackSlot(src - x86_64_v2.MAX_REG)*8;
+            int off = enc._fun.computeStackOffset(enc._code,src);
             LoadX86.enc(enc, dstX ? SONTypeFloat.F64 : SONTypeInteger.BOT, dst, (short)x86_64_v2.RSP, (short)-1, off, 0);
             return;
         }
@@ -66,6 +67,8 @@ public class SplitX86 extends SplitNode {
 
         // 0x66 if moving between register classes
         if( dstX ^ srcX )  enc.add1(0x66);
+        if( !dstX && srcX )
+            { short tmp=src; src=dst; dst=tmp; }
         enc.add1(x86_64_v2.rex(dst, src, 0));
 
         // pick opcode based on regs
@@ -84,7 +87,6 @@ public class SplitX86 extends SplitNode {
             // xmm->reg(66 0F 7E /r MOVQ r/m64, xmm)
             enc.add1(0x0F);
             enc.add1(0x7E);
-            short tmp=src; src=dst; dst=tmp;
         }
 
         enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, src));
@@ -92,6 +94,7 @@ public class SplitX86 extends SplitNode {
 
     // General form: "mov  dst = src"
     @Override public void asm(CodeGen code, SB sb) {
-        sb.p(code.reg(this)).p(" = ").p(code.reg(in(1)));
+        FunNode fun = code._encoding==null ? null : code._encoding._fun;
+        sb.p(code.reg(this,fun)).p(" = ").p(code.reg(in(1),fun));
     }
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/StoreX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/StoreX86.java
index 5728951..49ec2bb 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/StoreX86.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/StoreX86.java
@@ -31,16 +31,20 @@ public class StoreX86 extends MemOpX86 {
         short src = enc.reg(val());
 
         if( src == -1 ) {
-            // return opcode for optimised immediate store
-            if( x86_64_v2.imm8(_imm) ) enc.add1(0xC6);
-            else if( x86_64_v2.imm32(_imm) ) enc.add1(0xC7);
-            else enc.add1(x86_64_v2.rex(-1, ptr, idx)).add1(0xC7);
+            int log = _declaredType.log_size();
+            x86_64_v2.rexF(-1, ptr, idx, log==3, enc);
+            switch( log ) {
+            case 0: enc           .add1(0xC6); break;
+            case 1: enc.add1(0x66).add1(0xC7); break;
+            case 2: enc           .add1(0xC7); break;
+            case 3: enc           .add1(0xC7); break;
+            }
             x86_64_v2.indirectAdr(_scale, idx, ptr, _off, src, enc);
-            switch (x86_64_v2.imm_size(_imm)) {
-            case  8: enc.add1(_imm); break;
-            case 16: enc.add2(_imm); break;
-            case 32: enc.add4(_imm); break;
-            case 64: enc.add8(_imm); break;
+            switch( log ) {
+            case 0: enc.add1(_imm); break;
+            case 1: enc.add2(_imm); break;
+            case 2: enc.add4(_imm); break;
+            case 3: enc.add8(_imm); break;
             }
         } else {
             encVal(enc,_declaredType,ptr,idx,src,_off,_scale);
@@ -49,15 +53,25 @@ public class StoreX86 extends MemOpX86 {
 
     // Non-immediate encoding
     static void encVal( Encoding enc, SONType decl, short ptr, short idx, short src, int off, int scale ) {
-        if( decl instanceof SONTypeFloat ) {
+        int log = decl.log_size();
+        // Float reg being stored
+        if( src >= x86_64_v2.XMM_OFFSET ) {
             src -= (short)x86_64_v2.XMM_OFFSET;
-            enc.add1( decl==SONTypeFloat.F32 ? 0xF3 : 0xF2 ).add1(0x0F).add1(0x11);
+            assert log == 2 || log == 3;
+            enc.add1( log==2 ? 0xF3 : 0xF2 );
+            x86_64_v2.rexF(src,ptr,idx,false,enc);
+            enc.add1(0x0F).add1(0x11);
+        } else {
+            // byte stores from sil, dil, bpl, spl need a rex
+            if( log == 0 && src >= x86_64_v2.RSP ) enc.add1(x86_64_v2.rex(src,ptr,idx,false));
+            else x86_64_v2.rexF(src,ptr,idx,log==3,enc);
+            switch( log ) {
+            case 0: enc           .add1(0x88); break;
+            case 1: enc.add1(0x66).add1(0x89); break;
+            case 2: enc           .add1(0x89); break;
+            case 3: enc           .add1(0x89); break;
+            }
         }
-        else if( decl.log_size() == 0 ) enc.add1(0x88);
-        else if( decl.log_size() == 1 ) enc.add1(0x89);
-        else if( decl.log_size() == 2 ) enc.add1(0x89);
-        else if( decl.log_size() == 3 ) enc.add1(x86_64_v2.rex(src, ptr, idx)).add1(0x89);
-
         x86_64_v2.indirectAdr(scale, idx, ptr, off, src, enc);
     }
 
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/UJmpX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/UJmpX86.java
index c70a884..d404bb5 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/UJmpX86.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/UJmpX86.java
@@ -47,7 +47,7 @@ public class UJmpX86 extends CFGNode implements MachNode, RIPRelSize {
 
     @Override public void asm(CodeGen code, SB sb) {
         CFGNode target = uctrl();
-        assert target.nOuts() > 1; // Should optimize jmp to empty targets
+        //assert target.nOuts() > 1; // Should optimize jmp to empty targets
         sb.p(label(target));
     }
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/x86_64_v2.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/x86_64_v2.java
index 61035af..bee5d2e 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/x86_64_v2.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/x86_64_v2.java
@@ -5,8 +5,6 @@
 import com.compilerprogramming.ezlang.compiler.nodes.*;
 import com.compilerprogramming.ezlang.compiler.sontypes.*;
 
-import java.io.ByteArrayOutputStream;
-
 public class x86_64_v2 extends Machine {
     public x86_64_v2( CodeGen code ) {}
     // X86-64 V2.  Includes e.g. SSE4.2 and POPCNT.
@@ -22,6 +20,17 @@ public x86_64_v2( CodeGen code ) {}
     public static final int MAX_REG = 33;
     public static final int RPC = 33;
 
+    // Human-readable name for a register number, e.g. "RAX".
+    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",
+    };
+    // Hard crash for bad register number, fix yer bugs!
+    @Override public String[] regs() { return REGS; }
+
     public static int XMM_OFFSET = 16;
     // General purpose register mask: pointers and ints, not floats
     static final long RD_BITS = 0b1111111111111111L; // All the GPRs
@@ -36,7 +45,7 @@ public x86_64_v2( CodeGen code ) {}
     static RegMask RPC_MASK = new RegMask(RPC);
 
     static final long SPILLS = -(1L << MAX_REG);
-    static final RegMask SPLIT_MASK = new RegMask(WR_BITS | FP_BITS | (1L<<FLAGS) | SPILLS, -1L );
+    static final RegMask SPLIT_MASK = new RegMask(WR_BITS | FP_BITS /* | (1L<<FLAGS)*/ | SPILLS, -1L );
 
     // Load/store mask; both GPR and FPR
     static RegMask MEM_MASK = new RegMask(WR_BITS | FP_BITS);
@@ -59,10 +68,10 @@ public x86_64_v2( CodeGen code ) {}
     public static int REX_WB = 0x49;
 
     public enum MOD {
-        INDIRECT, //  [mem]
-        INDIRECT_disp8, // [mem + 0x12]
-        INDIRECT_disp32,// [mem + 0x12345678]
-        DIRECT,          // mem
+        INDIRECT,               //  [mem]
+        INDIRECT_disp8,         // [mem + 0x12]
+        INDIRECT_disp32,        // [mem + 0x12345678]
+        DIRECT,                 // mem
     };
 
     // opcode included here
@@ -79,7 +88,7 @@ static public int jumpop(String op) {
         case ">"  -> 0x8F;
         case "<"  -> 0x8C;
         case "<=" -> 0x8E;
-        case ">=" -> 0X8D;
+        case ">=" -> 0x8D;
         default -> throw new IllegalArgumentException("Too many arguments");
         };
     }
@@ -119,14 +128,6 @@ public static int rex(int reg, int ptr, int idx, boolean wide) {
         return rex;
     }
 
-    // return the size of the immediate
-    public static int imm_size(long imm) {
-        if(imm8(imm)) return 8;
-        if(imm16(imm)) return 16;
-        if(imm32(imm)) return 32;
-        return 64;
-    }
-
     public static int rex(int reg, int ptr, int idx) {
         return rex(reg, ptr, idx, true);
     }
@@ -175,41 +176,93 @@ public static void indirectAdr( int scale, short index, short base, int offset,
         }
     }
 
-    // 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",
+    // Limit of float args passed in registers
+    static RegMask[] XMMS8 = new RegMask[]{
+        new RegMask(XMM0), new RegMask(XMM1), new RegMask(XMM2), new RegMask(XMM3),
+        new RegMask(XMM4), new RegMask(XMM5), new RegMask(XMM6), new RegMask(XMM7),
     };
-    @Override public String reg( int reg ) {
-        return reg < REGS.length ? REGS[reg] : "[rsp+"+(reg-REGS.length)*8+"]";
-    }
 
-    // Stack slots, in units of 8 bytes.
-    @Override public int stackSlot( int reg ) {
-        return reg < REGS.length ? -1 : reg-REGS.length;
+    // Map from function signature and argument index to register.
+    // Used to set input registers to CallNodes, and ParmNode outputs.
+    @Override public RegMask callArgMask( SONTypeFunPtr tfp, int idx ) { return callInMask(tfp,idx); }
+    static RegMask callInMask( SONTypeFunPtr tfp, int idx ) {
+        if( idx==0 ) return RPC_MASK;
+        if( idx==1 ) return null;
+        return switch( CodeGen.CODE._callingConv ) {
+        case "SystemV" -> callSys5 (tfp,idx);
+        case "Win64"   -> callWin64(tfp,idx);
+        default -> throw Utils.TODO();
+        };
     }
 
+    // Maximum stack args used by this signature
+    @Override public short maxArgSlot( SONTypeFunPtr tfp ) {
+        return switch( CodeGen.CODE._callingConv ) {
+        case "SystemV" -> maxArgSlotSys5 (tfp);
+        case "Win64"   -> maxArgSlotWin64(tfp);
+        default -> throw Utils.TODO();
+        };
+    }
 
-    // WIN64(param passing)
-    static RegMask[] CALLINMASK_WIN64 = new RegMask[] {
+    // Win64 - max *4* args in registers of all kinds; after that on stack.
+    // Stack args land in increasing memory order with empty/mirror slots for
+    // every register.
+    //
+    // foo( int i0, flt f1, int i2, flt f3, int i4, flt f5, int i6, ... )
+    //
+    // -- Prior Frame --
+    // i6
+    // f5
+    // i4
+    // empty mirror, XMM3= f3
+    // empty mirror, R08 = i2
+    // empty mirror, XMM1= f1
+    // empty mirror, RCX = i0
+    // -- Caller Frame; 16b align --
+    // RPC
+    // PAD/ALIGN
+    // -- Callee Frame; 16b align --
+    static RegMask[] WIN64_CALL = new RegMask[] {
         RCX_MASK,
         RDX_MASK,
         R08_MASK,
         R09_MASK,
     };
 
+    static RegMask callWin64(SONTypeFunPtr tfp, int idx ) {
+        // idx 2,3,4,5 passed in registers, with stack slot mirrors.
+        // idx >= 6 passed on stack, starting at slot#1 (#0 reserved for RPC).
+        if( idx >= 6 )
+            return new RegMask(MAX_REG+1/*RPC*/+(idx-2));
+        return tfp.arg(idx-2) instanceof SONTypeFloat
+            ? XMMS8     [idx-2]
+            : WIN64_CALL[idx-2];
+    }
+    static short maxArgSlotWin64(SONTypeFunPtr tfp) {
+        return (short)tfp.nargs();
+    }
+
+    // Sys5: max 6 GPRs and 8 FPRS filled first.  Extra args land in increasing
+    // memory order as needed - no mirror space.
+    //
+    // foo( int i0, flt f1, int i2, flt f3, int i4, flt f5, int i6, ... )
+    // RDI =i0, RSI =i2, RDX =i4, RCX =i6, R08 =i8, R09 =i10
+    // XMM0=f1, XMM1=f3, XMM2=f5, XMM3=f7, XMM4=f9, XMM5=f11, XMM6=f13, XMM7=f15
+    //
+    // -- Prior Frame --
+    // f18
+    // i18
+    // f17
+    // i16
+    // i14
+    // i12
+    // -- Caller Frame; 16b align --
+    // RPC
+    // PAD/ALIGN
+    // -- Callee Frame; 16b align --
     // SystemV(param passing)
-    static RegMask[] CALLINMASK_SYSTEMV = new RegMask[] {
+    static RegMask[] SYS5_CALL = new RegMask[] {
         RDI_MASK,
         RSI_MASK,
         RDX_MASK,
@@ -218,63 +271,29 @@ public static void indirectAdr( int scale, short index, short base, int offset,
         R09_MASK,
     };
 
-    // Limit of float args passed in registers
-    static RegMask[] XMMS4 = new RegMask[]{
-        new RegMask(XMM0), new RegMask(XMM1), new RegMask(XMM2), new RegMask(XMM3),
-    };
-
-    // Map from function signature and argument index to register.
-    // Used to set input registers to CallNodes, and ParmNode outputs.
-    static RegMask callInMask( SONTypeFunPtr tfp, int idx ) {
-        if( idx==0 ) return RPC_MASK;
-        if( idx==1 ) return null;
-        // Count floats in signature up to index
-        int fcnt=0;
-        for( int i=2; i<idx; i++ )
-            if( tfp.arg(i-2) instanceof SONTypeFloat )
-                fcnt++;
-        // Floats up to XMMS in XMM registers
-        if( tfp.arg(idx-2) instanceof SONTypeFloat ) {
-            if( fcnt < XMMS4.length )
-                return XMMS4[fcnt];
-        } else {
-            RegMask[] cargs = switch( CodeGen.CODE._callingConv ) {
-            case "SystemV" -> CALLINMASK_SYSTEMV;
-            case "Win64"   -> CALLINMASK_WIN64;
-            default        -> throw new IllegalArgumentException("Unknown calling convention: "+CodeGen.CODE._callingConv);
-            };
-            if( idx-2-fcnt < cargs.length )
-                return cargs[idx-2-fcnt];
+    static RegMask callSys5(SONTypeFunPtr tfp, int idx ) {
+        // First 6 integers passed in registers: rdi,rsi,rdx,rcx,r08,r09
+        // First 8 floats passed in registers: xmm0-xmm7
+        int icnt=0, fcnt=0;     // Count of ints, floats
+        for( int i=2; i<idx; i++ ) {
+            if( tfp.arg(i-2) instanceof SONTypeFloat ) fcnt++;
+            else icnt++;
         }
-        throw Utils.TODO(); // Pass on stack slot
-    }
-
-    // Return the max stack slot used by this signature, or 0
-    static short maxSlot( SONTypeFunPtr tfp ) {
-        // Count floats in signature up to index
-        int fcnt=0;
-        for( int i=0; i<tfp.nargs(); i++ )
-            if( tfp.arg(i) instanceof SONTypeFloat )
-                fcnt++;
-        if( fcnt >= XMMS4.length )
-            throw Utils.TODO();
-        RegMask[] cargs = switch( CodeGen.CODE._callingConv ) {
-        case "SystemV" -> CALLINMASK_SYSTEMV;
-        case "Win64"   -> CALLINMASK_WIN64;
-        default        -> throw new IllegalArgumentException("Unknown calling convention: "+CodeGen.CODE._callingConv);
-        };
-        if( tfp.nargs()-fcnt >= cargs.length )
-            throw Utils.TODO();
-        return 0;               // No stack args
-    }
-
-    // Return single int/ptr register.  Used by CallEnd output and Return input.
-    static RegMask retMask( SONTypeFunPtr tfp ) {
-        return tfp.ret() instanceof SONTypeFloat ? XMM0_MASK : RAX_MASK;
+        int nstk = Math.max(icnt-6,0)+Math.max(fcnt-8,0);
+        return tfp.arg(idx-2) instanceof SONTypeFloat
+            ? fcnt<8 ? XMMS8    [fcnt] : new RegMask(MAX_REG+1/*RPC*/+nstk)
+            : icnt<6 ? SYS5_CALL[icnt] : new RegMask(MAX_REG+1/*RPC*/+nstk);
+    }
+    static short maxArgSlotSys5(SONTypeFunPtr tfp) {
+        int icnt=0, fcnt=0;     // Count of ints, floats
+        for( int i=0; i<tfp.nargs(); i++ ) {
+            if( tfp.arg(i) instanceof SONTypeFloat ) fcnt++;
+            else icnt++;
+        }
+        int nstk = Math.max(icnt-6,0)+Math.max(fcnt-8,0);
+        return (short)nstk;
     }
 
-
-    // caller saved(systemv)
     static final long SYSTEM5_CALLER_SAVE =
         (1L << RAX) | (1L << RCX) | (1L << RDX) |
         (1L << RDI) | (1L << RSI) |
@@ -282,86 +301,28 @@ static RegMask retMask( SONTypeFunPtr tfp ) {
         (1L << FLAGS) |           // Flags are killed
         // All FP regs are killed
         FP_BITS;
-    static final RegMask SYSTEM5_CALLER_SAVE_MASK = new RegMask(SYSTEM5_CALLER_SAVE);
 
-    // caller saved(win64)
     static final long WIN64_CALLER_SAVE =
         (1L<< RAX) | (1L<< RCX) | (1L<< RDX) |
         (1L<< R08) | (1L<< R09) | (1L<< R10) | (1L<< R11) |
         (1L<<FLAGS)|           // Flags are killed
         // Only XMM0-XMM5 are killed; XMM6-XMM15 are preserved
         (1L<<XMM0) | (1L<<XMM1) | (1L<<XMM2) | (1L<<XMM3) |
-        (1L<<XMM4) | (1L<<XMM5);
-    static final RegMask WIN64_CALLER_SAVE_MASK = new RegMask(WIN64_CALLER_SAVE);
+        (1L<<XMM4) | (1L<<XMM5) ;
 
-    static RegMask x86CallerSave() {
+    @Override public long callerSave() {
         return switch (CodeGen.CODE._callingConv) {
-        case "SystemV" -> SYSTEM5_CALLER_SAVE_MASK;
-        case "Win64"   ->   WIN64_CALLER_SAVE_MASK;
+        case "SystemV" -> SYSTEM5_CALLER_SAVE;
+        case "Win64"   ->   WIN64_CALLER_SAVE;
         default -> throw new IllegalArgumentException("Unknown calling convention: " + CodeGen.CODE._callingConv);
         };
     }
-
-    @Override public RegMask callerSave() { return x86CallerSave(); }
-
-    static final RegMask SYSTEM5_CALLEE_SAVE_MASK;
-    static final RegMask   WIN64_CALLEE_SAVE_MASK;
-
-    static {
-        long callee = ~SYSTEM5_CALLER_SAVE;
-        // Remove the spills
-        callee &= (1L<<MAX_REG)-1;
-        callee &= ~(1L<<FLAGS);
-        callee &= ~(1L<<RSP);
-        SYSTEM5_CALLEE_SAVE_MASK = new RegMask(callee);
-
-        callee = ~WIN64_CALLER_SAVE;
-        // Remove the spills
-        callee &= (1L<<MAX_REG)-1;
-        callee &= ~(1L<<FLAGS);
-        callee &= ~(1L<<RSP);
-        WIN64_CALLEE_SAVE_MASK = new RegMask(callee);
-    }
-    static RegMask x86CalleeSave() {
-        return switch( CodeGen.CODE._callingConv ) {
-        case "SystemV" -> SYSTEM5_CALLEE_SAVE_MASK;
-        case "Win64"   ->   WIN64_CALLEE_SAVE_MASK;
-        default        -> throw new IllegalArgumentException("Unknown calling convention: "+CodeGen.CODE._callingConv);
-        };
-    }
-    @Override public RegMask calleeSave() { return x86CalleeSave(); }
-
-    static final RegMask[] WIN64_RET_MASKS, SYS5_RET_MASKS;
-    static {
-        WIN64_RET_MASKS = makeRetMasks(  WIN64_CALLEE_SAVE_MASK);
-         SYS5_RET_MASKS = makeRetMasks(SYSTEM5_CALLEE_SAVE_MASK);
-    }
-    private static RegMask[] makeRetMasks(RegMask mask) {
-        int nSaves = mask.size();
-        RegMask[] masks = new RegMask[4 + nSaves];
-        masks[0] = null;
-        masks[1] = null;     // Memory
-        masks[2] = null;     // Varies, either XMM0 or RAX
-        masks[3] = RPC_MASK;
-        short reg = mask.firstReg();
-        for( int i=0; i<nSaves; i++ ) {
-            masks[i+4] = new RegMask(reg);
-            reg = mask.nextReg(reg);
-        }
-        return masks;
+    @Override public long neverSave() { return 1L<<RSP; }
+    @Override public RegMask retMask( SONTypeFunPtr tfp ) {
+        return tfp.ret() instanceof SONTypeFloat ? XMM0_MASK : RAX_MASK;
     }
+    @Override public int rpc() { return RPC; }
 
-    // Return single int/ptr register.  Used by CallEnd output and Return input.
-    static RegMask retMask( SONTypeFunPtr tfp, int i ) {
-        if( i==2 )
-            return tfp.ret() instanceof SONTypeFloat ? XMM0_MASK : RAX_MASK;
-        RegMask[] masks = switch (CodeGen.CODE._callingConv) {
-        case "SystemV" ->  SYS5_RET_MASKS;
-        case "Win64"   -> WIN64_RET_MASKS;
-        default        -> throw new IllegalArgumentException("Unknown calling convention: "+CodeGen.CODE._callingConv);
-        };
-        return masks[i];
-    }
 
     // Create a split op; any register to any register, including stack slots
     @Override public SplitNode split(String kind, byte round, LRG lrg) {
@@ -373,50 +334,47 @@ static RegMask retMask( SONTypeFunPtr tfp, int i ) {
         return new UJmpX86();
     }
 
-    // Break an infinite loop
-    @Override public NeverNode 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 AddFNode    addf -> addf(addf);
+        case AddNode      add -> add(add);
+        case AndNode      and -> and(and);
+        case BoolNode    bool -> cmp(bool);
+        case CProjNode      c -> new CProjNode(c);
         case CallEndNode cend -> new CallEndX86(cend);
-        case CallNode call -> call(call);
-        case CastNode cast -> new CastX86(cast);
-        case CProjNode c -> new CProjNode(c);
+        case CallNode    call -> call(call);
+        case CastNode    cast -> new CastX86(cast);
         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 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 NotNode not -> new NotX86(not);
-        case OrNode or -> or(or);
-        case ParmNode parm -> new ParmX86(parm);
-        case PhiNode phi -> new PhiNode(phi);
-        case ProjNode prj -> prj(prj);
-        case ReadOnlyNode read -> new ReadOnlyNode(read);
-        case ReturnNode ret -> new RetX86(ret, ret.fun());
-        case SarNode sar -> sar(sar);
-        case ShlNode shl -> shl(shl);
-        case ShrNode shr -> shr(shr);
-        case StartNode start -> new StartNode(start);
-        case StopNode stop -> new StopNode(stop);
-        case StoreNode st -> st(st);
-        case SubFNode subf -> new SubFX86(subf);
-        case SubNode sub -> sub(sub);
-        case ToFloatNode tfn -> i2f8(tfn);
-        case XorNode xor -> xor(xor);
+        case MinusNode    neg -> new NegX86(neg);
+        case MulFNode    mulf -> new MulFX86(mulf);
+        case MulNode      mul -> mul(mul);
+        case NewNode      nnn -> new NewX86(nnn);
+        case NotNode      not -> new NotX86(not);
+        case OrNode        or -> or(or);
+        case ParmNode    parm -> new ParmX86(parm);
+        case PhiNode      phi -> new PhiNode(phi);
+        case ProjNode     prj -> prj(prj);
+        case ReadOnlyNode read-> new ReadOnlyNode(read);
+        case ReturnNode   ret -> new RetX86(ret, ret.fun());
+        case SarNode      sar -> sar(sar);
+        case ShlNode      shl -> shl(shl);
+        case ShrNode      shr -> shr(shr);
+        case StartNode  start -> new StartNode(start);
+        case StopNode    stop -> new StopNode(stop);
+        case StoreNode     st -> st(st);
+        case SubFNode    subf -> new SubFX86(subf);
+        case SubNode      sub -> sub(sub);
+        case ToFloatNode  tfn -> i2f8(tfn);
+        case XCtrlNode      x -> new ConstantNode(SONType.XCONTROL);
+        case XorNode      xor -> xor(xor);
 
         case LoopNode loop -> new LoopNode(loop);
         case RegionNode region -> new RegionNode(region);
@@ -425,7 +383,7 @@ public Node instSelect(Node n) {
     }
 
     public static boolean imm8( long imm ) { return -128 <= imm && imm <= 127; }
-    public static boolean imm16(long imm) {return -32768 <= imm && imm <= 32767; }
+
     public static boolean imm32( long imm ) { return (int)imm==imm; }
 
     // Attempt a full LEA-style break down.
@@ -433,7 +391,7 @@ private Node add(AddNode add) {
         Node lhs = add.in(1);
         Node rhs = add.in(2);
         if( lhs instanceof LoadNode ld && ld.nOuts() == 1 && ld._declaredType.log_size() >= 3)
-            return new AddMemX86(add, address(ld), ld.ptr(), idx, off, scale, imm(rhs), val);
+            return new AddMemX86(add, address(ld), ld.ptr(), idx, off, scale, 0, rhs);
 
 //        if(rhs instanceof LoadNode ld && ld.nOuts() == 1 && ld._declaredType.log_size() >= 3) {
 //            throw Utils.TODO(); // Swap load sides
@@ -441,7 +399,7 @@ private Node add(AddNode add) {
 
         // Attempt a full LEA-style break down.
         // Returns one of AddX86, AddIX86, LeaX86, or LHS
-        if( rhs instanceof ConstantNode off && off._con instanceof SONTypeInteger toff ) {
+        if( rhs instanceof ConstantNode off2 && off2._con instanceof SONTypeInteger toff ) {
             long imm = toff.value();
             assert imm!=0;        // Folded in peeps
             if( (int)imm != imm ) // Full 64bit immediate
@@ -518,27 +476,29 @@ private Node _cmp(BoolNode bool) {
         Node lhs = bool.in(1);
         Node rhs = bool.in(2);
 
+        // Since cmp does not record a BOP, SetX/Jmp need to know if the CMP is swapped.
+
         // Vs memory
-        if( lhs instanceof LoadNode ld && ld.nOuts() == 1 )
+        if( lhs instanceof LoadNode ld && ld.nOuts() == 1 && rhs._type.isa(ld._declaredType) )
             return new CmpMemX86(bool, address(ld), ld.ptr(), idx, off, scale, imm(rhs), val, false);
 
-        if( rhs instanceof LoadNode ld && ld.nOuts() == 1 )
+        // Operands swap in the encoding directly, no need for Set/Jmp to swap `bop`
+        if( rhs instanceof LoadNode ld && ld.nOuts() == 1 && lhs._type.isa(ld._declaredType))
             return new CmpMemX86(bool, address(ld), ld.ptr(), idx, off, scale, imm(lhs), val, true );
 
         // Vs immediate
         if( rhs instanceof ConstantNode con && con._con instanceof SONTypeInteger ti && imm32(ti.value()) )
-            return new CmpIX86(bool, (int)ti.value());
+            return new CmpIX86(bool, (int)ti.value(), false);
 
-        if( lhs instanceof ConstantNode con && con._con instanceof SONTypeInteger ti && imm32(ti.value()) ) {
-            swap = true;
-            return new CmpIX86(bool, (int)ti.value(), 0.5);
-        }
+        // Operand swap compare; Set and Jmp need to swap `bop`
+        if( lhs instanceof ConstantNode con && con._con instanceof SONTypeInteger ti && imm32(ti.value()) )
+            return new CmpIX86(bool, (int)ti.value(), swap=true);
 
         // x vs y
         return new CmpX86(bool);
     }
 
-    private Node con(ConstantNode con) {
+    private Node con( ConstantNode con ) {
         if(!con._con.isConstant()) return new ConstantNode(con); // Default unknown caller inputs
         return switch (con._con) {
             case SONTypeInteger ti -> new IntX86(con);
@@ -560,10 +520,11 @@ 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
-        BoolNode bool;
-        if( iff.in(1) instanceof BoolNode bool0 ) bool = bool0;
-        else iff.setDef(1, bool=new BoolNode.NE(iff.in(1), new ConstantNode(SONTypeInteger.ZERO)));
-        return new JmpX86(iff, bool.op());
+        String op = "!=";
+        if( iff.in(1) instanceof BoolNode bool ) op = swap ? IfNode.swap(bool.op()) : bool.op();
+        else if( iff.in(1)==null ) op = "=="; // Never-node cutout
+        else iff.setDef(1, new BoolNode.NE(iff.in(1), new ConstantNode(SONTypeInteger.ZERO)));
+        return new JmpX86(iff, op);
     }
 
     private Node ld(LoadNode ld) {
@@ -604,21 +565,25 @@ private Node shr( ShrNode shr ) {
         return new ShrX86(shr);
     }
 
-    private Node st (StoreNode st ){
+    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()) {
-               return new MemAddX86(address(st), st.ptr(), idx, off, scale, imm(op.in(2)), val);
-            }
+        if( op instanceof AddNode ) {
+            if( op.in(1) instanceof LoadNode ld && stld_match(st,ld) )
+                return new MemAddX86(address(st), st.ptr(), idx, off, scale, imm(op.in(2)), val);
+            if( op.in(2) instanceof LoadNode ld && stld_match(st,ld) )
+                return new MemAddX86(address(st), st.ptr(), idx, off, scale, imm(op.in(1)), val);
         }
-
         return new StoreX86(address(st), st.ptr(), idx, off, scale, imm(st.val()), val);
     }
+    private static boolean stld_match(StoreNode st, LoadNode ld ) {
+        return
+            ld.in(0) == st.in(0) &&
+            ld.mem() == st.mem() &&
+            ld.ptr() == st.ptr() &&
+            ld.off() == st.off();
+    }
+
 
     private Node sub (SubNode sub ){
         return sub.in(2) instanceof ConstantNode con && con._con instanceof SONTypeInteger ti
@@ -664,7 +629,7 @@ private <N extends MemOpNode > N address(N mop) {
         return mop;
     }
 
-    private int imm (Node xval ){
+    private int imm( Node xval ) {
         assert val == null && imm == 0;
         if( xval instanceof ConstantNode con && con._con instanceof SONTypeInteger ti) {
             val = null;

From 741396ff5d53713bc482fdbdb56b8d9701aacd84 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Sat, 26 Apr 2025 00:34:33 +0100
Subject: [PATCH 062/119] Sync with final ch21

---
 .../ezlang/compiler/sontypes/SONTypeFunPtr.java                  | 1 +
 .../ezlang/compiler/sontypes/SONTypeTuple.java                   | 1 +
 2 files changed, 2 insertions(+)

diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeFunPtr.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeFunPtr.java
index 0b41493..07b06df 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeFunPtr.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeFunPtr.java
@@ -44,6 +44,7 @@ public SONTypeFunPtr(byte nil, SONTypeTuple sig, SONType ret, long fidxs) {
 //    public static SONTypeFunPtr TEST  = make((byte)2, SONTypeTuple.TEST, SONTypeInteger.BOT,1);
 //    public static SONTypeFunPtr TEST0 = make((byte)3, SONTypeTuple.TEST, SONTypeInteger.BOT,3);
     public static SONTypeFunPtr MAIN  = make((byte)3, SONTypeTuple.MAIN, SONTypeInteger.BOT,-1);
+    public static SONTypeFunPtr CALLOC= make((byte)3,SONTypeTuple.CALLOC,SONTypeMemPtr.BOT,-1);
     public static void gather(ArrayList<SONType> ts) { /* ts.add(TEST); ts.add(TEST0); */ ts.add(BOT); ts.add(MAIN); }
 
     @Override
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeTuple.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeTuple.java
index ecb14ef..73ea8f4 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeTuple.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeTuple.java
@@ -17,6 +17,7 @@ public class SONTypeTuple extends SONType {
     public static final SONTypeTuple START= make(SONType.CONTROL, SONTypeMem.TOP, SONTypeInteger.BOT);
     public static final SONTypeTuple MAIN = make(SONTypeInteger.BOT);
     public static final SONTypeTuple RET  = make(SONType.CONTROL, SONTypeMem.BOT, SONType.BOTTOM);
+    public static final SONTypeTuple CALLOC = make(SONTypeInteger.BOT,SONTypeInteger.BOT);
 
     public static final SONTypeTuple IF_BOTH    = make(new SONType[]{SONType. CONTROL, SONType. CONTROL});
     public static final SONTypeTuple IF_NEITHER = make(new SONType[]{SONType.XCONTROL, SONType.XCONTROL});

From 044886d01037ea4c18892822121afb87b42de6e2 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Sat, 26 Apr 2025 00:38:21 +0100
Subject: [PATCH 063/119] Sync with final ch21

---
 .../ezlang/compiler/codegen/BuildLRG.java     |   4 +-
 .../ezlang/compiler/codegen/CodeGen.java      | 105 ++++++--
 .../ezlang/compiler/codegen/Encoding.java     | 237 +++++++++++-------
 .../compiler/codegen/GlobalCodeMotion.java    |  38 ++-
 .../ezlang/compiler/codegen/IFG.java          |   6 +-
 .../ezlang/compiler/codegen/LRG.java          |   5 -
 .../ezlang/compiler/codegen/LinkMem.java      |   7 +-
 .../compiler/codegen/ListScheduler.java       |   3 +
 .../ezlang/compiler/codegen/Machine.java      | 107 +++++---
 .../ezlang/compiler/codegen/RegAlloc.java     |  71 +++---
 .../ezlang/compiler/codegen/RegMask.java      |  10 +-
 .../ezlang/compiler/codegen/RegMaskRW.java    |   5 +
 12 files changed, 394 insertions(+), 204 deletions(-)

diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/BuildLRG.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/BuildLRG.java
index 99c3fbd..003d26e 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/BuildLRG.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/BuildLRG.java
@@ -55,8 +55,8 @@ public static boolean run(int round, RegAlloc alloc) {
                         if( n.in(i)!=null ) {
                             LRG lrg2 = alloc.lrg(n.in(i));
                             if( lrg2 != null ) { // Anti-dep or other, no LRG
-                                RegMask use_mask = mach.regmap(i);
-                                if( !lrg2.machUse(mach,(short)i,use_mask.size1()).and(use_mask) )
+                                RegMask use_mask = mach.regmap(i); // use_mask is also null for anti-dep
+                                if( use_mask!=null && !lrg2.machUse(mach,(short)i,use_mask.size1()).and(use_mask) )
                                     alloc.fail(lrg2); // Empty register mask, must split
                             }
                         }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/CodeGen.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/CodeGen.java
index 22e05c3..d7eaa3a 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/CodeGen.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/CodeGen.java
@@ -22,6 +22,7 @@ public enum Phase {
         Parse,                  // Parse ASCII text into Sea-of-Nodes IR
         Opto,                   // Run ideal optimizations
         TypeCheck,              // Last check for bad programs
+        LoopTree,               // Build a loop tree; break infinite loops
         InstSelect,             // Convert to target hardware nodes
         Schedule,               // Global schedule (code motion) nodes
         LocalSched,             // Local schedule
@@ -52,6 +53,27 @@ public CodeGen( String src, SONTypeInteger arg, long workListSeed ) {
     }
 
 
+    // All passes up to Phase, except ELF
+    public CodeGen driver( Phase phase ) { return driver(phase,null,null); }
+    public CodeGen driver( Phase phase, String cpu, String callingConv ) {
+        if( _phase==null )                       parse();
+        if( _phase.ordinal() < phase.ordinal() ) opto();
+        if( _phase.ordinal() < phase.ordinal() ) typeCheck();
+        if( _phase.ordinal() < phase.ordinal() ) loopTree();
+        if( _phase.ordinal() < phase.ordinal() && cpu != null ) instSelect(cpu,callingConv);
+        if( _phase.ordinal() < phase.ordinal() ) GCM();
+        if( _phase.ordinal() < phase.ordinal() ) localSched();
+        if( _phase.ordinal() < phase.ordinal() ) regAlloc();
+        if( _phase.ordinal() < phase.ordinal() ) encode();
+        return this;
+    }
+
+    // Run all the phases through final ELF emission
+    public CodeGen driver( String cpu, String callingConv, String obj ) throws IOException {
+        return driver(Phase.Encoding,cpu,callingConv).exportELF(obj);
+    }
+
+
     // ---------------------------
     /**
      *  A counter, for unique node id generation.  Starting with value 1, to
@@ -125,6 +147,7 @@ public CodeGen parse() {
         assert _phase == null;
         _phase = Phase.Parse;
         long t0 = System.currentTimeMillis();
+
         P.parse();
         _tParse = (int)(System.currentTimeMillis() - t0);
         return this;
@@ -180,18 +203,42 @@ public CodeGen typeCheck() {
         return this;
     }
 
+    // ---------------------------
+    // Build the loop tree; break never-exit loops
+    public int _tLoopTree;
+    public CodeGen loopTree() {
+        assert _phase.ordinal() <= Phase.TypeCheck.ordinal();
+        _phase = Phase.LoopTree;
+        long t0 = System.currentTimeMillis();
+        // Build the loop tree, fix never-exit loops
+        _start.buildLoopTree(_stop);
+        _tLoopTree = (int)(System.currentTimeMillis() - t0);
+        return this;
+    }
 
     // ---------------------------
     // Code generation CPU target
     public Machine _mach;
-    //
+    // Chosen calling convention (usually either Win64 or SystemV)
     public String _callingConv;
+    // Callee save registers
+    public RegMask _callerSave;
+
+    // All returns have the following inputs:
+    // 0 - ctrl
+    // 1 - memory
+    // 2 - varies with returning GPR,FPR
+    // 3 - RPC
+    // 4+- Caller save registers
+    public RegMask[] _retMasks;
+    // Return Program Counter
+    public RegMask _rpcMask;
 
     // Convert to target hardware nodes
     public int _tInsSel;
     public CodeGen instSelect( String cpu, String callingConv ) { return instSelect(cpu,callingConv,PORTS); }
     public CodeGen instSelect( String cpu, String callingConv, String base ) {
-        assert _phase.ordinal() <= Phase.TypeCheck.ordinal();
+        assert _phase.ordinal() == Phase.LoopTree.ordinal();
         _phase = Phase.InstSelect;
 
         _callingConv = callingConv;
@@ -203,6 +250,27 @@ public CodeGen instSelect( String cpu, String callingConv, String base ) {
         try { _mach = ((Class<Machine>) Class.forName( clzFile )).getDeclaredConstructor(new Class[]{CodeGen.class}).newInstance(this); }
         catch( Exception e ) { throw new RuntimeException(e); }
 
+        // Build global copies of common register masks.
+        long callerSave = _mach.callerSave();
+        long  neverSave = _mach. neverSave();
+        int maxReg = Math.min(64,_mach.regs().length);
+        assert maxReg>=64 || (-1L << maxReg & callerSave)==0; // No stack slots in callerSave
+        _callerSave = new RegMask(callerSave);
+
+        // Build a Return RegMask array.  All returns have the following inputs:
+        // 0 - ctrl
+        // 1 - memory
+        // 2 - varies with returning GPR,FPR
+        // 3 - RPC
+        // 4+- Caller save registers
+        _retMasks = new RegMask[(maxReg-_callerSave.size()-Long.bitCount( neverSave ))+4];
+        for( int reg=0, i=4; reg<maxReg; reg++ )
+            if( !_callerSave.test(reg) && ((1L<<reg)&neverSave)==0 )
+                _retMasks[i++] = new RegMask(reg);
+        _rpcMask = new RegMask(_mach.rpc());
+        _retMasks[3] = _rpcMask;
+
+
         // Convert to machine ops
         long t0 = System.currentTimeMillis();
         _uid = 1;               // All new machine nodes reset numbering
@@ -258,7 +326,7 @@ private void _instOuts( Node n, BitSet visit ) {
 
 
     // ---------------------------
-    // Control Flow Graph in RPO order.
+    // Control Flow Graph in Reverse Post Order.
     public int _tGCM;
     public Ary<CFGNode> _cfg = new Ary<>(CFGNode.class);
 
@@ -269,8 +337,6 @@ public CodeGen GCM( boolean show) {
         _phase = Phase.Schedule;
         long t0 = System.currentTimeMillis();
 
-        // Build the loop tree, fix never-exit loops
-        _start.buildLoopTree(_stop);
         GlobalCodeMotion.buildCFG(this);
         _tGCM = (int)(System.currentTimeMillis() - t0);
         if( show )
@@ -305,34 +371,32 @@ public CodeGen regAlloc() {
         return this;
     }
 
-    public String reg(Node n) {
+    // Human readable register name
+    public String reg(Node n) { return reg(n,null); }
+    public String reg(Node n, FunNode fun) {
         if( _phase.ordinal() >= Phase.RegAlloc.ordinal() ) {
-            String s = _regAlloc.reg(n);
+            String s = _regAlloc.reg(n,fun);
             if( s!=null ) return s;
         }
         return "N"+ n._nid;
     }
 
+
     // ---------------------------
     // Encoding
     public int _tEncode;
-    public boolean _JIT;
-    public Encoding _encoding;
-    public CodeGen encode(boolean jit) {
+    public Encoding _encoding;   // Encoding object
+    public void preEncode() {  } // overridden by alternative ports
+    public CodeGen encode() {
         assert _phase == Phase.RegAlloc;
         _phase = Phase.Encoding;
         long t0 = System.currentTimeMillis();
         _encoding = new Encoding(this);
-        _JIT = jit;
+        preEncode();
         _encoding.encode();
         _tEncode = (int)(System.currentTimeMillis() - t0);
         return this;
     }
-    public CodeGen encode() {
-        return encode(false);
-    }
-    // Encoded binary, no relocation info
-    public byte[] binary() { return _encoding.bits(); }
 
     // ---------------------------
     // Exporting to external formats
@@ -365,6 +429,15 @@ public CodeGen exportELF(String fname) throws IOException {
     // Debugging helper
     public Node f(int idx) { return _stop.find(idx); }
 
+
+    String printCFG() {
+        if( _cfg==null ) return "no CFG";
+        SB sb = new SB();
+        for( CFGNode cfg : _cfg )
+            IRPrinter.printLine(cfg,sb);
+        return sb.toString();
+    }
+
     public static void print_as_hex(Encoding enc) {
         for (byte b : enc._bits.toByteArray()) {
             System.out.print(String.format("%02X", b));
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/Encoding.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/Encoding.java
index 6696e1c..53f20ed 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/Encoding.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/Encoding.java
@@ -21,7 +21,7 @@
 public class Encoding {
 
     // Top-level program graph structure
-    final CodeGen _code;
+    public final CodeGen _code;
 
     // Instruction bytes.  The registers are encoded already.  Relocatable data
     // is in a fixed format depending on the kind of relocation.
@@ -153,11 +153,13 @@ public void largeConstant( Node relo, SONType t, int off, int elf ) {
         _bigCons.put(relo,new Relo(relo,t,(byte)off,(byte)elf));
     }
 
+    // --------------------------------------------------
     void encode() {
-        // Basic block layout: invert branches to keep blocks in-order; insert
+        // Basic block layout: negate branches to keep blocks in-order; insert
         // unconditional jumps.  Attempt to keep backwards branches taken,
         // forwards not-taken (this is the default prediction on most
-        // hardware).  Layout is still RPO but with more restrictions.
+        // hardware).  Layout is still Reverse Post Order but with more
+        // restrictions.
         basicBlockLayout();
 
         // Write encoding bits in order into a big byte array.
@@ -170,20 +172,25 @@ void encode() {
 
         // Patch RIP-relative and local encodings now.
         patchLocalRelocations();
-
     }
 
-    // Basic block layout: invert branches to keep blocks in-order; insert
-    // unconditional jumps.  Attempt to keep backwards branches taken,
-    // forwards not-taken (this is the default prediction on most
-    // hardware).  Layout is still RPO but with more restrictions.
+    // --------------------------------------------------
+    // Basic block layout: negate branches to keep blocks in-order; insert
+    // unconditional jumps.  Attempt to keep backwards branches taken, forwards
+    // not-taken (this is the default prediction on most hardware).  Layout is
+    // still Reverse Post Order but with more restrictions.
     private void basicBlockLayout() {
+        IdentityHashMap<LoopNode,Ary<CFGNode>> rpos = new IdentityHashMap<>();
         Ary<CFGNode> rpo = new Ary<>(CFGNode.class);
+        rpos.put(_code._start.loop(),rpo);
         BitSet visit = _code.visit();
+        //IdentityHashMap<CFGNode,LoopNode> looptail = new IdentityHashMap<>();
+        rpo.add(_code._stop);
         for( Node n : _code._start._outputs )
             if( n instanceof FunNode fun ) {
                 int x = rpo._len;
-                _rpo_cfg(fun, visit, rpo);
+                //_rpo_cfg2(fun, visit, rpo, looptail);
+                _rpo_cfg(fun, visit, rpos );
                 assert rpo.at(x) instanceof ReturnNode;
             }
         rpo.add(_code._start);
@@ -195,64 +202,37 @@ private void basicBlockLayout() {
         _code._cfg = rpo;       // Save the new ordering
     }
 
-    // Basic block layout.  Now that RegAlloc is finished, no more spill code
-    // will appear.  We can change our BB layout from RPO to something that
-    // minimizes actual branches, takes advantage of fall-through edges, and
-    // tries to help simple branch predictions: back branches are predicted
-    // taken, forward not-taken.
-    private void _rpo_cfg(CFGNode bb, BitSet visit, Ary<CFGNode> rpo) {
-        if( visit.get(bb._nid) ) return;
+
+    private void _rpo_cfg(CFGNode bb, BitSet visit, IdentityHashMap<LoopNode,Ary<CFGNode>> rpos ) {
+        if( bb==null || visit.get(bb._nid) ) return;
         visit.set(bb._nid);
-        if( bb.nOuts()==0 ) return; // StopNode
-        if( !(bb instanceof IfNode iff) ) {
-            CFGNode next = bb instanceof ReturnNode ? (CFGNode)bb.out(bb.nOuts()-1) : bb.uctrl();
-            // If the *next* BB has already been visited, we may need an
-            // unconditional jump here
-            if( visit.get(next._nid) && !(next instanceof StopNode) ) {
-                boolean needJump = next instanceof LoopNode
-                    // If backwards to a loop, and the block has statements,
-                    // will need a jump.  Empty blocks can just backwards branch.
-                    ? (bb.nOuts()>1)
-                    // Forwards jump.  If all the blocks, in RPO order, to our
-                    // target are empty, we will fall in and not need a jump.
-                    : !isEmptyBackwardsScan(rpo,next);
-                if( needJump ) {
-                    CFGNode jmp = _code._mach.jump();
-                    jmp.setDefX(0,bb);
-                    next.setDef(next._inputs.find(bb),jmp);
-                    rpo.add(jmp);
-                }
-            }
-            _rpo_cfg(next,visit,rpo);
-        } else {
-            boolean invert = false;
+        CFGNode next = bb.uctrl();
+
+        // Loops run an inner "rpo_cfg", then append the entire loop body in
+        // place.  This keeps loop bodies completely contained, although if
+        // Some Day Later we have real profile data we ought to arrange the
+        // branch orderings based on frequency.
+        if( bb instanceof LoopNode loop ) {
+            Ary<CFGNode> body = new Ary<>(CFGNode.class); // Private RPO for the loop
+            rpos.put(loop.loop(),body);                   // Find it via loop tree
+            _rpo_cfg(next,visit,rpos);                    // RPO the loop
+            body.add(loop);                               // Include loop last (first in RPO)
+            Ary<CFGNode> outer = rpos.get(loop.cfg(1).loop());
+            outer.addAll(body); // Append to original CFG
+            return;
+        }
+
+        // IfNodes visit 2 sides, and may choose to reorder them in the RPO
+        if( bb instanceof IfNode iff ) {
             // Pick out the T/F projections
             CProjNode t = iff.cproj(0);
             CProjNode f = iff.cproj(1);
-            int tld = t.loopDepth(), fld = f.loopDepth(), bld = bb.loopDepth();
-            // Decide entering or exiting a loop
-            if( tld==bld ) {
-                // if T is empty, keep
-                if( t.nOuts()>1 &&
-                    // Else swap so T is forward and exits, while F falls into next loop block
-                    ((fld<bld && t.out(0)!=t.loop()) ||
-                     // Jump to an empty block and fall into a busy block
-                     (fld==bld && f.nOuts()==1)) ) {
-                    invert = true;
-                } // Else nothing
-            } else if( tld > bld ) { // True enters a deeper loop
-                throw Utils.TODO();
-            } else if( fld == bld && f.out(0) instanceof LoopNode ) { // Else True exits a loop
-                // if false is the loop backedge, make sure its true/taken
-                invert = true;
-            } // always forward and good
-            // Invert test and Proj fields
-            if( invert ) {
-                iff.invert();
+            // Invert the branch or not
+            if( shouldInvert(t,f,iff.loopDepth()) ) {
+                iff.negate();
                 t.invert();
                 f.invert();
                 CProjNode tmp=f; f=t; t=tmp; // Swap t/f
-                int d=tld; tld=fld; fld=d;   // Swap depth
             }
 
             // Whichever side is visited first becomes last in the RPO.  With
@@ -260,29 +240,79 @@ private void _rpo_cfg(CFGNode bb, BitSet visit, Ary<CFGNode> rpo) {
             // when the False RPO visit returns, the IF is immediately next.
             // When the RPO is reversed, the fall-through path will always be
             // following the IF.
+            _rpo_cfg(t,visit,rpos); // True side first
+            next = f;               // False side last
+        }
 
-            // If loops are involved, attempt to keep them in a line.  Visit
-            // the exits first, so they follow the loop body when the order gets
-            // reversed.
-            if( fld < tld || (fld==tld && f.nOuts()==1) ) {
-                _rpo_cfg(f,visit,rpo);
-                _rpo_cfg(t,visit,rpo);
-            } else {
-                _rpo_cfg(t,visit,rpo);
-                _rpo_cfg(f,visit,rpo);
+        // If the *next* BB has already been visited, and we are not already a
+        // jump, we may need an unconditional forwards jump here
+        Ary<CFGNode> rpo = rpos.get(bb.loop());
+        if( next!=null && visit.get(next._nid) && !(bb instanceof IfNode) ) {
+            boolean needJump = next instanceof LoopNode
+                // Empty blocks from an IF will invert the IF and backwards
+                // branch to the loop head.  If not empty, or not an IF
+                // will need a jump.
+                ? (bb.nOuts()>1 || !(bb.cfg0() instanceof IfNode) )
+                // Forwards jump.  If all the blocks, in RPO order, to our
+                // target are empty, we will fall in and not need a jump.
+                : !backwardsEmptyScan(rpo,next);
+            if( needJump ) {
+                CFGNode jmp = _code._mach.jump();
+                jmp._ltree = bb._ltree;
+                jmp.setDefX(0,bb);
+                next.setDef(next._inputs.find(bb),jmp);
+                rpo.add(jmp);
             }
         }
-        assert rpo._len>0 || bb instanceof ReturnNode;
+
+        _rpo_cfg(next,visit,rpos);
         rpo.add(bb);
     }
 
-    private static boolean isEmptyBackwardsScan(Ary<CFGNode> rpo, CFGNode next) {
+    // Should this test be inverted?
+    private static boolean shouldInvert(CFGNode t, CFGNode f, int bld) {
+        int tld = t.loopDepth(), fld = f.loopDepth();
+        // These next two are symmetric and can happen in any order; if `tld <
+        // bld` is true, the `fld < bld` must be false, or else both directions
+        // exit the loop... and the IF test would not be in the loop.
+
+        // true to exit a loop usually (and false falls into Yet Another Loop
+        // Block), invert if false is taking an empty backwards branch.
+        if( tld < bld ) return  forwardsEmptyScan(f,bld);
+        // false to exit a loop, normally invert to true (except empty back)
+        if( fld < bld ) return !forwardsEmptyScan(t,bld);
+
+        // Not exiting the loop; staying at this depth (or going deeper loop).
+
+        // Jump/true to an empty backwards block
+        if( forwardsEmptyScan(t,bld) ) return false;
+        if( forwardsEmptyScan(f,bld) ) return true ;
+
+        // Fall/false into a full block, Jump/true to an empty block.
+        if( f.nOuts()>1 && t.nOuts()==1 ) return false;
+        if( t.nOuts()>1 && f.nOuts()==1 ) return true ;
+        // Everything else equal, use pre-order
+        return t._pre > f._pre;
+    }
+
+    // Do we continue forwards from 'c' to the Loop back edge, without doing
+    // anything else?  Then this branch can directly jump to the loop head.
+    private static boolean forwardsEmptyScan( CFGNode c, int bld ) {
+        if( c.nOuts()!=1 || c.loopDepth()!=bld ) return false;
+        return c.uctrl() instanceof RegionNode cfg &&
+            (cfg instanceof LoopNode || forwardsEmptyScan(cfg,bld));
+    }
+
+    // Is the CFG from "next" to the end empty?  This means jumping to "next"
+    // will naturally fall into the end.
+    private static boolean backwardsEmptyScan(Ary<CFGNode> rpo, CFGNode next) {
         for( int i=rpo._len-1; rpo.at(i)!=next; i-- )
             if( rpo.at(i).nOuts()!=1 )
                 return false;
         return true;
     }
 
+    // --------------------------------------------------
     // Write encoding bits in order into a big byte array.
     // Record opcode start and length.
     public FunNode _fun;        // Currently encoding function
@@ -310,6 +340,7 @@ else if( bb instanceof FunNode fun ) {
         padN(16,_bits);
     }
 
+    // --------------------------------------------------
     // Short-form RIP-relative support: replace short encodings with long
     // encodings and expand the code, changing all the offsets.
     private void compactShortForm() {
@@ -337,12 +368,15 @@ private void compactShortForm() {
                     slide += (newStart+15 & -16)-newStart;
                 }
                 _opStart[bb._nid] += slide;
+                // Slide down all other (non-CFG) ops in the block
+                for( Node n : bb._outputs )
+                    if( n instanceof MachNode && !(n instanceof CFGNode) )
+                        _opStart[n._nid] += slide;
                 if( bb instanceof RIPRelSize riprel ) {
-                    CFGNode target = ((CFGNode)bb.out(0)).uctrlSkipEmpty();
+                    CFGNode target = (bb instanceof IfNode iff ? iff.cproj(0) : (CFGNode)bb.out(0)).uctrlSkipEmpty();
                     // Delta is from opStart to opStart.  X86 at least counts
                     // the delta from the opEnd, but we don't have the end until
                     // we decide the size - so the encSize has to deal
-                    assert _opStart[target._nid] > 0;
                     int delta = _opStart[target._nid] - _opStart[bb._nid];
                     byte opLen = riprel.encSize(delta);
                     // Recorded size is smaller than the current size?
@@ -374,16 +408,25 @@ private void compactShortForm() {
     }
 
 
+    // --------------------------------------------------
+    // Patch local encodings now
+    void patchLocalRelocations() {
+        // Walk the local code-address relocations
+        for( Node src : _internals.keySet() ) {
+            int start  = _opStart[src._nid];
+            Node dst =  _internals.get(src);
+            int target = _opStart[dst._nid];
+            ((RIPRelSize)src).patch(this, start, _opLen[src._nid], target - start);
+        }
+    }
+
+    // --------------------------------------------------
     // Write the constant pool into the BAOS and optionally patch locally
     void writeConstantPool( BAOS bits, boolean patch ) {
-        HashSet<Relo> ts = new HashSet<>();
-        for( Relo t : _bigCons.values() ) {
-            if( ts.contains(t) )
-                throw Utils.TODO(); // Dup!  Compress!
-            ts.add(t);
-        }
         padN(16,bits);
 
+        HashMap<SONType,Integer> targets = new HashMap<>();
+
         // By log size
         for( int log = 3; log >= 0; log-- ) {
             // Write the 8-byte constants
@@ -391,30 +434,40 @@ void writeConstantPool( BAOS bits, boolean patch ) {
                 Relo relo = _bigCons.get(op);
                 if( relo._t.log_size()==log ) {
                     // Map from relo to constant start and patch
-                    relo._target = bits.size();
+                    Integer target = targets.get(relo._t);
+                    if( target==null ) {
+                        targets.put(relo._t,target = bits.size());
+                        // Put constant into code space.
+                        if( relo._t instanceof SONTypeTuple tt ) // Constant tuples put all entries
+                            for( SONType tx : tt._types )
+                                addN(log,tx,bits);
+                        else
+                            addN(log,relo._t,bits);
+                    }
+                    relo._target = target;
                     relo._opStart= _opStart[op._nid];
                     // Go ahead and locally patch in-memory
                     if( patch )
                         ((RIPRelSize)op).patch(this, relo._opStart, _opLen[op._nid], relo._target - relo._opStart);
-                    // Put constant into code space.
-                    if( relo._t instanceof SONTypeTuple tt ) // Constant tuples put all entries
-                        for( SONType tx : tt._types )
-                            addN(log,tx,bits);
-                    else
-                        addN(log,relo._t,bits);
                 }
             }
         }
     }
 
 
-    // Patch local encodings now
-    void patchLocalRelocations() {
-        // Walk the local code-address relocations
-        for( Node src : _internals.keySet() ) {
-            Node dst =  _internals.get(src);
-            int target = _opStart[dst._nid];
+    // A series of libc/external calls that Simple can link against in a JIT.
+    // Since no runtime in the JVM process, using magic numbers for the CPU
+    // emulators to pick up on.
+    public static int SENTINAL_CALLOC = -4;
+
+    void patchGlobalRelocations() {
+        for( Node src : _externals.keySet() ) {
             int start  = _opStart[src._nid];
+            String dst =  _externals.get(src);
+            int target = switch( dst ) {
+            case "calloc" -> SENTINAL_CALLOC;
+            default -> throw Utils.TODO();
+            };
             ((RIPRelSize)src).patch(this, start, _opLen[src._nid], target - start);
         }
     }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/GlobalCodeMotion.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/GlobalCodeMotion.java
index 86712da..deba31f 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/GlobalCodeMotion.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/GlobalCodeMotion.java
@@ -160,7 +160,7 @@ private static void breadth(Node stop, Node[] ns, CFGNode[] late) {
                         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._isLoad )
                     for( Node memuse : ld.mem()._outputs )
                         if( late[memuse._nid]==null &&
                             // New makes new memory, never crushes load memory
@@ -180,8 +180,8 @@ private static void breadth(Node stop, Node[] ns, CFGNode[] late) {
                 if( def!=null && late[def._nid]==null ) {
                     work.push(def);
                     // if the def has a load use, maybe the load can fire
-                    for( Node ld : def._outputs )
-                        if( ld instanceof LoadNode && late[ld._nid]==null )
+                    for( Node out : def._outputs )
+                        if( out instanceof MemOpNode ld && ld._isLoad && late[ld._nid]==null )
                             work.push(ld);
                 }
         }
@@ -197,7 +197,7 @@ private static void _doSchedLate(Node n, Node[] ns, CFGNode[] late) {
               lca = use_block(n,use, late)._idom(lca,null);
 
         // Loads may need anti-dependencies, raising their LCA
-        if( n instanceof LoadNode load )
+        if( n instanceof MemOpNode load && load._isLoad )
             lca = find_anti_dep(lca,load,early,late);
 
         // Walk up from the LCA to the early, looking for best place.  This is
@@ -235,7 +235,7 @@ private static boolean better( CFGNode lca, CFGNode best ) {
                 (lca.idepth() > best.idepth() || best instanceof IfNode);
     }
 
-    private static CFGNode find_anti_dep(CFGNode lca, LoadNode load, CFGNode early, CFGNode[] late) {
+    private static CFGNode find_anti_dep(CFGNode lca, MemOpNode load, CFGNode early, CFGNode[] late) {
         // 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() )
@@ -243,10 +243,12 @@ private static CFGNode find_anti_dep(CFGNode lca, LoadNode load, CFGNode early,
         // Walk load->mem uses, looking for Stores causing an anti-dep
         for( Node mem : load.mem()._outputs ) {
             switch( mem ) {
-            case StoreNode st:
-                assert late[st._nid]!=null;
-                lca = anti_dep(load,late[st._nid],st.cfg0(),lca,st);
-                break;
+            case MemOpNode st:
+                if( !st._isLoad ) {
+                    assert late[mem._nid] != null;
+                    lca = anti_dep( load, late[mem._nid], mem.cfg0(), lca, st );
+                }
+                break; // Loads do not cause anti-deps on other loads
             case CallNode st:
                 assert late[st._nid]!=null;
                 lca = anti_dep(load,late[st._nid],st.cfg0(),lca,st);
@@ -259,7 +261,6 @@ private static CFGNode find_anti_dep(CFGNode lca, LoadNode load, CFGNode early,
                         lca = anti_dep(load,phi.region().cfg(i),load.mem().cfg0(),lca,null);
                 break;
             case NewNode st: break;
-            case LoadNode ld: break; // Loads do not cause anti-deps on other loads
             case ReturnNode ret: break; // Load must already be ahead of Return
             case MemMergeNode ret: break; // Mem uses now on ScopeMin
             case NeverNode never: break;
@@ -270,16 +271,13 @@ private static CFGNode find_anti_dep(CFGNode lca, LoadNode load, CFGNode early,
     }
 
     //
-    private static CFGNode anti_dep( LoadNode load, CFGNode stblk, CFGNode defblk, CFGNode lca, Node st ) {
-        // Walk store blocks "reach" from its scheduled location to its earliest
-        for( ; stblk != defblk.idom(); stblk = stblk.idom() ) {
-            // Store and Load overlap, need anti-dependence
-            if( stblk._anti==load._nid ) {
-                lca = stblk._idom(lca,null); // Raise Loads LCA
-                if( lca == stblk && st != null && st._inputs.find(load) == -1 ) // And if something moved,
-                    st.addDef(load);   // Add anti-dep as well
-                return lca;            // Cap this stores' anti-dep to here
-            }
+    private static CFGNode anti_dep( MemOpNode load, CFGNode stblk, CFGNode defblk, CFGNode lca, Node st ) {
+        // Store and Load overlap, need anti-dependence
+        if( stblk._anti==load._nid ) {
+            lca = stblk._idom(lca,null); // Raise Loads LCA
+            if( lca == stblk && st != null && st._inputs.find(load) == -1 ) // And if something moved,
+                st.addDef(load); // Add anti-dep as well
+            return lca;          // Cap this stores' anti-dep to here
         }
         return lca;
     }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/IFG.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/IFG.java
index a67cf16..37005a7 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/IFG.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/IFG.java
@@ -173,7 +173,7 @@ private static void do_node(RegAlloc alloc, Node n) {
             // Look for a must-use single register conflicting with some other must-def.
             if( n instanceof MachNode m ) {
                 RegMask ni_mask = m.regmap(i);
-                if( ni_mask.size1() ) { // Must-use single register
+                if( ni_mask!=null && ni_mask.size1() ) { // Must-use single register
                     // Search all current live
                     for( LRG tlrg : TMP.keySet() ) {
                         assert tlrg.leader();
@@ -452,8 +452,8 @@ private static int pickRiskyScore( LRG lrg ) {
         // Always pick callee-save registers as being very large area recovered
         // and very cheap to spill.
         if( lrg._machDef instanceof CalleeSaveNode )
-            return 1000000-2;
-        if( lrg._splitDef != null && lrg._splitDef.in(1) instanceof CalleeSaveNode &&
+            return 1000000-2-lrg._mask.firstReg();
+        if( lrg._splitDef != null && lrg._splitDef. in(1) instanceof CalleeSaveNode &&
             lrg._splitUse != null && lrg._splitUse.out(0) instanceof ReturnNode )
             return 1000000-1;
 
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/LRG.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/LRG.java
index 79b2ad7..f71851b 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/LRG.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/LRG.java
@@ -44,11 +44,6 @@ public class LRG {
     public MachNode _machDef, _machUse;
     short _uidx;                // _machUse input
 
-    // Mask set to empty via a kill-mask.  This is usually a capacity kill,
-    // which means we need to split and spill into memory - which means even if
-    // the def-side has many registers, it MUST spill.
-    boolean _killed;
-
     // Some splits used in biased coloring
     MachConcreteNode _splitDef, _splitUse;
 
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/LinkMem.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/LinkMem.java
index 39f3dcb..53cf805 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/LinkMem.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/LinkMem.java
@@ -6,9 +6,14 @@ public class LinkMem {
     LinkMem( CodeGen code ) { _code = code; }
 
     public CodeGen link() {
+        Encoding enc = _code._encoding;
+
+        // Patch external calls internally
+        enc.patchGlobalRelocations();
+
         // Write any large constants into a constant pool; they
         // are accessed by RIP-relative addressing.
-        _code._encoding.writeConstantPool(_code._encoding._bits,true);
+        enc.writeConstantPool(enc._bits,true);
 
         return _code;
     }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/ListScheduler.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/ListScheduler.java
index 220e7fb..5516492 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/ListScheduler.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/ListScheduler.java
@@ -48,6 +48,9 @@ private XSched init(CFGNode bb, Node n) {
                     computeSingleRDef(bb,use);
             else
                 computeSingleRDef(bb,n);
+
+            if( n instanceof NewNode || n instanceof CallNode )
+                bb.fun()._hasCalls = true;
             return this;
         }
 
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/Machine.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/Machine.java
index 73acfd1..b04ddbe 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/Machine.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/Machine.java
@@ -1,48 +1,93 @@
 package com.compilerprogramming.ezlang.compiler.codegen;
 
 import com.compilerprogramming.ezlang.compiler.nodes.*;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFunPtr;
 
 abstract public class Machine {
     // Human readable machine name.  Something like "x86-64" or "arm" or "risc5"
     public abstract String name();
     // Default opcode size for printing; 4 bytes for most 32-bit risc chips
     public int defaultOpSize() { return 4; }
+    // Human-readable name for a register number, e.g. "RAX" or "R0"
+    public abstract String[] regs();
     // Create a split op; any register to any register, including stack slots
     public abstract SplitNode split( String kind, byte round, LRG lrg);
-    // List of caller-save registers
-    public abstract RegMask callerSave();
-    // List of callee-save registers
-    public abstract RegMask calleeSave();
+    // List of caller-save registers, as a 64bit mask
+    public abstract long callerSave();
+    // List of never-save registers, e.g. RSP or a ZERO register if you have one
+    public abstract long neverSave();
+    // Call Argument Mask
+    public abstract RegMask callArgMask(SONTypeFunPtr tfp, int arg);
+    // Return register mask, based on signature (GPR vs FPR)
+    public abstract RegMask retMask(SONTypeFunPtr tfp);
+    // Return PC register
+    public abstract int rpc();
     // Return a MachNode unconditional branch
     public abstract CFGNode jump();
-    // Break an infinite loop
-    public abstract NeverNode never( CFGNode ctrl );
     // Instruction select from ideal nodes
     public abstract Node instSelect( Node n );
 
-    // Convert a register to a zero-based stack *slot*, or -1.
-    // Stack slots are assumed 8 bytes each.
-    // Actual stack layout is up to each CPU.
-    // X86, with too many args & spills:
-    // | CALLER |
-    // |  argN  | // slot 1, required by callER
-    // +--------+
-    // |  RPC   | // slot 0, required by callER
-    // | callee | // slot 3, callEE
-    // | callee | // slot 2, callEE
-    // |  PAD16 |
-    // +--------+
-
-    // RISC/ARM, with too many args & spills:
-    // | CALLER |
-    // |  argN  | // slot 0, required by callER
-    // +--------+
-    // | callee | // slot 3, callEE: might be RPC
-    // | callee | // slot 2, callEE
-    // | callee | // slot 1, callEE
-    // |  PAD16 |
-    // +--------+
-    public abstract int stackSlot( int reg );
-    // Human-readable name for a register number, e.g. "RAX" or "R0"
-    public abstract String reg( int reg );
+    /** Stack Slot Numbering
+
+RA gives all registers a number, starting at 0.  Stack slots continue this
+numbering going up from the last register number.  Common register numbers are
+0-15 for GPRs, 16-31 for FPRs, 32 for flags, and stack slots starting at
+register#33 going up.  These numbers are machine-specific, YMMV, etc; e.g. RPC
+is only in stack slot 0 on X86; other cpus start with the rpc in a register
+which may spill into any generic spill slot.
+
+For ease of reading and printing, stack slots start again at slot#0 - but
+during RA they are actually biased by CPU.MAX_REG.
+
+Stack Layout during Reg Alloc:
+
+         Slot  Value    Description
+        ----  -------  -----------
+MAXREG+ R+1   argR     maxArgSlot(calleR tfp), can be 0
+        ...
+MAXREG+ 1     arg0
+        ----  -- Caller ------
+MAXREG+ 0     RPC
+MAXREG+ R+E+N PAD      optional alignment
+
+MAXREG+  +    Spills   Caller Frame Space; N spills
+MAXREG+ R+E+1 Spills
+
+MAXREG+ R+E   argE     maxArgSlot(calleE tfp), can be 0
+        ...
+MAXREG+ R+2   arg0
+        ----  -- Callee -------
+        ...
+
+Slots defined by maxArgSlot do not have to contain the actual passed argument;
+they can be "shadow slots".  If they exist, they are property of the frame /
+FunNode where the TFP comes from.
+
+For a given calleR, there are generally many calleE's and they all share the
+low-range stack slots.
+
+Example:, Win64, main(int arg) { calloc(1,size); }
+
+                           Post-Warp: SP+56   shadow-main-arg
+Pre-alloc               Post-Alloc    SP+48   RPC
+--- -------             ----------    -------------
+40  spill#3             SP+40         SP+40   spill#3
+39  spill#2             SP+32          ""
+38  spill#1             SP+24          ""
+37  spill#0             SP+16          ""     spill#0
+36  shadow-calloc#size  SP+ 8         SP+ 8   shadow-calloc#size
+35  shadow-calloc#1     SP+ 0         SP+ 0   shadow-calloc#1
+--- --- FRAME BASE ----------         ---- FRAME BASE -------
+34  shadow-main-arg     SP+56
+33  RPC                 SP+48
+
+
+    */
+
+
+    // Maximum stack slot (or 0) for the args in this TFP.  This will include
+    // shadow slots if defined in the ABI, even if all arguments are passed in
+    // registers.
+    public abstract short maxArgSlot(SONTypeFunPtr tfp);
+
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/RegAlloc.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/RegAlloc.java
index 09d6c6b..5c01edf 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/RegAlloc.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/RegAlloc.java
@@ -129,11 +129,18 @@ public short regnum( Node n ) {
     }
 
     // Printable register number for node n
-    String reg( Node n ) {
+    String reg( Node n ) { return reg(n,null); }
+    String reg( Node n, FunNode fun ) {
         LRG lrg = lrg(n);
         if( lrg==null ) return null;
+        // No register yet, use LRG
         if( lrg._reg == -1 ) return "V"+lrg._lrg;
-        return _code._mach.reg(lrg._reg);
+        // Chosen machine register unless stack-slot and past RA
+        String[] regs = _code._mach.regs();
+        if( lrg._reg < regs.length || _code._phase.ordinal() <= CodeGen.Phase.RegAlloc.ordinal() || fun==null )
+            return RegMask.reg(regs,lrg._reg);
+        // Stack-slot past RA uses the frame layout logic
+        return "[rsp+"+fun.computeStackOffset(_code,lrg._reg)+"]";
     }
 
     // -----------------------
@@ -141,13 +148,23 @@ String reg( Node n ) {
 
     public void regAlloc() {
         // Insert callee-save registers
-        FunNode lastFun=null;
+        String[] regs = _code._mach.regs();
+        long neverSave= _code._mach.neverSave();
+        for( CFGNode bb : _code._cfg )
+            if( bb instanceof FunNode fun ) {
+                ReturnNode ret = fun.ret();
+                int len = Math.min(regs.length,64);
+                for( int reg=0; reg<len; reg++ )
+                    if( !_code._callerSave.test(reg) && ((1L<<reg)&neverSave)==0 ) {
+                        ret.addDef(new CalleeSaveNode(fun,reg,regs[reg]));
+                        assert ret.regmap(ret.nIns()-1).firstReg()==reg;
+                    }
+            }
+        // Cache reg masks for New and Call
         for( CFGNode bb : _code._cfg ) {
-            if( bb instanceof FunNode fun )
-                insertCalleeSave(lastFun=fun);
-            // Leaf routine, or not?
-            // X86 requires 16b RSP aligned if NOT leaf
-            if( bb instanceof CallNode ) lastFun._hasCalls = true;
+            if( bb instanceof CallEndNode cend ) cend.cacheRegs(_code);
+            for( Node n : bb._outputs )
+                if( n instanceof NewNode nnn ) nnn.cacheRegs(_code);
         }
 
         // Top driver: repeated rounds of coloring and splitting.
@@ -178,20 +195,6 @@ private boolean graphColor(byte round) {
             IFG.color(round,this);      // If colorable
     }
 
-    // Insert callee-save registers.  Walk the callee-save RegMask ignoring any
-    // Parms, then insert a Parm and an edge from the Ret to the Parm with the
-    // callee-save register.
-    private void insertCalleeSave( FunNode fun ) {
-        RegMask saves = _code._mach.calleeSave();
-        ReturnNode ret = fun.ret();
-
-        for( short reg = saves.firstReg(); reg != -1; reg = saves.nextReg(reg) ) {
-            ret.addDef(new CalleeSaveNode(fun,reg,_code._mach.reg(reg)));
-            assert ((MachNode)ret).regmap(ret.nIns()-1).firstReg()==reg;
-        }
-    }
-
-
     // -----------------------
     // Split conflicted live ranges.
     void split(byte round) {
@@ -539,20 +542,26 @@ private SplitNode makeSplit( String kind, byte round, LRG lrg ) {
     // -----------------------
     // POST PASS: Remove empty spills that biased-coloring made
     private void postColor() {
-        int maxSlot = -1;
+        int maxReg = -1;
         for( CFGNode bb : _code._cfg ) { // For all ops
-            if( bb instanceof FunNode )
-                maxSlot = -1;
+            if( bb instanceof FunNode fun )
+                maxReg = -1;   // Reset for new function
+            // Compute frame size, based on arguments and largest reg seen
             if( bb instanceof ReturnNode ret )
-                ret.fun()._maxSlot = (short)maxSlot;
+                ret.fun().computeFrameAdjust(_code,maxReg);
+            // Raise frame size by max stack args passed, even if ignored
+            if( bb instanceof CallEndNode cend )
+                maxReg = Math.max(maxReg,cend._xslot);
+
             for( int j=0; j<bb.nOuts(); j++ ) {
                 Node n = bb.out(j);
-                if( lrg(n)!=null ) {
-                    int slot = _code._mach.stackSlot(lrg(n)._reg);
-                    maxSlot = Math.max(maxSlot,slot);
-                }
+                if( lrg(n)!=null )
+                    maxReg = Math.max(maxReg,lrg(n)._reg+1);
+                // Raise frame size by max stack args passed to New
+                if( n instanceof NewNode nnn )
+                    maxReg = Math.max(maxReg,nnn._xslot);
 
-                if( !(n instanceof SplitNode lo) ) continue;
+                if( !(n instanceof SplitNode ) ) continue;
                 int defreg = lrg(n     )._reg;
                 int usereg = lrg(n.in(1))._reg;
                 // Attempt to bypass split
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/RegMask.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/RegMask.java
index e09c801..1e7faff 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/RegMask.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/RegMask.java
@@ -110,13 +110,17 @@ public SB toString(SB sb) {
         Machine mach = CodeGen.CODE._mach;
         if( _bits0==0 && _bits1==0 ) return sb.p("[]");
         sb.p("[");
+        String[] regs = mach.regs();
         for( int i=0; i<64; i++ )
             if( ((_bits0 >> i)&1) != 0 )
-                sb.p(mach.reg(i)).p(",");
+                sb.p(reg(regs,i)).p(",");
         for( int i=0; i<64; i++ )
             if( ((_bits1 >> i)&1) != 0 )
-                sb.p(mach.reg(i+64)).p(",");
+                sb.p(reg(regs,i+64)).p(",");
         return sb.unchar().p("]");
     }
-}
 
+    public static String reg(String[] regs, int i) {
+        return i<regs.length ? regs[i] : "[stk#"+i+"]";
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/RegMaskRW.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/RegMaskRW.java
index f391fcc..4443f33 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/RegMaskRW.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/RegMaskRW.java
@@ -9,6 +9,11 @@ public boolean clr(int r) {
         else         _bits1 &= ~(1L<<(r-64));
         return _bits0!=0 || _bits1!=0;
     }
+    // sets bit at position r.
+    public void set(int r) {
+        if( r < 64 ) _bits0 |= (1L<<(r   ));
+        else         _bits1 |= (1L<<(r-64));
+    }
     @Override RegMaskRW and( RegMask mask ) {
         if( mask==null ) return this;
         _bits0 &= mask._bits0;

From 787840a8cede23acc2c173cdc03c34580efd7bb2 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Sat, 26 Apr 2025 00:43:54 +0100
Subject: [PATCH 064/119] Sync with final ch21

---
 .../ezlang/compiler/IterPeeps.java            |   2 -
 .../ezlang/compiler/SB.java                   |  23 +-
 .../ezlang/compiler/nodes/AddNode.java        |   2 +-
 .../ezlang/compiler/nodes/CFGNode.java        |   8 +-
 .../ezlang/compiler/nodes/CProjNode.java      |   4 +-
 .../ezlang/compiler/nodes/CallEndNode.java    |  40 +++-
 .../ezlang/compiler/nodes/CastNode.java       |   2 +-
 .../ezlang/compiler/nodes/ConstantNode.java   |   4 +-
 .../ezlang/compiler/nodes/FunNode.java        |  66 +++---
 .../ezlang/compiler/nodes/IfNode.java         |  12 +-
 .../ezlang/compiler/nodes/LoadNode.java       |   8 +-
 .../ezlang/compiler/nodes/LoopNode.java       |  23 +-
 .../ezlang/compiler/nodes/MemMergeNode.java   |  11 +-
 .../ezlang/compiler/nodes/MemOpNode.java      |  39 +++-
 .../ezlang/compiler/nodes/NeverNode.java      |   3 +-
 .../ezlang/compiler/nodes/NewNode.java        |  40 +++-
 .../ezlang/compiler/nodes/Node.java           |  41 ++--
 .../ezlang/compiler/nodes/PhiNode.java        |  87 +++++---
 .../ezlang/compiler/nodes/ProjNode.java       |   2 +-
 .../ezlang/compiler/nodes/RegionNode.java     |  44 +++-
 .../ezlang/compiler/nodes/ReturnNode.java     |  41 +++-
 .../ezlang/compiler/nodes/ShlNode.java        |   2 +-
 .../ezlang/compiler/nodes/SplitNode.java      |   5 +-
 .../ezlang/compiler/nodes/StopNode.java       |   3 +-
 .../ezlang/compiler/nodes/StoreNode.java      |   4 +-
 .../ezlang/compiler/nodes/StructNode.java     |   2 +-
 .../ezlang/compiler/print/ASMPrinter.java     | 204 ++++--------------
 .../ezlang/compiler/print/IRPrinter.java      |   1 -
 28 files changed, 423 insertions(+), 300 deletions(-)

diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/IterPeeps.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/IterPeeps.java
index c18aa90..f58ae9f 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/IterPeeps.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/IterPeeps.java
@@ -49,7 +49,6 @@ public class IterPeeps {
 
     public IterPeeps( long seed ) { _work = new WorkList<>(seed); }
 
-    @SuppressWarnings("unchecked")
     public <N extends Node> N add( N n ) { return (N)_work.push(n); }
 
     public void addAll( Ary<Node> ary ) { _work.addAll(ary); }
@@ -127,7 +126,6 @@ private boolean progressOnList(CodeGen code) {
      * Classic WorkList, with a fast add/remove, dup removal, random pull.
      * The Node's nid is used to check membership in the worklist.
      */
-    @SuppressWarnings("unchecked")
     public static class WorkList<E extends Node> {
 
         private Node[] _es;
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/SB.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/SB.java
index 0ed652d..fe7f97d 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/SB.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/SB.java
@@ -29,28 +29,21 @@ else if( Double.isInfinite(s) ) {
   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; }
-  // 1 byte, 2 hex digits, 8 bits
-  public SB hex1(int s) {
-    int digit = (s>>4) & 0xf;
-    _sb.append((char)((digit <= 9 ? '0' : ('A'-10))+digit));
-    digit = s & 0xf;
-    _sb.append((char)((digit <= 9 ? '0' : ('A'-10))+digit));
+  // 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;
   }
-  // 2 bytes, 4 hex digits, 16 bits, Big Endian
-  public SB hex2(int s) { return hex1(s>> 8).hex1(s); }
-  // 4 bytes, 8 hex digits, 32 bits, Big Endian
-  public SB hex4(int s) { return hex2(s>>16).hex2(s); }
-  // 8 bytes, 16 hex digits, 64 bits, Big Endian
-    public SB hex8(long s) { return hex4((int)(s>>32)).hex4((int)s); }
-
   // Fixed width field
   public SB fix( int sz, String s ) {
     for( int i=0; i<sz; i++ )
       _sb.append( i < s.length() ? s.charAt(i) : ' ');
     return this;
   }
-  public char at(int idx ) { return _sb.charAt(idx); }
 
   // Not spelled "p" on purpose: too easy to accidentally say "p(1.0)" and
   // suddenly call the autoboxed version.
@@ -90,8 +83,6 @@ public boolean was_nl() {
   //
   public SB unchar() { return unchar(1); }
   public SB unchar(int x) { _sb.setLength(_sb.length()-x); return this; }
-  public SB setLen(int len) { _sb.setLength(len); return this; }
-  public String subString(int start, int end ) { return _sb.substring(start,end); }
 
   public SB clear() { _sb.setLength(0); return this; }
   public int len() { return _sb.length(); }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/AddNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/AddNode.java
index 0098b61..060fba6 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/AddNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/AddNode.java
@@ -96,7 +96,7 @@ public Node idealize () {
         // If lhs.in(2) is not a constant, we add ourselves as a dependency
         // because if it later became a constant then we could make this
         // transformation.
-        if( lhs.in(2).addDep(this)._type.isConstant() && rhs._type.isConstant() )
+        if( addDep(lhs.in(2))._type.isConstant() && rhs._type.isConstant() )
             return new AddNode(lhs.in(1),new AddNode(lhs.in(2),rhs).peephole());
 
 
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CFGNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CFGNode.java
index 2c348b7..b1c2a94 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CFGNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CFGNode.java
@@ -53,7 +53,7 @@ public CFGNode uctrl() {
 
     // Used by the encoding / final BB layout
     public CFGNode uctrlSkipEmpty() {
-        CFGNode x = this, y=null;
+        CFGNode x = this, y;
         while( x.nOuts() == 1 && (y=x.uctrl())!=null ) // Skip empty blocks
             x = y;
         return x;
@@ -80,8 +80,8 @@ public CFGNode _idom(CFGNode rhs, Node dep) {
         CFGNode lhs = this;
         while( lhs != rhs ) {
             var comp = lhs.idepth() - rhs.idepth();
-            if( comp >= 0 ) lhs = ((CFGNode)lhs.addDep(dep)).idom();
-            if( comp <= 0 ) rhs = ((CFGNode)rhs.addDep(dep)).idom();
+            if( comp >= 0 ) lhs = (dep==null ? lhs : dep.addDep(lhs)).idom();
+            if( comp <= 0 ) rhs = (dep==null ? rhs : dep.addDep(rhs)).idom();
         }
         return lhs;
     }
@@ -102,7 +102,7 @@ public FunNode fun() {
     public LoopNode loop() { return _ltree._head; }
     public int loopDepth() { return _ltree==null ? 0 : _ltree.depth(); }
 
-    LoopTree _ltree;
+    public LoopTree _ltree;
     public int _pre;            // Pre-order numbers for loop tree finding
     static class LoopTree {
         LoopTree _par;
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CProjNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CProjNode.java
index 9e2f384..9c18492 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CProjNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CProjNode.java
@@ -44,7 +44,7 @@ public Node idealize() {
         }
 
         // Flip a negating if-test, to remove the not
-        if( ctrl() instanceof IfNode iff && iff.pred().addDep(this) instanceof NotNode not )
+        if( ctrl() instanceof IfNode iff && addDep(iff.pred()) instanceof NotNode not )
             return new CProjNode(new IfNode(iff.ctrl(),not.in(1)).peephole(),1-_idx,_idx==0 ? "False" : "True");
 
         // Copy of some other input
@@ -58,7 +58,7 @@ public void invert() {
     }
 
     @Override
-    boolean eq( Node n ) { return _idx == ((CProjNode)n)._idx; }
+    public boolean eq( Node n ) { return _idx == ((CProjNode)n)._idx; }
 
     @Override
     int hash() { return _idx; }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CallEndNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CallEndNode.java
index c705f53..07c2aa8 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CallEndNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CallEndNode.java
@@ -1,7 +1,11 @@
 package com.compilerprogramming.ezlang.compiler.nodes;
 
 import com.compilerprogramming.ezlang.compiler.Compiler;
+import com.compilerprogramming.ezlang.compiler.SB;
 import com.compilerprogramming.ezlang.compiler.codegen.CodeGen;
+import com.compilerprogramming.ezlang.compiler.codegen.Encoding;
+import com.compilerprogramming.ezlang.compiler.codegen.RegMask;
+import com.compilerprogramming.ezlang.compiler.codegen.RegMaskRW;
 import com.compilerprogramming.ezlang.compiler.sontypes.*;
 import java.util.BitSet;
 
@@ -38,7 +42,7 @@ public SONType compute() {
             return SONTypeTuple.RET.dual();
         SONType ret = SONType.BOTTOM;
         SONTypeMem mem = SONTypeMem.BOT;
-        if( call.fptr().addDep(this)._type instanceof SONTypeFunPtr tfp ) {
+        if( addDep(call.fptr())._type instanceof SONTypeFunPtr tfp ) {
             ret = tfp.ret();
             // Here, if I can figure out I've found *all* callers, then I can meet
             // across the linked returns and join with the function return type.
@@ -84,10 +88,10 @@ public Node idealize() {
                         return this;
                     }
                 } else {
-                    fun.addDep(this);
+                    addDep(fun);
                 }
             } else { // Function ptr has multiple users (so maybe multiple call sites)
-                fptr.addDep(this);
+                addDep(fptr);
             }
         }
 
@@ -97,4 +101,34 @@ public Node idealize() {
     @Override public Node pcopy(int idx) {
         return _folding ? in(1).in(idx) : null;
     }
+
+    // ------------
+    // MachNode specifics, shared across all CPUs
+    public int _xslot;
+    private RegMask _retMask;
+    private RegMask _kills;
+    public void cacheRegs(CodeGen code) {
+        // Return mask depends on TFP (either GPR or FPR)
+        _retMask = code._mach.retMask(call().tfp());
+        // Kill mask is all caller-saves, and any mirror stack slots for args
+        // in registers.
+        RegMaskRW kills = code._callerSave.copy();
+        // Start of stack slots
+        int maxReg = code._mach.regs().length;
+        // Incoming function arg slots, all low numbered in the RA
+        int fslot = fun()._maxArgSlot;
+        // Killed slots for this calls outgoing args
+        int xslot = code._mach.maxArgSlot(call().tfp());
+        _xslot = (maxReg+fslot)+xslot;
+        for( int i=0; i<xslot; i++ )
+            kills.set((maxReg+fslot)+i);
+        _kills = kills;
+    }
+    public String op() { return "cend"; }
+    public RegMask regmap(int i) { return null; }
+    public RegMask outregmap() { return null; }
+    public RegMask outregmap(int idx) { return idx==2  ? _retMask : null; }
+    public RegMask killmap() { return _kills; }
+    public void encoding( Encoding enc ) { }
+    public void asm(CodeGen code, SB sb) {  }
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CastNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CastNode.java
index 5808cd8..58de87f 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CastNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CastNode.java
@@ -46,7 +46,7 @@ public Node idealize() {
     }
 
     @Override
-    boolean eq(Node n) {
+    public boolean eq(Node n) {
         CastNode cast = (CastNode)n; // Contract
         return _t==cast._t;
     }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ConstantNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ConstantNode.java
index 8ad7f3f..4eb1798 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ConstantNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ConstantNode.java
@@ -26,7 +26,7 @@ public ConstantNode( SONType type ) {
         _con = _type = type;
     }
     public ConstantNode( Node con, SONType t ) { super(con);  _con = t;  }
-    public ConstantNode( ConstantNode con ) { super(con);  _con = con._type;  }
+    public ConstantNode( ConstantNode con ) { this(con,con._type);  }
 
     public static Node make( SONType type ) {
         if( type== SONType. CONTROL ) return new CtrlNode();
@@ -44,7 +44,7 @@ public static Node make( SONType type ) {
     public StringBuilder _print1(StringBuilder sb, BitSet visited) {
         if( _con instanceof SONTypeFunPtr tfp && tfp.isConstant() ) {
             FunNode fun = CodeGen.CODE.link(tfp);
-            if( fun._name != null )
+            if( fun!=null && fun._name != null )
                 return sb.append("{ ").append(fun._name).append("}");
         }
         return sb.append(_con==null ? "---" : _con.print(new SB()));
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/FunNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/FunNode.java
index a0eb0be..9992008 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/FunNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/FunNode.java
@@ -1,6 +1,9 @@
 package com.compilerprogramming.ezlang.compiler.nodes;
 
 import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.CodeGen;
+import com.compilerprogramming.ezlang.compiler.codegen.Encoding;
+import com.compilerprogramming.ezlang.compiler.codegen.RegMask;
 import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
 import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFunPtr;
 import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeTuple;
@@ -164,36 +167,51 @@ private static boolean walkDown( Node n, BitSet cfgs, BitSet body, BitSet visit
     }
 
 
-    // | CALLER |
-    // |        |
-    // | argN+1 | // slot 1, callER +32
-    // | argN   | // slot 0, callER +24
-    // +--------+ // OLD FRAME
-    // |  PAD   | // Alignment pad  +16
-    // | callee | // slot 3, callEE + 8
-    // | callee | // slot 2, callEE + 0
-    // +--------+ // RSP
-
-    // TODO: lower this field into a generic FunMachNode.
-    // Replace all FunXXX variants with FunMachNode.
+    // ------------
+    // MachNode specifics, shared across all CPUs
 
     // Function may call other functions?  X86 requires 16b aligned SP on
     // outbound calls, but not leaf.  Set during an unrelated RA scan.
     public boolean _hasCalls;
 
-    // Max observed spill slot, set during RA postcolor
-    public short _maxSlot = -1;
-    // Max argument slot, set during early Encoding.
-    public short _maxArgSlot = -1;
-    // Frame adjust, including padding, st during early Encoding
-    public short _frameAdjust;
+    // Maximum incoming argument slot, based on function type
+    public int _maxArgSlot;
+
+    // Frame adjust, including padding, set during early Encoding
+    public int _frameAdjust;
 
+    public String op() { return _frameAdjust==0 ? "entry" : "subi"; }
+    public void postSelect(CodeGen code) {
+        code.link(this);
+        // Max slot seen past space reserved for incoming arguments
+        _maxArgSlot = code._mach.maxArgSlot(sig());
+    }
+    public RegMask regmap(int i) { return null; }
+    public RegMask outregmap() { return null; }
+    public void encoding( Encoding enc ) { throw Utils.TODO();  }
+    public void asm(CodeGen code, SB sb) {
+        if( _frameAdjust!=0 )
+            sb.p("rsp -= #").p(_frameAdjust);
+    }
 
-    // Convert a stack slot, based on the frame, into an offset.  Stack layout
-    // is the same for all CPUs; X86 will pre-spill the RPC.
-    public final int computeStackSlot(int slotN) {
-        return slotN < _maxArgSlot
-            ? slotN + _frameAdjust
-            : slotN - _maxArgSlot;
+    public void computeFrameAdjust(CodeGen code, int maxReg) {
+        // Max slot seen, or 0.
+        int maxSlot = Math.max(maxReg - code._mach.regs().length,_maxArgSlot);
+        // Frame adjust before rounding, can be negative for no spills
+        int size = maxSlot - _maxArgSlot;
+        _frameAdjust = size*8;
     }
+
+    // Given a high register number and during encoding compute the stack
+    // pointer offset.  Pre-RA we lack the fun._frameAdjust.  We also need a
+    // FunNode.
+    public int computeStackOffset(CodeGen code, int reg) {
+        String[] regs = code._mach.regs();
+        int stackSlot = reg - regs.length;
+        int slotRotate = stackSlot < _maxArgSlot
+            ? stackSlot + (_frameAdjust>>3)
+            : stackSlot - _maxArgSlot;
+        return slotRotate*8;
+    }
+
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/IfNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/IfNode.java
index b9475bd..862c8b6 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/IfNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/IfNode.java
@@ -20,7 +20,9 @@ public IfNode(Node ctrl, Node pred) {
     @Override
     public StringBuilder _print1(StringBuilder sb, BitSet visited) {
         sb.append("if( ");
-        return in(1)._print0(sb, visited).append(" )");
+        if( in(1)==null ) sb.append("never");
+        else in(1)._print0(sb, visited);
+        return sb.append(" )");
     }
 
     public Node ctrl() { return in(0); }
@@ -57,19 +59,19 @@ public Node idealize() {
         // test on either the true or false branch, that side wins.
         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 ) {
+                if( addDep(dom) instanceof IfNode iff && addDep(iff.pred())==pred() && prior instanceof CProjNode prj ) {
                     setDef(1,con( prj._idx==0 ? 1 : 0 ));
                     return this;
                 }
         return null;
     }
 
-    // MachNode variants need to support this and invert the conditional test.
+    // MachNode variants need to support this and negate the conditional test.
     // The following CProjs will be inverted by the caller.
-    public void invert() { throw Utils.TODO(); }
+    public void negate() { throw Utils.TODO(); }
 
     // Negate the sense of a test
-    public static String invert( String bop ) {
+    public static String negate( String bop ) {
         return switch( bop ) {
         case "<"  -> ">=";
         case "<=" -> ">" ;
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/LoadNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/LoadNode.java
index fdec37a..4607e8e 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/LoadNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/LoadNode.java
@@ -19,7 +19,7 @@ public class LoadNode extends MemOpNode {
      * @param off   The offset inside the struct base
      */
     public LoadNode(String name, int alias, SONType glb, Node mem, Node ptr, Node off) {
-        super(name, alias, glb, mem, ptr, off);
+        super(name, alias, true, glb, mem, ptr, off);
     }
 
     // GraphVis DOT code (must be valid Java identifiers) and debugger labels
@@ -76,7 +76,7 @@ public Node idealize() {
         while( true ) {
             switch( mem ) {
             case StoreNode st:
-                if( ptr == st.ptr().addDep(this) && off() == st.off() )
+                if( ptr == addDep(st.ptr()) && off() == st.off() )
                     return extend(castRO(st.val())); // Proved equal
                 // Can we prove unequal?  Offsets do not overlap?
                 if( !off()._type.join(st.off()._type).isHigh() && // Offsets overlap
@@ -87,7 +87,7 @@ public Node idealize() {
                 break;
             case PhiNode phi:
                 // Assume related
-                phi.addDep(this);
+                addDep(phi);
                 break outer;
             case ConstantNode top: break outer;  // Assume shortly dead
             case ProjNode mproj: // Memory projection
@@ -181,7 +181,7 @@ private boolean profit(PhiNode phi, int idx) {
         if( px==null ) return false;
         if( px._type instanceof SONTypeMem mem && mem._t.isHighOrConst() ) return true;
         if( px instanceof StoreNode st1 && ptr()==st1.ptr() && off()==st1.off() ) return true;
-        px.addDep(this);
+        addDep(px);
         return false;
     }
 
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/LoopNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/LoopNode.java
index e3f63f3..4ff533c 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/LoopNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/LoopNode.java
@@ -2,6 +2,7 @@
 
 import com.compilerprogramming.ezlang.compiler.codegen.CodeGen;
 import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
+import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeMem;
 
 public class LoopNode extends RegionNode {
     public LoopNode( Node entry ) { super(null,entry,null); }
@@ -40,19 +41,23 @@ public StopNode forceExit( FunNode fun, StopNode stop ) {
             x = x.idom();
         }
         // Found a no-exit loop.  Insert an exit
-        NeverNode iff = CodeGen.CODE._mach == null
-            ? new NeverNode(back()) // Ideal never-branch
-            : CodeGen.CODE._mach.never(back()); // Machine never-branch
+        NeverNode iff = new NeverNode(back()); // Ideal never-branch
         CProjNode t = new CProjNode(iff,0,"True" ).init();
         CProjNode f = new CProjNode(iff,1,"False").init();
-        setDef(2,t);
-        iff._ltree = t._ltree = _ltree;
+        setDef(2,t);            // True continues loop, False (never) exits loop
         ReturnNode ret = fun.ret();
-        f._ltree = ret._ltree;
+        iff._ltree = t._ltree = _ltree;
+        ret._ltree = f._ltree = stop._ltree;
 
         // Now fold control into the exit.  Might have 1 valid exit, or an
         // XCtrl or a bunch of prior NeverNode exits.
         Node top = new ConstantNode(SONType.TOP).peephole();
+        Node memout = new MemMergeNode(false);
+        memout.addDef(f); // placeholder for control
+        for( Node u : _outputs )
+            if( u instanceof PhiNode phi && phi._type.isa(SONTypeMem.BOT) )
+                memout.addDef(phi);
+
         Node ctrl = ret.ctrl(), mem = ret.mem(), expr = ret.expr();
         if( ctrl!=null && ctrl._type != SONType.XCONTROL ) {
             // Perfect aligned exit?
@@ -61,17 +66,17 @@ public StopNode forceExit( FunNode fun, StopNode stop ) {
                   expr instanceof PhiNode prez && prez.region()==r ) ) {
                 // Nope, insert an aligned exit layer
                 RegionNode r = new RegionNode((Node)null,ctrl).init();
-                ctrl = r;  r._ltree = _ltree;
+                ctrl = r;  r._ltree = stop._ltree;
                 mem  = new PhiNode(r,mem ).init();
                 expr = new PhiNode(r,expr).init();
             }
             // Append new Never exit
             ctrl.addDef(f  );
-            mem .addDef(top);
+            mem .addDef(memout);
             expr.addDef(top);
         } else {
             ctrl = f;
-            mem  = top;
+            mem  = memout;
             expr = top;
         }
         ret.setDef(0,ctrl);
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MemMergeNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MemMergeNode.java
index 09487c9..6a36e74 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MemMergeNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MemMergeNode.java
@@ -65,11 +65,18 @@ public Node merge() {
         if( inProgress() ) return null;
 
         // If not merging any memory (all memory is just the default)
-        if( nIns()==2 )
+        if( allDefault() )
             return in(1);       // Become default memory
 
         return null;
     }
+    private boolean allDefault() {
+        for( int i=2; i<nIns(); i++ )
+            if( in(1) != in(i) )
+                return false;
+        return true;
+    }
+
 
     public Node in( Var v ) { return in(v._idx); }
 
@@ -149,7 +156,7 @@ void _useless( ) {
         }
     }
 
-    @Override boolean eq( Node n ) {
+    @Override public boolean eq( Node n ) {
         return this==n || !_inProgress;
     }
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MemOpNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MemOpNode.java
index 1806b27..0bf695d 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MemOpNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MemOpNode.java
@@ -20,27 +20,56 @@
  */
 public abstract class MemOpNode extends Node {
 
-    public final String _name;
+    // The equivalence alias class
     public final int _alias;
+
+    // True if load-like, false if store-like.
+    //
+    // Stores produce memory (maybe as part of a tuple with other things),
+    // loads do not.
+    //
+    // Loads might pick up anti-dependences on prior Stores, and never cause an
+    // anti-dependence themselves.
+    //
+    // Stores must maximally sink to the least dominator use.  Loads can be
+    // opportunistically hoisted.
+    public final boolean _isLoad;
+
     // Declared type; not final because it might be a forward-reference
     // which will be lazily improved when the reference is declared.
     public SONType _declaredType;
 
-    public MemOpNode(String name, int alias, SONType glb, Node mem, Node ptr, Node off) {
+    // A debug name, no semantic meaning
+    public final String _name;
+    public MemOpNode(String name, int alias, boolean isLoad, SONType glb, Node mem, Node ptr, Node off) {
         super(null, mem, ptr, off);
         _name  = name;
         _alias = alias;
         _declaredType = glb;
+        _isLoad = isLoad;
     }
-    public MemOpNode(String name, int alias, SONType glb, Node mem, Node ptr, Node off, Node value) {
-        this(name, alias, glb, mem, ptr, off);
+    public MemOpNode(String name, int alias, boolean isLoad, SONType glb, Node mem, Node ptr, Node off, Node value) {
+        this(name, alias, isLoad, glb, mem, ptr, off);
         addDef(value);
     }
     public MemOpNode( Node mach, MemOpNode mop ) {
         super(mach);
         _name  = mop==null ? null : mop._name;
         _alias = mop==null ? 0    : mop._alias;
+        _isLoad= mop==null ? true : mop._isLoad;
         _declaredType = mop==null ? SONType.BOTTOM : mop._declaredType;
+        if( mop==null )
+            throw Utils.TODO("Load or not");
+    }
+
+    // Used by M2 when translating graph to Simple
+    public MemOpNode( boolean isLoad ) {
+        super((Node)null);
+        _name         = null;
+        _alias        = 0;
+
+        _isLoad       = isLoad;
+        _declaredType = SONType.BOTTOM;
     }
 
     //
@@ -56,7 +85,7 @@ public MemOpNode( Node mach, MemOpNode mop ) {
 
 
     @Override
-    boolean eq(Node n) {
+    public boolean eq(Node n) {
         MemOpNode mem = (MemOpNode)n; // Invariant
         return _alias==mem._alias;    // When comparing types error to use "equals"; always use "=="
     }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/NeverNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/NeverNode.java
index b60f7a1..5e7f4e7 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/NeverNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/NeverNode.java
@@ -8,7 +8,8 @@
 
 // "Never true" for infinite loop exits
 public class NeverNode extends IfNode {
-    public NeverNode(Node ctrl) { super(ctrl,Compiler.ZERO); }
+    public NeverNode(NeverNode ctrl) { super(ctrl); }
+    public NeverNode(Node ctrl) { super(ctrl,null); }
 
     @Override public String label() { return "Never"; }
 
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/NewNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/NewNode.java
index 364cea0..8048e02 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/NewNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/NewNode.java
@@ -1,5 +1,9 @@
 package com.compilerprogramming.ezlang.compiler.nodes;
 
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.codegen.CodeGen;
+import com.compilerprogramming.ezlang.compiler.codegen.RegMask;
+import com.compilerprogramming.ezlang.compiler.codegen.RegMaskRW;
 import com.compilerprogramming.ezlang.compiler.sontypes.*;
 
 import java.util.BitSet;
@@ -76,8 +80,42 @@ public SONTypeTuple compute() {
     public Node idealize() { return null; }
 
     @Override
-    boolean eq(Node n) { return this == n; }
+    public boolean eq(Node n) { return this == n; }
 
     @Override
     int hash() { return _ptr.hashCode(); }
+
+    // ------------
+    // MachNode specifics, shared across all CPUs
+    public int _arg2Reg, _xslot;
+    private RegMask _arg3Mask;
+    private RegMask _retMask;
+    private RegMask _kills;
+    public void cacheRegs(CodeGen code) {
+        _arg2Reg  = code._mach.callArgMask(SONTypeFunPtr.CALLOC,2).firstReg();
+        _arg3Mask = code._mach.callArgMask(SONTypeFunPtr.CALLOC,3);
+        // Return mask depends on TFP (either GPR or FPR)
+        _retMask = code._mach.retMask(SONTypeFunPtr.CALLOC);
+        // Kill mask is all caller-saves, and any mirror stack slots for args
+        // in registers.
+        RegMaskRW kills = code._callerSave.copy();
+        // Start of stack slots
+        int maxReg = code._mach.regs().length;
+        // Incoming function arg slots, all low numbered in the RA
+        int fslot = cfg0().fun()._maxArgSlot;
+        // Killed slots for this calls outgoing args
+        int xslot = code._mach.maxArgSlot(SONTypeFunPtr.CALLOC);
+        _xslot = (maxReg+fslot)+xslot;
+        for( int i=0; i<xslot; i++ )
+            kills.set((maxReg+fslot)+i);
+        _kills = kills;
+    }
+    public String op() { return "alloc"; }
+    public RegMask regmap(int i) { return i==1 ? _arg3Mask : null; }
+    public RegMask outregmap() { return null; }
+    public RegMask outregmap(int idx) { return idx==1  ? _retMask : null; }
+    public RegMask killmap() { return _kills; }
+    public void asm(CodeGen code, SB sb) {
+        sb.p("#calloc, ").p(code.reg(size()));
+    }
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/Node.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/Node.java
index 6975943..75f8085 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/Node.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/Node.java
@@ -17,14 +17,14 @@
  * The Node class provides common functionality used by all subtypes.
  * Subtypes of Node specialize by overriding methods.
  */
-public abstract class Node {
+public abstract class Node implements Cloneable {
 
     /**
      * Each node has a unique dense Node ID within a compilation context
      * The ID is useful for debugging, for using as an offset in a bitvector,
      * as well as for computing equality of nodes (to be implemented later).
      */
-    public final int _nid;
+    public int _nid;
 
     /**
      * Inputs to the node. These are use-def references to Nodes.
@@ -33,7 +33,7 @@ public abstract class Node {
      * Ordering is required because e.g. "a/b" is different from "b/a".
      * The first input (offset 0) is often a {@link CFGNode} node.
      */
-    public final Ary<Node> _inputs;
+    public Ary<Node> _inputs;
 
     /**
      * Outputs reference Nodes that are not null and have this Node as an
@@ -44,7 +44,7 @@ public abstract class Node {
      * walked in either direction.  These outputs are typically used for
      * efficient optimizations but otherwise have no semantics meaning.
      */
-    public final Ary<Node> _outputs;
+    public Ary<Node> _outputs;
 
 
     /**
@@ -550,17 +550,16 @@ public SONType setType(SONType type) {
      * or output of this node, that is, it is at least one step away.  The node
      * being added must benefit from this node being peepholed.
      */
-    Node addDep( Node dep ) {
+    <N extends Node> N addDep( N dep ) {
         // Running peepholes during the big assert cannot have side effects
         // like adding dependencies.
-        if( CODE._midAssert ) return this;
-        if( dep == null ) return this;
-        if( _deps==null ) _deps = new Ary<>(Node.class);
-        if( _deps   .find(dep) != -1 ) return this; // Already on list
-        if( _inputs .find(dep) != -1 ) return this; // No need for deps on immediate neighbors
-        if( _outputs.find(dep) != -1 ) return this;
-        _deps.add(dep);
-        return this;
+        if( CODE._midAssert ) return dep;
+        if( dep._deps==null ) dep._deps = new Ary<>(Node.class);
+        if( dep._deps   .find(this) != -1 ) return dep; // Already on list
+        if( dep._inputs .find(this) != -1 ) return dep; // No need for deps on immediate neighbors
+        if( dep._outputs.find(this) != -1 ) return dep;
+        dep._deps.add(this);
+        return dep;
     }
 
     // Move the dependents onto a worklist, and clear for future dependents.
@@ -586,7 +585,7 @@ public void moveDepsToWorklist( ) {
     }
     // Subclasses add extra checks (such as ConstantNodes have same constant),
     // and can assume "this!=n" and has the same Java class.
-    boolean eq( Node n ) { return true; }
+    public boolean eq( Node n ) { return true; }
 
 
     // Cached hash.  If zero, then not computed AND this Node is NOT in the GVN
@@ -668,7 +667,7 @@ public Node swap12() {
     boolean allCons(Node dep) {
         for( int i=1; i<nIns(); i++ )
             if( !(in(i)._type.isConstant()) ) {
-                in(i).addDep(dep); // If in(i) becomes a constant later, will trigger some peephole
+                dep.addDep(in(i)); // If in(i) becomes a constant later, will trigger some peephole
                 return false;
             }
         return true;
@@ -680,6 +679,18 @@ boolean allCons(Node dep) {
     // Does not need to be implemented in isCFG() nodes.
     Node copy(Node lhs, Node rhs) { throw Utils.TODO("Binary ops need to implement copy"); }
 
+    public Node copy() {
+        Node n;
+        try { n = (Node)clone(); }
+        catch( Exception e ) { throw new RuntimeException(e); }
+        n._nid = CODE.getUID(); // allocate unique dense ID
+        n._inputs  = new Ary<>(Node.class);
+        n._outputs = new Ary<>(Node.class);
+        n._deps = null;
+        n._hash = 0;
+        return n;
+    }
+
     // Report any post-optimize errors
     public CompilerException err() { return null; }
 
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/PhiNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/PhiNode.java
index 5523b17..903f792 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/PhiNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/PhiNode.java
@@ -59,7 +59,7 @@ public SONType compute() {
         for (int i = 1; i < nIns(); i++)
             // If the region's control input is live, add this as a dependency
             // to the control because we can be peeped should it become dead.
-            if( r.in(i).addDep(this)._type != SONType.XCONTROL )
+            if( addDep(r.in(i))._type != SONType.XCONTROL )
                 t = t.meet(in(i)._type);
         return t;
     }
@@ -81,37 +81,15 @@ public Node idealize() {
             if( r.in(i)._type == SONType.XCONTROL )
                 return null;
 
-        // Pull "down" a common data op.  One less op in the world.  One more
-        // Phi, but Phis do not make code.
-        //   Phi(op(A,B),op(Q,R),op(X,Y)) becomes
-        //     op(Phi(A,Q,X), Phi(B,R,Y)).
-        Node op = in(1);
-        if( !isMem() && op.nIns()==3 && op.in(0)==null && same_op() ) {
-            assert !(op instanceof CFGNode);
-            Node[] lhss = new Node[nIns()];
-            Node[] rhss = new Node[nIns()];
-            lhss[0] = rhss[0] = in(0); // Set Region
-            for( int i=1; i<nIns(); i++ ) {
-                lhss[i] = in(i).in(1);
-                rhss[i] = in(i).in(2);
-            }
-            Node phi_lhs = new PhiNode(_label, _declaredType,lhss).peephole();
-            Node phi_rhs = new PhiNode(_label, _declaredType,rhss).peephole();
-            Node down = op.copy(phi_lhs,phi_rhs);
-            // Test not running backwards, which can happen for e.g. And's
-            if( down.compute().isa(compute()) )
-                return down;
-            in(1).in(1).addDep(this);
-            in(1).in(2).addDep(this);
-            in(2).in(1).addDep(this);
-            in(2).in(2).addDep(this);
-            down.kill();
-        }
+        // Generic "pull down op"
+        Node progress;
+        if( same_op() && (progress = drop_same_op()) != null )
+            return progress;
 
         // If merging Phi(N, cast(N)) - we are losing the cast JOIN effects, so just remove.
         if( nIns()==3 ) {
-            if( in(1) instanceof CastNode cast && cast.in(1).addDep(this)==in(2) ) return in(2);
-            if( in(2) instanceof CastNode cast && cast.in(1).addDep(this)==in(1) ) return in(1);
+            if( in(1) instanceof CastNode cast && addDep(cast.in(1))==in(2) ) return in(2);
+            if( in(2) instanceof CastNode cast && addDep(cast.in(1))==in(1) ) return in(1);
         }
         // If merging a null-checked null and the checked value, just use the value.
         // if( val ) ..; phi(Region,False=0/null,True=val);
@@ -124,7 +102,7 @@ public Node idealize() {
                 Node val = in(3-nullx);
                 if( val instanceof CastNode cast )
                     val = cast.in(1);
-                if( r.idom(this).addDep(this) instanceof IfNode iff && iff.pred().addDep(this)==val ) {
+                if( addDep(r.idom(this)) instanceof IfNode iff && addDep(iff.pred())==val ) {
                     // Must walk the idom on the null side to make sure we hit False.
                     CFGNode idom = (CFGNode)r.in(nullx);
                     while( idom != null && idom.nIns() > 0 && idom.in(0) != iff ) idom = idom.idom();
@@ -137,13 +115,52 @@ public Node idealize() {
         return null;
     }
 
+    // Same op on all Phi paths; all ops have only the Phi as a use.
+    // None have a control input.
     private boolean same_op() {
-        for( int i=2; i<nIns(); i++ )
-            if( in(1).getClass() != in(i).getClass() )
+        for( int i=1; i<nIns(); i++ ) {
+            Node op = in(i);
+            if( in(1).getClass() != op.getClass() || op.in(0)!=null || in(1).nIns() != op.nIns() )
+                return false;      // Wrong class or CFG bound or mismatched inputs
+            if( op.nOuts() > 1 ) { // Too many users, but addDep in case lose users
+                for( Node out : op._outputs )
+                    if( out!=null && out!=this )
+                        addDep(out);
                 return false;
+            }
+        }
         return true;
     }
 
+    private Node drop_same_op() {
+        assert !(in(1) instanceof CFGNode);
+        Node op = in(1);
+        Node cp = op.copy();
+        cp._type = null;    // Fresh type
+        cp.addDef(null);    // No control
+
+        for( int j=1; j<op.nIns(); j++ ) {
+            boolean needsPhi = false;
+            Node x = op.in(j); // Jth input from sample #1
+            for( int i=2; i<nIns(); i++ )
+                if( in(i).in(j) != x )
+                    { needsPhi=true; break; }
+            if( needsPhi ) {
+                x = new PhiNode(_label,op.in(j)._type.glb());
+                x.addDef(region());
+                for( int i=1; i<nIns(); i++ )
+                    x.addDef(in(i).in(j));
+                x = x.peephole();
+            }
+            cp.addDef(x);
+        }
+        // Test not running backwards, which can happen for e.g. And's
+        if( cp.compute().isa(compute()) )
+            return cp;
+        cp.kill();
+        return null;
+    }
+
     /**
      * If only single unique input, return it
      */
@@ -154,7 +171,7 @@ private Node singleUniqueInput() {
         for( int i=1; i<nIns(); i++ ) {
             // If the region's control input is live, add this as a dependency
             // to the control because we can be peeped should it become dead.
-            if( region().in(i).addDep(this)._type != SONType.XCONTROL && in(i) != this )
+            if( addDep(region().in(i))._type != SONType.XCONTROL && in(i) != this )
                 if( live == null || live == in(i) ) live = in(i);
                 else return null;
         }
@@ -166,7 +183,7 @@ boolean allCons(Node dep) {
         if( !(region() instanceof RegionNode r) ) return false;
         // When the region completes (is no longer in progress) the Phi can
         // become a "all constants" Phi, and the "dep" might make progress.
-        addDep(dep);
+        dep.addDep(this);
         if( r.inProgress() ) return false;
         return super.allCons(dep);
     }
@@ -177,7 +194,7 @@ public boolean inProgress() {
     }
 
     // Never equal if inProgress
-    @Override boolean eq( Node n ) {
+    @Override public boolean eq( Node n ) {
         return !inProgress();
     }
 
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ProjNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ProjNode.java
index dcac4b4..212caed 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ProjNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ProjNode.java
@@ -47,7 +47,7 @@ public SONType compute() {
     @Override public Node idealize() { return ((MultiNode)in(0)).pcopy(_idx); }
 
     @Override
-    boolean eq( Node n ) { return _idx == ((ProjNode)n)._idx; }
+    public boolean eq( Node n ) { return _idx == ((ProjNode)n)._idx; }
 
     @Override
     int hash() { return _idx; }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/RegionNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/RegionNode.java
index 3bccfab..34fa3cd 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/RegionNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/RegionNode.java
@@ -44,7 +44,7 @@ public Node idealize() {
         if( !hasPhi() &&         // No Phi users, just a control user
             in(1) instanceof CProjNode p1 &&
             in(2) instanceof CProjNode p2 &&
-            p1.in(0).addDep(this)==p2.in(0).addDep(this) &&
+            addDep(p1.in(0))==addDep(p2.in(0)) &&
             p1.in(0) instanceof IfNode iff ) {
             // Replace with the iff.ctrl directly
             if( nIns()==3 ) return iff.ctrl();
@@ -53,6 +53,29 @@ public Node idealize() {
             return delDef(2);
         }
 
+        // Flatten stack regions (no loops involved)
+        if( getClass() == RegionNode.class ) {
+            for( int i=1; i<nIns(); i++ )
+                if( cfg(i) instanceof RegionNode region && region.getClass() == RegionNode.class && region.nIns()>2 && !hasMidUser(region) ) {
+                    assert !region.inProgress();
+                    // Fold Phis
+                    for( Node use : _outputs ) {
+                        if( use instanceof PhiNode phi ) {
+                            PhiNode phi2 = phi.in(i) instanceof PhiNode phi2x && phi2x.region()==region ? phi2x : null;
+                            for( int j=1; j<region.nIns(); j++ )
+                                phi.addDef(phi2==null ? phi.in(i) : phi2.in(j));
+                            CodeGen.CODE.add(phi);
+                        }
+                    }
+
+                    // Fold Region
+                    for( int j=1; j<region.nIns(); j++ )
+                        addDef(region.in(j));
+                    setDef(i,Compiler.XCTRL);
+                    return this;
+                }
+        }
+
         return null;
     }
 
@@ -95,6 +118,23 @@ boolean hasPhi() {
         return false;
     }
 
+    // Is there a value in-between 2 Regions?  Difficult to correctly fuse
+    // without extensive testing, so just ignore.
+    boolean hasMidUser(RegionNode r) {
+        for( Node use : r._outputs ) {
+            if( use==this ) continue;
+            if( use instanceof PhiNode ) {
+                for( Node data : use._outputs ) {
+                    if( !(data instanceof PhiNode phi2) || phi2.region()!=this )
+                        { addDep(use); addDep(data); return true; }
+                }
+            } else
+                { addDep(use);  return true; } // Control user
+        }
+        return false;
+    }
+
+
     // Immediate dominator of Region is a little more complicated.
     @Override public int idepth() {
         if( _idepth!=0 ) return _idepth;
@@ -118,6 +158,6 @@ boolean hasPhi() {
     public boolean inProgress() { return nIns()>1 && in(nIns()-1) == null; }
 
     // Never equal if inProgress
-    @Override boolean eq( Node n ) { return !inProgress(); }
+    @Override public boolean eq( Node n ) { return !inProgress(); }
 
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ReturnNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ReturnNode.java
index b44328e..065bdcb 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ReturnNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ReturnNode.java
@@ -2,6 +2,10 @@
 
 import com.compilerprogramming.ezlang.compiler.Compiler;
 import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.Utils;
+import com.compilerprogramming.ezlang.compiler.codegen.CodeGen;
+import com.compilerprogramming.ezlang.compiler.codegen.Encoding;
+import com.compilerprogramming.ezlang.compiler.codegen.RegMask;
 import com.compilerprogramming.ezlang.compiler.sontypes.*;
 import com.compilerprogramming.ezlang.exceptions.CompilerException;
 
@@ -30,7 +34,7 @@ public ReturnNode(Node ctrl, Node mem, Node data, Node rpc, FunNode fun ) {
     public Node mem () { return in(1); }
     public Node expr() { return in(2); }
     public Node rpc () { return in(3); }
-    public FunNode fun() { return _fun; }
+    @Override public FunNode fun() { return _fun; }
 
     @Override
     public String label() { return "Return"; }
@@ -128,4 +132,39 @@ static CompilerException mixerr( boolean ti, boolean tf, boolean tp, boolean tn)
         if( tp || tn ) sb.p("reference and ");
         return Compiler.error(sb.unchar(5).toString());
     }
+
+
+    // ------------
+    // MachNode specifics, shared across all CPUs
+    public String op() {
+        return _fun._frameAdjust > 0 ? "addi" : "ret";
+    }
+    // Correct Nodes outside the normal edges
+    public void postSelect(CodeGen code) {
+        FunNode fun = (FunNode)rpc().in(0);
+        _fun = fun;
+        fun.setRet(this);
+    }
+    public RegMask regmap(int i) {
+        return i==2
+            ? CodeGen.CODE._mach.retMask(_fun.sig())
+            : CodeGen.CODE._retMasks[i];
+    }
+    public RegMask outregmap() { return null; }
+    public void encoding( Encoding enc ) { throw Utils.TODO(); }
+    public void asm(CodeGen code, SB sb) {
+        int frameAdjust = fun()._frameAdjust;
+        if( frameAdjust>0 )
+            sb.p("rsp += #").p(frameAdjust).p("\nret");
+        // Post code-gen, just print the "ret"
+        if( code._phase.ordinal() <= CodeGen.Phase.RegAlloc.ordinal() )
+            // Prints return reg (either RAX or XMM0), RPC and then the
+            // callee-save registers.
+            for( int i=2; i<nIns(); i++ )
+                sb.p(code.reg(in(i),fun())).p("  ");
+        // If we did not get the expected rpc, print which one we got
+        else if( code._regAlloc.regnum(rpc()) != code._mach.rpc() )
+            sb.p("[").p(code.reg(rpc())).p("]");
+    }
+
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ShlNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ShlNode.java
index 072f468..f8d59b5 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ShlNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ShlNode.java
@@ -37,7 +37,7 @@ public Node idealize() {
             if( (shl.value()&63)==0 )
                 return lhs;
             // (x + c) << i  =>  (x << i) + (c << i)
-            if( lhs instanceof AddNode add && add.addDep(this).in(2)._type instanceof SONTypeInteger c && c.isConstant() ) {
+            if( lhs instanceof AddNode add && addDep(add).in(2)._type instanceof SONTypeInteger c && c.isConstant() ) {
                 long sum = c.value() << shl.value();
                 if( Integer.MIN_VALUE <= sum  && sum <= Integer.MAX_VALUE )
                     return new AddNode(new ShlNode(add.in(1),rhs).peephole(), Compiler.con(sum) );
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/SplitNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/SplitNode.java
index 9a980ec..0040146 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/SplitNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/SplitNode.java
@@ -11,7 +11,10 @@ public abstract class SplitNode extends MachConcreteNode {
     public SplitNode(String kind, byte round, Node[] nodes) { super(nodes); _kind = kind; _round = round; }
     @Override public String op() { return "mov"; }
     @Override public StringBuilder _print1(StringBuilder sb, BitSet visited) {
-        return in(1)._print0(sb.append("mov("),visited).append(")");
+        sb.append("mov(");
+        if( in(1) == null ) sb.append("---");
+        else in(1)._print0(sb,visited);
+        return sb.append(")");
     }
     @Override public SONType compute() { return in(0)._type; }
     @Override public Node idealize() { return null; }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StopNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StopNode.java
index 1d13a7f..f0fbdfa 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StopNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StopNode.java
@@ -25,7 +25,8 @@ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
         if( ret()!=null ) return ret()._print0(sb,visited);
         sb.append("Stop[ ");
         for( Node ret : _inputs )
-            ret._print0(sb, visited).append(" ");
+            if( ret!=null )
+                ret._print0(sb, visited).append(" ");
         return sb.append("]");
     }
 
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StoreNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StoreNode.java
index ca4a712..8d2c679 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StoreNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StoreNode.java
@@ -22,7 +22,7 @@ public class StoreNode extends MemOpNode {
      * @param value Value to be stored
      */
     public StoreNode(String name, int alias, SONType glb, Node mem, Node ptr, Node off, Node value, boolean init) {
-        super(name, alias, glb, mem, ptr, off, value);
+        super(name, alias, false, glb, mem, ptr, off, value);
         _init = init;
     }
 
@@ -97,7 +97,7 @@ private boolean checkOnlyUse(Node mem) {
         // when the other uses go away we can retry.
         for( Node use : mem._outputs )
             if( use != this )
-                use.addDep(this);
+                addDep(use);
         return false;
     }
 
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StructNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StructNode.java
index 2d0779e..71f3bb2 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StructNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StructNode.java
@@ -39,7 +39,7 @@ public SONTypeStruct compute() {
     public Node idealize() { return null; }
 
     @Override
-    boolean eq(Node n) { return _ts == ((StructNode)n)._ts; }
+    public boolean eq(Node n) { return _ts == ((StructNode)n)._ts; }
 
     @Override
     int hash() { return _ts==null ? 0 : _ts.hashCode(); }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/print/ASMPrinter.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/print/ASMPrinter.java
index 646c7e0..f620244 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/print/ASMPrinter.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/print/ASMPrinter.java
@@ -17,48 +17,6 @@ public static SB print(SB sb, CodeGen code) {
         for( int i=0; i<code._cfg._len; i++ )
             if( code._cfg.at(i) instanceof FunNode fun )
                 iadr = print(iadr,sb,code,fun,i);
-
-        // Skip padding
-        while( ((iadr+7) & -8) > iadr )
-            iadr++;
-
-        // constant pool
-        Encoding enc = code._encoding;
-        if(  enc!=null && !enc._bigCons.isEmpty() ) {
-            sb.p("--- Constant Pool ------").nl();
-            // By log size
-            for( int log = 3; log >= 0; log-- ) {
-                for( Node op : enc._bigCons.keySet() ) {
-                    Encoding.Relo relo = enc._bigCons.get(op);
-                    if( relo._t.log_size()==log ) {
-                        sb.hex2(iadr).p("  ");
-                        if( relo._t instanceof SONTypeTuple tt ) {
-                            for( SONType tx : tt._types ) {
-                                switch( log ) {
-                                case 0: sb.hex1(enc.read1(iadr)); break;
-                                case 1: sb.hex2(enc.read2(iadr)); break;
-                                case 2: sb.hex4(enc.read4(iadr)); break;
-                                case 3: sb.hex8(enc.read8(iadr)); break;
-                                }
-                                iadr += (1<<log);
-                                sb.p(" ");
-                            }
-                        } else {
-                            switch( log ) {
-                            case 0: sb.hex1(enc.read1(iadr)).fix(9-1,""); break;
-                            case 1: sb.hex2(enc.read2(iadr)).fix(9-2,""); break;
-                            case 2: sb.hex4(enc.read4(iadr)).fix(9-4,""); break;
-                            case 3: sb.hex8(enc.read8(iadr)).p(" "); break;
-                            }
-                            iadr += (1<<log);
-                        }
-                        relo._t.print(sb).nl();
-                    }
-                }
-            }
-        }
-
-
         return sb;
     }
 
@@ -69,8 +27,7 @@ private static int print(int iadr, SB sb, CodeGen code, FunNode fun, int cfgidx)
         fun.sig().print(sb);
         sb.p("---------------------------").nl();
 
-        if( fun._frameAdjust != 0 )
-            iadr = doInst(iadr,sb,code,cfgidx,fun,true,true);
+
         while( !(code._cfg.at(cfgidx) instanceof ReturnNode) )
             iadr = doBlock(iadr,sb,code,fun,cfgidx++);
 
@@ -81,69 +38,66 @@ private static int print(int iadr, SB sb, CodeGen code, FunNode fun, int cfgidx)
         return iadr;
     }
 
+    static private final int encWidth = 8;
     static private final int opWidth = 5;
     static private final int argWidth = 30;
     static int doBlock(int iadr, SB sb, CodeGen code, FunNode fun, int cfgidx) {
-        final int encWidth = code._mach==null ? 2 : code._mach.defaultOpSize()*2;
         CFGNode bb = code._cfg.at(cfgidx);
         if( bb != fun && !(bb instanceof IfNode) && !(bb instanceof CallEndNode) && !(bb instanceof CallNode)  && !(bb instanceof CProjNode && bb.in(0) instanceof CallEndNode ))
-            sb.p(label(bb)).p(":").nl();
+            sb.p(bb instanceof LoopNode ? "LOOP" : "L").p(bb._nid).p(":").nl();
         if( bb instanceof CallNode ) return iadr;
         final boolean postAlloc = code._phase.ordinal() > CodeGen.Phase.RegAlloc.ordinal();
-        final boolean postEncode= code._phase.ordinal() >=CodeGen.Phase.Encoding.ordinal();
 
-        boolean once=false;
-        for( Node n : bb.outs() ) {
-            if( !(n instanceof PhiNode phi) ) continue;
-            if( phi._type instanceof SONTypeMem || phi._type instanceof SONTypeRPC ) continue; // Nothing for the hidden ones
-            // Post-RegAlloc phi prints all on one line
+        // Count Phis
+        int nPhi=0;
+        for( ; nPhi<bb.nOuts(); nPhi++ )
+            if( !(bb.out(nPhi) instanceof PhiNode) )
+                break;
+
+        if( nPhi>0 ) {
+            // Post-alloc phi prints all on one line
             if( postAlloc ) {
-                if( !once ) { once=true; sb.fix(4," ").p(" ").fix(encWidth,"").p("  "); }
-                sb.p(phi._label).p(':').p(code.reg(phi)).p(',');
+                sb.fix(4," ").p(" ").fix(encWidth,"").p("  ");
+                for( int i=0; i<nPhi; i++ ) {
+                    PhiNode phi = (PhiNode)bb.out(i);
+                    if( !(phi._type instanceof SONTypeMem || phi._type instanceof SONTypeRPC) ) // Nothing for the hidden ones
+                        sb.p(phi._label).p(':').p(code.reg(phi)).p(',');
+                }
+                sb.unchar().nl();
+
             } else {
-                // Pre-RegAlloc phi prints one line per
-                sb.fix(4," ").p(" ").fix(encWidth,"").p("  ").fix(opWidth,phi._label).p(" ").p(code.reg(phi));
-                if( phi.getClass() == PhiNode.class ) {
-                    sb.p(" = phi( ");
-                    for( int i=1; i<phi.nIns(); i++ )
-                        sb.p("N").p(phi.in(i)._nid).p(",");
-                    sb.unchar().p(" )");
+                for( int j=0; j<nPhi; j++ ) {
+                    PhiNode phi = (PhiNode)bb.out(j);
+                    if( phi._type instanceof SONTypeMem || phi._type instanceof SONTypeRPC ) continue; // Nothing for the hidden ones
+                    sb.fix(4," ").p(" ").fix(encWidth,"").p("  ").fix(opWidth,phi._label).p(" ");
+                    sb.p(code.reg(phi));
+                    if( !(phi instanceof ParmNode) ) {
+                        sb.p(" = phi( ");
+                        for( int i=1; i<phi.nIns(); i++ )
+                            sb.p("N").p(phi.in(i)._nid).p(",");
+                        sb.unchar().p(" )");
+                    }
+                    sb.nl();
                 }
-                sb.nl();
             }
         }
-        if( once ) sb.unchar().nl();
 
         // All the non-phis
-        for( int i=0; i<bb.nOuts(); i++ )
-            if( !(bb.out(i) instanceof PhiNode) )
-                iadr = doInst(iadr, sb,code, cfgidx, bb.out(i),postAlloc, postEncode );
+        for( int i=nPhi; i<bb.nOuts(); i++ )
+            iadr = doInst(iadr, sb,code,fun,bb,bb.out(i),postAlloc );
 
         return iadr;
     }
 
-    static int doInst( int iadr, SB sb, CodeGen code, int cfgidx, Node n, boolean postAlloc, boolean postEncode ) {
-        if( n==null || n instanceof CProjNode ) return iadr;
-        if( postAlloc && n instanceof CalleeSaveNode ) return iadr;
-        if( postEncode && n instanceof ProjNode ) return iadr;
-        if( n instanceof MemMergeNode ) return iadr;
-        if( n.getClass() == ConstantNode.class ) return iadr; // Default placeholders
-        final int dopz = code._mach==null ? 2 : code._mach.defaultOpSize();
-        final int encWidth = dopz*2;
+    static int doInst(int iadr, SB sb, CodeGen code, FunNode fun, CFGNode bb, Node n, boolean postAlloc ) {
+        if( n instanceof CProjNode ) return iadr;
+        if( n instanceof CalleeSaveNode && postAlloc ) return iadr;
 
         // All blocks ending in a Region will need to either fall into or jump
         // to this block.  Until the post-reg-alloc block layout cleanup, we
         // need to assume a jump.  There's no real hardware op here, yet.
-        if( n instanceof RegionNode cfg && !(n instanceof FunNode) ) {
-            if( postEncode ) return iadr; // All jumps inserted already
-            while( cfgidx < code._cfg._len-1 ) {
-                CFGNode next = code._cfg.at(++cfgidx);
-                if( next == n ) return iadr; // Fall-through, no branch
-                if( next.nOuts()>1 )
-                    break;      // Has code in the block, need to jump around
-                // No code in the block, can fall through it
-            }
-            sb.hex2(iadr++).p(" ").fix(encWidth,"??").p("  ").fix(opWidth,"JMP").p(" ").fix(argWidth,label(cfg)).nl();
+        if( n instanceof RegionNode && !(n instanceof FunNode) ) {
+            sb.hex4(iadr++).p(" ").fix(encWidth,"??").p("  ").fix(opWidth,"JMP").p(" ").fix(argWidth,"L"+n._nid).nl();
             return iadr;
         }
 
@@ -157,38 +111,24 @@ static int doInst( int iadr, SB sb, CodeGen code, int cfgidx, Node n, boolean po
 
         // ADDR ENCODING  Op--- dst = src op src       // Comment
         // 1234 abcdefgh  ld4   RAX = [Rbase + off]    // Comment
-        sb.hex2(iadr).p(" ");
+        sb.hex4(iadr);
+        sb.p(" ");
 
         // Encoding
-        int fatEncoding = 0;
-        if( code._encoding != null ) {
-            int size = code._encoding._opLen[n._nid];
-            if( code._asmLittle )
-                for( int i=0; i<Math.min(size,dopz); i++ )
-                    sb.hex1(code._encoding._bits.buf()[iadr++]);
-            else {
-                iadr += Math.min(size,dopz);
-                for( int i=0; i<Math.min(size,dopz); i++ )
-                    sb.hex1(code._encoding._bits.buf()[iadr-i-1]);
-            }
-            for( int i=size*2; i<encWidth; i++ )
-                sb.p(" ");
-            fatEncoding = size - (encWidth>>1); // Not-printed parts of encoding
-        } else
-            sb.fix(encWidth,"");
+        int size = 1;           // TODO: Fake encoding size
+        iadr += size;
+        sb.fix(encWidth,"??");
         sb.p("  ");
 
         // Op; generally "ld4" or "call"
-        sb.fix(opWidth, n instanceof MachNode mach ? mach.op() : n.label()).p(" ");
+        sb.fix(opWidth, n instanceof MachNode mach ? mach.op() : n.label());
+        sb.p(" ");
 
         // General asm args
-        String isMultiOp = null;
         if( n instanceof MachNode mach ) {
             int old = sb.len();
             mach.asm(code,sb);
-            int len = sb.len();
-            isMultiOp = isMultiOp(sb,old,len);
-            sb.fix(argWidth-(len-old),""); // Pad out
+            sb.fix(argWidth-(sb.len()-old),""); // Pad out
 
         } else if( !(n._type instanceof SONTypeMem) ) {
             // Room for some inputs
@@ -209,64 +149,14 @@ static int doInst( int iadr, SB sb, CodeGen code, int cfgidx, Node n, boolean po
 
         sb.nl();
 
-        // Printing more op bits than fit
-        if( isMultiOp != null && code._encoding != null ) {
-            // Multiple ops, template style, no RA, no scheduling.  Print out
-            // one-line-per-newline, with encoding bits up front.
-            int size = code._encoding._opLen[n._nid];
-            int off = Math.min(size,dopz);
-            while( isMultiOp!=null ) {
-                sb.hex2(iadr).p(" ");
-                int len = Math.min(size-off,dopz);
-                for( int i=0; i<len; i++ )
-                    sb.hex1(code._encoding._bits.buf()[iadr++]);
-                off += len;
-                for( int i=size-off; i<dopz; i++ ) sb.p("  ");
-                sb.p("  ");
-                int x = isMultiOp.indexOf('\n');
-                if( x== -1 ) {  // Last line
-                    sb.p(isMultiOp).nl();
-                    isMultiOp = null;
-                } else {
-                    sb.p(isMultiOp.substring(0,x+1)); // Includes the newline
-                    isMultiOp = isMultiOp.substring(x+1);
-                }
-
-            }
-
-        } else if( fatEncoding > 0 ) {
-            // Extra bytes past the default encoding width, all put on a line by
-            // themselves.  X86 special for super long encodings
-            sb.hex2(iadr).p(" ");
-            for( int i=0; i<fatEncoding; i++ )
-                sb.hex1(code._encoding._bits.buf()[iadr++]);
-            sb.nl();
-        }
-
-
         // MultiNodes are immediately followed by projection(s)
         if( !(n instanceof CFGNode) && n instanceof MultiNode ) {
             for( Node proj : n._outputs ) {
                 assert proj instanceof ProjNode;
-                doInst(iadr,sb,code, cfgidx, proj,postAlloc,postEncode);
+                doInst(iadr,sb,code,fun,bb,proj,postAlloc);
             }
         }
 
         return iadr;
     }
-
-    private static String label( CFGNode bb ) {
-        return (bb instanceof LoopNode ? "LOOP" : "L")+bb._nid;
-    }
-
-    private static String isMultiOp(SB sb, int old, int len) {
-        for( int i=old; i<len; i++ )
-            if( sb.at(i)=='\n' ) {
-                String s = sb.subString(i+1,len);
-                sb.setLen(i);
-                return s;
-            }
-        return null;
-    }
-
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/print/IRPrinter.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/print/IRPrinter.java
index c3d38e1..a69581d 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/print/IRPrinter.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/print/IRPrinter.java
@@ -15,7 +15,6 @@ public abstract class IRPrinter {
     // NNID NNAME DDEF DDEF  [[  UUSE UUSE  ]]  TYPE
     // 1234 sssss 1234 1234 1234 1234 1234 1234 tttttt
     public static SB printLine( Node n, SB sb ) {
-        if( n==null ) return sb;
         sb.p("%4d %-7.7s ".formatted(n._nid,n.label()));
         if( n._inputs==null )
             return sb.p("DEAD\n");

From 0eaa7bb9da8656a5b8c7eceb37ca1035b15970bc Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Sat, 26 Apr 2025 00:49:41 +0100
Subject: [PATCH 065/119] Sync with final ch21

---
 .../com/compilerprogramming/ezlang/compiler/TestSONTypes.java   | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/seaofnodes/src/test/java/com/compilerprogramming/ezlang/compiler/TestSONTypes.java b/seaofnodes/src/test/java/com/compilerprogramming/ezlang/compiler/TestSONTypes.java
index 7acfabd..e7e293f 100644
--- a/seaofnodes/src/test/java/com/compilerprogramming/ezlang/compiler/TestSONTypes.java
+++ b/seaofnodes/src/test/java/com/compilerprogramming/ezlang/compiler/TestSONTypes.java
@@ -196,7 +196,7 @@ func foo()->Int {
 
         static void testCPU( String src, String cpu, String os, int spills, String stop ) {
         CodeGen code = new CodeGen(src);
-        code.parse().opto().typeCheck().instSelect(cpu,os).GCM().localSched().regAlloc().encode();
+        code.parse().opto().typeCheck().loopTree().instSelect(cpu,os).GCM().localSched().regAlloc().encode();
         int delta = spills>>3;
         if( delta==0 ) delta = 1;
         assertEquals("Expect spills:",spills,code._regAlloc._spillScaled,delta);

From c86989f833dc49484dfddc61cadc6ae444392f76 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Sat, 26 Apr 2025 23:48:17 +0100
Subject: [PATCH 066/119] Sync with final ch21

---
 .../ezlang/parser/AST.java                    | 13 +++-
 .../ezlang/compiler/Compiler.java             |  3 +-
 seaofnodes/src/test/cases/mergsort/sort.smp   | 64 +++++++++++++++++++
 .../ezlang/compiler/Simple.java               | 31 +++++----
 4 files changed, 98 insertions(+), 13 deletions(-)
 create mode 100644 seaofnodes/src/test/cases/mergsort/sort.smp

diff --git a/parser/src/main/java/com/compilerprogramming/ezlang/parser/AST.java b/parser/src/main/java/com/compilerprogramming/ezlang/parser/AST.java
index 81a0015..46bcf78 100644
--- a/parser/src/main/java/com/compilerprogramming/ezlang/parser/AST.java
+++ b/parser/src/main/java/com/compilerprogramming/ezlang/parser/AST.java
@@ -545,8 +545,14 @@ public void accept(ASTVisitor visitor) {
      */
     public static class NewExpr extends Expr {
         public final TypeExpr typeExpr;
+        public final long len; // temp hack, as this needs to be an expression rather than constant
         public NewExpr(TypeExpr typeExpr) {
             this.typeExpr = typeExpr;
+            this.len = 0;
+        }
+        public NewExpr(TypeExpr typeExpr, long len) {
+            this.typeExpr = typeExpr;
+            this.len = len;
         }
         @Override
         public StringBuilder toStr(StringBuilder sb) {
@@ -572,8 +578,13 @@ public static class InitExpr extends Expr {
         public final NewExpr newExpr;
         public final List<Expr> initExprList;
         public InitExpr(NewExpr newExpr, List<Expr> initExprList) {
-            this.newExpr = newExpr;
             this.initExprList = initExprList;
+            // For arrays we compute length based on number of elements
+            // This is not actually correct - see https://github.com/CompilerProgramming/ez-lang/issues/47
+            if (initExprList.size() != newExpr.len)
+                this.newExpr = new NewExpr(newExpr.typeExpr,initExprList.size());
+            else
+                this.newExpr = newExpr;
         }
         @Override
         public StringBuilder toStr(StringBuilder sb) {
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java
index b93dda1..46805d5 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java
@@ -554,7 +554,7 @@ private Node compileNewExpr(AST.NewExpr newExpr) {
         Type type = newExpr.type;
         if (type instanceof Type.TypeArray typeArray) {
             SONTypeMemPtr tarray = (SONTypeMemPtr) TYPES.get(typeArray.name());
-            return newArray(tarray._obj,ZERO);
+            return newArray(tarray._obj,newExpr.len==0?ZERO:con(newExpr.len));
         }
         else if (type instanceof Type.TypeStruct typeStruct) {
             SONTypeMemPtr tptr = (SONTypeMemPtr) TYPES.get(typeStruct.name());
@@ -686,6 +686,7 @@ private Node compileCallExpr(AST.CallExpr callExpr) {
 
         // Post-call setup
         CallEndNode cend = (CallEndNode)new CallEndNode(call).peephole();
+        call.peephole();        // Rerun peeps after CallEnd, allows early inlining
         // Control from CallEnd
         ctrl(new CProjNode(cend,0,ScopeNode.CTRL).peephole());
         // Memory from CallEnd
diff --git a/seaofnodes/src/test/cases/mergsort/sort.smp b/seaofnodes/src/test/cases/mergsort/sort.smp
new file mode 100644
index 0000000..b82b3f4
--- /dev/null
+++ b/seaofnodes/src/test/cases/mergsort/sort.smp
@@ -0,0 +1,64 @@
+// -*- mode: java;  -*-
+// based on the top-down version from https://en.wikipedia.org/wiki/Merge_sort
+
+val merge_sort = { int[] a, int[] b, int n ->
+    copy_array (a, 0, n, b);
+    split_merge(a, 0, n, b);
+};
+
+val split_merge = { int[] b, int begin, int end, int[] a ->
+    if (end - begin <= 1)
+        return 0;
+    int middle = (end + begin) / 2;
+    split_merge(a, begin, middle, b);
+    split_merge(a, middle, end, b);
+    merge(b, begin, middle, end, a);
+    return 0;
+};
+
+val merge = { int[] b, int begin, int middle, int end, int[] a ->
+    int i = begin, j = middle;
+
+    for (int k = begin; k < end; k++) {
+        // && and ||
+        bool cond = false;
+        if (i < middle) {
+            if (j >= end)          cond = true;
+            else if (a[i] <= a[j]) cond = true;
+        }
+        if (cond) b[k] = a[i++];
+        else      b[k] = a[j++];
+    }
+};
+
+val copy_array = { int[] a, int begin, int end, int[] b ->
+    for (int k = begin; k < end; k++)
+        b[k] = a[k];
+};
+
+val eq = { int[] a, int[] b, int n ->
+    int result = 1;
+    int i = 0;
+    while (i < n) {
+        if (a[i] != b[i]) {
+            result = 0;
+            break;
+        }
+        i = i + 1;
+    }
+    return result;
+};
+
+val main = { ->
+    int[] !a = new int[10];
+    a[0] = 10; a[1] = 9; a[2] = 8; a[3] = 7; a[4] = 6;
+    a[5] = 5; a[6] = 4; a[7] = 3; a[8] = 2; a[9] = 1;
+    int[] !b = new int[10];
+    b[0] = 0; b[1] = 0; b[2] = 0; b[3] = 0; b[4] = 0;
+    b[5] = 0; b[6] = 0; b[7] = 0; b[8] = 0; b[9] = 0;
+    int[] !expect = new int[10];
+    expect[0] = 1; expect[1] = 2; expect[2] = 3; expect[3] = 4; expect[4] = 5;
+    expect[5] = 6; expect[6] = 7; expect[7] = 8; expect[8] = 9; expect[9] = 10;
+    merge_sort(a, b, 10);
+    return eq(a,expect,10);
+};
diff --git a/seaofnodes/src/test/java/com/compilerprogramming/ezlang/compiler/Simple.java b/seaofnodes/src/test/java/com/compilerprogramming/ezlang/compiler/Simple.java
index 2fa8e9d..712b539 100644
--- a/seaofnodes/src/test/java/com/compilerprogramming/ezlang/compiler/Simple.java
+++ b/seaofnodes/src/test/java/com/compilerprogramming/ezlang/compiler/Simple.java
@@ -16,11 +16,12 @@ public class Simple {
     static final int DUMP_AFTER_PARSE        = 1<<0;
     static final int DUMP_AFTER_OPTO         = 1<<1;
     static final int DUMP_AFTER_TYPE_CHECK   = 1<<2;
-    static final int DUMP_AFTER_INSTR_SELECT = 1<<3;
-    static final int DUMP_AFTER_GCM          = 1<<4;
-    static final int DUMP_AFTER_LOCAL_SCHED  = 1<<5;
-    static final int DUMP_AFTER_REG_ALLOC    = 1<<6;
-    static final int DUMP_AFTER_ENCODE       = 1<<7;
+    static final int DUMP_AFTER_LOOP_TREE    = 1<<3;
+    static final int DUMP_AFTER_INSTR_SELECT = 1<<4;
+    static final int DUMP_AFTER_GCM          = 1<<5;
+    static final int DUMP_AFTER_LOCAL_SCHED  = 1<<6;
+    static final int DUMP_AFTER_REG_ALLOC    = 1<<7;
+    static final int DUMP_AFTER_ENCODE       = 1<<8;
 
     static final int DUMP_FINAL              = 1<<16;
 
@@ -88,12 +89,13 @@ static void dump(CodeGen code, int dump, int pass) {
                     case DUMP_AFTER_PARSE        -> "01-parse.dot";
                     case DUMP_AFTER_OPTO         -> "02-opto.dot";
                     case DUMP_AFTER_TYPE_CHECK   -> "03-type_check.dot";
-                    case DUMP_AFTER_INSTR_SELECT -> "04-instr_select.dot";
-                    case DUMP_AFTER_GCM          -> "05-gcm.dot";
-                    case DUMP_AFTER_LOCAL_SCHED  -> "06-local_sched.dot";
-                    case DUMP_AFTER_REG_ALLOC    -> "07-reg_allos.dot";
-                    case DUMP_AFTER_ENCODE       -> "08-local_sched.dot";
-                    case DUMP_FINAL              -> "09-final.dot";
+                    case DUMP_AFTER_LOOP_TREE    -> "04-loop_tree.dot";
+                    case DUMP_AFTER_INSTR_SELECT -> "05-instr_select.dot";
+                    case DUMP_AFTER_GCM          -> "06-gcm.dot";
+                    case DUMP_AFTER_LOCAL_SCHED  -> "07-local_sched.dot";
+                    case DUMP_AFTER_REG_ALLOC    -> "08-reg_allos.dot";
+                    case DUMP_AFTER_ENCODE       -> "09-local_sched.dot";
+                    case DUMP_FINAL              -> "10-final.dot";
                     default                      -> throw Utils.TODO();
                 };
 
@@ -110,6 +112,7 @@ static void dump(CodeGen code, int dump, int pass) {
                         case DUMP_AFTER_PARSE        -> "After Parse:";
                         case DUMP_AFTER_OPTO         -> "After OPTO:";
                         case DUMP_AFTER_TYPE_CHECK   -> "After Type Check:";
+                        case DUMP_AFTER_LOOP_TREE    -> "After Loop Tree:";
                         case DUMP_AFTER_INSTR_SELECT -> "After Instruction Selection:";
                         case DUMP_AFTER_GCM          -> "After GCM:";
                         case DUMP_AFTER_LOCAL_SCHED  -> "After Local Scheduling:";
@@ -134,6 +137,8 @@ static void print_compilation_times(CodeGen code) {
         System.out.println(String.format("Optimization Time:          %.3f sec", t));
         total += t = code._tTypeCheck / 1e3;
         System.out.println(String.format("Type Checking Time:         %.3f sec", t));
+        total += t = code._tLoopTree / 1e3;
+        System.out.println(String.format("Loop Tree Time:             %.3f sec", t));
         total += t = code._tInsSel / 1e3;
         System.out.println(String.format("Instruction Selection Time: %.3f sec", t));
         total += t = code._tGCM / 1e3;
@@ -180,6 +185,7 @@ public static void main(String[] args) throws Exception {
                     case "--dump-after-parse":        dump |= DUMP_AFTER_PARSE; break;
                     case "--dump-after-opto":         dump |= DUMP_AFTER_OPTO; break;
                     case "--dump-after-type-check":   dump |= DUMP_AFTER_TYPE_CHECK; break;
+                    case "--dump-after-loop-tree":    dump |= DUMP_AFTER_LOOP_TREE; break;
                     case "--dump-after-instr-select": dump |= DUMP_AFTER_INSTR_SELECT; break;
                     case "--dump-after-gcm":          dump |= DUMP_AFTER_GCM; break;
                     case "--dump-after-local-sched":  dump |= DUMP_AFTER_LOCAL_SCHED; break;
@@ -250,6 +256,9 @@ public static void main(String[] args) throws Exception {
         code.typeCheck();
         dump(code, dump, DUMP_AFTER_TYPE_CHECK);
 
+        code.loopTree();
+        dump(code, dump, DUMP_AFTER_LOOP_TREE);
+
         if (do_codegen) {
             code.instSelect(cpu, abi);
             dump(code, dump, DUMP_AFTER_INSTR_SELECT);

From 94e4a77fcb4d48e6de314108957474b9f39f9c14 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Sun, 27 Apr 2025 22:21:00 +0100
Subject: [PATCH 067/119] Sync with final ch21

---
 .../ezlang/compiler/nodes/CallEndNode.java                | 2 +-
 .../ezlang/compiler/nodes/CallNode.java                   | 2 +-
 .../ezlang/compiler/nodes/FunNode.java                    | 1 +
 .../ezlang/compiler/nodes/ReturnNode.java                 | 4 +++-
 .../ezlang/compiler/sontypes/SONTypeMemPtr.java           | 8 ++++----
 .../ezlang/compiler/sontypes/SONTypeStruct.java           | 7 +++++--
 6 files changed, 15 insertions(+), 9 deletions(-)

diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CallEndNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CallEndNode.java
index 07c2aa8..1dd1f50 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CallEndNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CallEndNode.java
@@ -59,7 +59,7 @@ public Node idealize() {
 
         // Trivial inlining: call site calls a single function; single function
         // is only called by this call site.
-        if( !_folding && nIns()==2 && in(0) instanceof CallNode call ) {
+        if( false && !_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
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CallNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CallNode.java
index 56902cf..5f88fd4 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CallNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CallNode.java
@@ -138,7 +138,7 @@ public void unlink_all() {
                 assert linked(fun);
                 int idx = fun._inputs.find(this);
                 for( Node use : fun._outputs )
-                    if( use instanceof ParmNode parm )
+                    if( use instanceof ParmNode )
                         use.delDef(idx);
                 fun.delDef(idx);
                 cend().delDef(cend()._inputs.find(fun.ret()));
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/FunNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/FunNode.java
index 9992008..9d0eba1 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/FunNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/FunNode.java
@@ -88,6 +88,7 @@ public Node idealize() {
 
         // Upgrade inferred or user-written return type to actual
         if( _ret!=null && _ret._type instanceof SONTypeTuple tt && tt.ret() != _sig.ret() )
+		    // FIXME Dibyendu
             //throw Utils.TODO();
             return null;
 
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ReturnNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ReturnNode.java
index 065bdcb..b63280a 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ReturnNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ReturnNode.java
@@ -61,7 +61,9 @@ public SONType compute() {
         if( inProgress () ) return null;
         if( _fun.isDead() ) return null;
 
-//        // Upgrade signature based on return type
+          // Upgrade signature based on return type
+          // FIXME Dibyendu - EZ lang does not support modifying function signature
+          // but we should probably do the checking?
 //        SONType ret = expr()._type;
 //        SONTypeFunPtr fcn = _fun.sig();
 //        assert ret.isa(fcn.ret());
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeMemPtr.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeMemPtr.java
index 1d74de0..e3b197f 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeMemPtr.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeMemPtr.java
@@ -43,9 +43,9 @@ public SONTypeMemPtr(byte nil, SONTypeStruct obj) {
 
     // An abstract pointer, pointing to either a Struct or an Array.
     // Can also be null or not, so 4 choices {TOP,BOT} x {nil,not}
-    public static SONTypeMemPtr BOT = make((byte)3, SONTypeStruct.BOT);
-    public static SONTypeMemPtr TOP = BOT.dual();
-    public static SONTypeMemPtr NOTBOT = make((byte)2, SONTypeStruct.BOT);
+    public static final SONTypeMemPtr BOT = make((byte)3, SONTypeStruct.BOT);
+    public static final SONTypeMemPtr TOP = BOT.dual();
+    public static final SONTypeMemPtr NOTBOT = make((byte)2, SONTypeStruct.BOT);
 
     //public static SONTypeMemPtr TEST= make((byte)2, SONTypeStruct.TEST);
     public static void gather(ArrayList<SONType> ts) { ts.add(NOTBOT); ts.add(BOT); /* ts.add(TEST); */ }
@@ -79,7 +79,7 @@ public SONTypeNil xmeet(SONType t) {
     // Is forward-reference
     @Override public boolean isFRef() { return _obj.isFRef(); }
 
-    @Override public int log_size() { return 2; } // (1<<2)==4-byte pointers
+    @Override public int log_size() { return 3; } // (1<<3)==8-byte pointers
 
     @Override int hash() { return _obj.hashCode() ^ super.hash(); }
 
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeStruct.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeStruct.java
index 256ddfa..cd69999 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeStruct.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeStruct.java
@@ -192,9 +192,12 @@ int hash() {
 
     @Override
     public SB print(SB sb) {
+        if( _fields == null ) return sb.p(_name); // Forward reference struct, just print the name
+        if( isAry() ) {
+            if( !isFinal() ) sb.p("!");
+            return sb.p(_name); // Skip printing generic array fields
+        }
         sb.p(_name);
-        if( _fields == null ) return sb; // Forward reference struct, just print the name
-        if( isAry() ) return sb; // Skip printing generic array fields
         sb.p(" {");
         for( Field f : _fields )
             f._type.print(sb).p(f._final ? " " : " !").p(f._fname).p("; ");

From 01b016fb4deec2f2a573420ab6077fb43fff9c04 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Tue, 29 Apr 2025 23:36:57 +0100
Subject: [PATCH 068/119] Apply fixes from Simple project

---
 .../ezlang/compiler/codegen/BuildLRG.java     |   2 +-
 .../ezlang/compiler/codegen/CodeGen.java      |  18 +
 .../ezlang/compiler/codegen/Encoding.java     |   2 -
 .../compiler/codegen/GlobalCodeMotion.java    |  40 +-
 .../ezlang/compiler/codegen/IFG.java          |   8 +-
 .../ezlang/compiler/codegen/Machine.java      |   8 +-
 .../ezlang/compiler/codegen/RegAlloc.java     |  30 +-
 .../ezlang/compiler/codegen/RegMask.java      |   6 +-
 .../ezlang/compiler/nodes/CFGNode.java        |   5 +-
 .../ezlang/compiler/nodes/CallEndNode.java    |   6 +-
 .../ezlang/compiler/nodes/FunNode.java        |   7 +-
 .../ezlang/compiler/nodes/LoopNode.java       |   4 +-
 .../ezlang/compiler/nodes/NewNode.java        |   4 +-
 .../ezlang/compiler/nodes/PhiNode.java        |   2 +-
 .../ezlang/compiler/nodes/RegionNode.java     |   7 +-
 .../ezlang/compiler/nodes/StartNode.java      |   2 +-
 .../ezlang/compiler/nodes/StoreNode.java      |   5 +
 .../compiler/nodes/cpus/arm/CallARM.java      |   2 +-
 .../compiler/nodes/cpus/arm/CallRRARM.java    |   2 +-
 .../compiler/nodes/cpus/arm/ParmARM.java      |   2 +-
 .../ezlang/compiler/nodes/cpus/arm/arm.java   |   7 +-
 .../compiler/nodes/cpus/riscv/CallRISC.java   |  10 +-
 .../compiler/nodes/cpus/riscv/CallRRISC.java  |   2 +-
 .../compiler/nodes/cpus/riscv/ParmRISC.java   |   2 +-
 .../compiler/nodes/cpus/riscv/StoreRISC.java  |   2 +-
 .../compiler/nodes/cpus/riscv/riscv.java      |   5 +-
 .../nodes/cpus/x86_64_v2/CallRX86.java        |   2 +-
 .../nodes/cpus/x86_64_v2/CallX86.java         |   2 +-
 .../nodes/cpus/x86_64_v2/ParmX86.java         |   2 +-
 .../nodes/cpus/x86_64_v2/SplitX86.java        |  11 +-
 .../nodes/cpus/x86_64_v2/StoreX86.java        |   2 +-
 .../nodes/cpus/x86_64_v2/x86_64_v2.java       |  18 +-
 .../src/test/cases/mergsort/sort_ez.out       | 716 +++++++++++++++++
 .../src/test/cases/mergsort/sort_smp.out      | 723 ++++++++++++++++++
 34 files changed, 1593 insertions(+), 73 deletions(-)
 create mode 100644 seaofnodes/src/test/cases/mergsort/sort_ez.out
 create mode 100644 seaofnodes/src/test/cases/mergsort/sort_smp.out

diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/BuildLRG.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/BuildLRG.java
index 003d26e..1b4f17b 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/BuildLRG.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/BuildLRG.java
@@ -64,7 +64,7 @@ public static boolean run(int round, RegAlloc alloc) {
                 }
 
                 // MultiNodes have projections which set registers
-                if( n instanceof MultiNode )
+                if( n instanceof MultiNode && !(n instanceof CFGNode) )
                     for( Node proj : n.outs() )
                         if( proj instanceof MachNode )
                             defLRG(alloc,proj);
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/CodeGen.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/CodeGen.java
index d7eaa3a..fa890bd 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/CodeGen.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/CodeGen.java
@@ -90,6 +90,24 @@ public int getUID() {
     public  int getALIAS() { return _alias++; }
 
 
+    // idepths are cached and valid until *inserting* CFG edges (deleting is
+    // OK).  This happens with inlining, which bumps the version to bulk
+    // invalidate the idepth caches.
+    private int _iDepthVersion = 0;
+    public void invalidateIDepthCaches() { _iDepthVersion++; }
+    public boolean validIDepth(int idepth) {
+        if( idepth==0 ) return false;
+        if( _iDepthVersion==0 ) return true;
+        return (idepth%100)==_iDepthVersion;
+    }
+    public int iDepthAt(int idepth) {
+        return 100*idepth+_iDepthVersion;
+    }
+    public int iDepthFrom(int idepth) {
+        assert idepth==0 || validIDepth(idepth);
+        return idepth+100;
+    }
+
     // 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; }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/Encoding.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/Encoding.java
index 53f20ed..1fc124e 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/Encoding.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/Encoding.java
@@ -323,7 +323,6 @@ private void writeEncodings() {
             if( !(bb instanceof MachNode mach0) )
                 _opStart[bb._nid] = _bits.size();
             else if( bb instanceof FunNode fun ) {
-                padN(16,_bits);
                 _fun = fun;     // Currently encoding function
                 _opStart[bb._nid] = _bits.size();
                 mach0.encoding( this );
@@ -337,7 +336,6 @@ else if( bb instanceof FunNode fun ) {
                 }
             }
         }
-        padN(16,_bits);
     }
 
     // --------------------------------------------------
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/GlobalCodeMotion.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/GlobalCodeMotion.java
index deba31f..627e7e0 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/GlobalCodeMotion.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/GlobalCodeMotion.java
@@ -167,6 +167,8 @@ private static void breadth(Node stop, Node[] ns, CFGNode[] late) {
                             !(memuse instanceof NewNode) &&
                             // Load-use directly defines memory
                             (memuse._type instanceof SONTypeMem ||
+                             // Load-use directly defines memory
+                             memuse instanceof CallNode ||
                              // Load-use indirectly defines memory
                              (memuse._type instanceof SONTypeTuple tt && tt._types[ld._alias] instanceof SONTypeMem)) )
                             continue outer;
@@ -200,6 +202,33 @@ private static void _doSchedLate(Node n, Node[] ns, CFGNode[] late) {
         if( n instanceof MemOpNode load && load._isLoad )
             lca = find_anti_dep(lca,load,early,late);
 
+
+        // Nodes setting a single register and getting killed will stay close
+        // to the uses, since they will be forced to spill anyway.  The kill
+        // check is very weak, and some may be hoisted only to spill in the RA.
+        if( n instanceof MachNode mach ) {
+            RegMask out = mach.outregmap();
+            if( out!=null && out.size1() ) {
+                int reg = mach.outregmap().firstReg();
+                // Look for kills
+                outer:
+                for( CFGNode lca2=lca; lca2 != early; lca2 = lca2.idom() ) {
+                    if( lca2 instanceof MachNode mach2 ) {
+                        for( int i=1; i<lca2.nIns(); i++ ) {
+                            RegMask mask = mach2.regmap(i);
+                            if( mask!=null && mask.test(reg) ) {
+                                early = lca2 instanceof IfNode ? lca2.idom() : lca2;
+                                break outer;
+                            }
+                        }
+                        RegMask kill = mach2.killmap();
+                        if( kill != null )
+                            throw Utils.TODO();
+                    }
+                }
+            }
+        }
+
         // Walk up from the LCA to the early, looking for best place.  This is
         // the lowest execution frequency, approximated by least loop depth and
         // deepest control flow.
@@ -231,8 +260,9 @@ private static CFGNode use_block(Node n, Node use, CFGNode[] late) {
     // Least loop depth first, then largest idepth
     private static boolean better( CFGNode lca, CFGNode best ) {
         return lca.loopDepth() < best.loopDepth() ||
-                lca instanceof NeverNode ||
-                (lca.idepth() > best.idepth() || best instanceof IfNode);
+            lca instanceof NeverNode ||
+            lca.idepth() > best.idepth() ||
+            best instanceof IfNode;
     }
 
     private static CFGNode find_anti_dep(CFGNode lca, MemOpNode load, CFGNode early, CFGNode[] late) {
@@ -249,9 +279,9 @@ private static CFGNode find_anti_dep(CFGNode lca, MemOpNode load, CFGNode early,
                     lca = anti_dep( load, late[mem._nid], mem.cfg0(), lca, st );
                 }
                 break; // Loads do not cause anti-deps on other loads
-            case CallNode st:
-                assert late[st._nid]!=null;
-                lca = anti_dep(load,late[st._nid],st.cfg0(),lca,st);
+            case CallNode call:
+                assert late[call._nid]!=null;
+                lca = anti_dep(load,late[call._nid],call.cfg0(),lca,call);
                 break;
             case PhiNode phi:
                 // Repeat anti-dep for matching Phi inputs.
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/IFG.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/IFG.java
index 37005a7..4868c13 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/IFG.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/IFG.java
@@ -241,14 +241,12 @@ private static void selfConflict(RegAlloc alloc, Node n, LRG lrg, Node prior) {
     private static void mergeLiveOut( RegAlloc alloc, CFGNode priorbb, int i ) {
         CFGNode bb = priorbb.cfg(i);
         if( bb == null ) return; // Start has no prior
-        if( !bb.blockHead() ) bb = bb.cfg0();
-        //if( i==0 && !(bb instanceof StartNode) ) bb = bb.cfg0();
-        assert bb.blockHead();
+        while( !bb.blockHead() ) bb = bb.cfg0();
 
         // Lazy get live-out set for bb
-      IdentityHashMap<LRG, Node> lrgs = BBOUTS.computeIfAbsent( bb, k -> new IdentityHashMap<>() );
+        IdentityHashMap<LRG, Node> lrgs = BBOUTS.computeIfAbsent( bb, k -> new IdentityHashMap<>() );
 
-      for( LRG lrg : TMP.keySet() ) {
+        for( LRG lrg : TMP.keySet() ) {
             Node def = TMP.get(lrg);
             // Effective def comes from phi input from prior block
             if( def instanceof PhiNode phi && phi.cfg0()==priorbb ) {
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/Machine.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/Machine.java
index b04ddbe..4115e57 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/Machine.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/Machine.java
@@ -16,8 +16,12 @@ abstract public class Machine {
     public abstract long callerSave();
     // List of never-save registers, e.g. RSP or a ZERO register if you have one
     public abstract long neverSave();
-    // Call Argument Mask
-    public abstract RegMask callArgMask(SONTypeFunPtr tfp, int arg);
+    // Call Argument Mask.  Passed in the function signature and argument
+    // number (2-based; 0 is for control and 1 for memory).  Also passed in a 0
+    // for the function itself, or for *outgoing* calls, the maximum stack slot
+    // given to the incoming function arguments (stack slots reserved for
+    // incoming arguments).
+    public abstract RegMask callArgMask(SONTypeFunPtr tfp, int arg, int maxArgSlot);
     // Return register mask, based on signature (GPR vs FPR)
     public abstract RegMask retMask(SONTypeFunPtr tfp);
     // Return PC register
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/RegAlloc.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/RegAlloc.java
index 5c01edf..0c778d5 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/RegAlloc.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/RegAlloc.java
@@ -261,7 +261,7 @@ boolean splitEmptyMaskSimple( byte round, LRG lrg ) {
     }
 
     // Single-def live range with an empty mask.  There are many single-reg
-    // uses.  Theory is there's many repeats if the same reg amongst the uses.
+    // uses.  Theory is there's many repeats of the same reg amongst the uses.
     // In of splitting once per use, start by splitting into groups based on
     // required input register.
     boolean splitEmptyMaskByUse( byte round, LRG lrg ) {
@@ -270,35 +270,45 @@ boolean splitEmptyMaskByUse( byte round, LRG lrg ) {
         // Look at each use, and break into non-overlapping register classes.
         Ary<RegMask> rclass = new Ary<>(RegMask.class);
         boolean done=false;
+        int ncalls=0;
         while( !done ) {
             done = true;
             for( Node use : def._outputs )
-                if( use instanceof MachNode mach )
+                if( use instanceof MachNode mach ) {
+                    if( mach instanceof CallNode ) ncalls++;
                     for( int i=1; i<use.nIns(); i++ )
                         if( use.in(i)==def )
                             done = putIntoRegClass( rclass, mach.regmap(i) );
+                }
         }
 
-        // See how many register classes we split into
-        if( rclass._len <= 1 ) return false;
+        // See how many register classes we split into.  Generally not
+        // productive to split like this across calls, which are going to kill
+        // all registers anyways.
+        if( rclass._len <= 1 || ncalls > 1 ) return false;
 
-        // Split by class
+        // Split by classh
+        Ary<Node> ns = new Ary<>(Node.class);
         for( RegMask rmask : rclass ) {
+            ns.addAll(def._outputs);
             Node split = makeSplit(def,"popular",round,lrg);
             split.insertAfter( def );
             if( split.nIns()>1 ) split.setDef(1,def);
             // all uses by class to split
-            for( int j=0; j < def._outputs._len; j++ ) {
-                Node use = def._outputs.at(j);
+            for( Node use : ns ) {
                 if( use instanceof MachNode mach && use!=split ) {
                     // Check all use inputs for n, in case there's several
                     for( int i = 1; i < use.nIns(); i++ )
                         // Find a def input, and check register class
-                        if( use.in( i ) == def && mach.regmap( i ).overlap( rmask ) )
-                            // Modify use to use the split version specialized to this rclass
-                            { use.setDef( i, split ); j--; break; }
+                        if( use.in( i ) == def ) {
+                            RegMask m = mach.regmap( i );
+                            if( m!=null && mach.regmap( i ).overlap( rmask ) )
+                                // Modify use to use the split version specialized to this rclass
+                                use.setDefOrdered( i, split );
+                        }
                 }
             }
+            ns.clear();
         }
         return true;
     }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/RegMask.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/RegMask.java
index 1e7faff..d38e002 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/RegMask.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/RegMask.java
@@ -4,11 +4,11 @@
 import com.compilerprogramming.ezlang.compiler.SB;
 
 /** RegMask
- *  A "register mask" - 1 bit set for each allowed register.  In addition
+ *  A "register mask" - 1 bit set for each allowed register.  In addition,
  *  "stack slot" registers may be allowed, effectively making the set infinite.
  * <p>
  *  For smaller and simpler machines it suffices to make such masks an i64 or
- *  i128 (64 or 128 bit integers), and this presentation is by far the better
+ *  i128 (64- or 128-bit integers), and this presentation is by far the better
  *  way to go... if all register allocations can fit in this bit limitation.
  *  The allocator will need bits for stack-based parameters and for splits
  *  which cannot get a register.  For a 32-register machine like the X86, add 1
@@ -28,7 +28,7 @@ public class RegMask {
 
     public RegMask(int bit) {
         if( bit < 64 ) _bits0 = 1L<<bit;
-        else _bits1 = 1L<<(bit=64);
+        else _bits1 = 1L<<(bit-64);
     }
     public RegMask(long bits ) { _bits0 = bits; }
     public RegMask(long bits0, long bits1 ) { _bits0 = bits0; _bits1 = bits1; }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CFGNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CFGNode.java
index b1c2a94..cda5c80 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CFGNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CFGNode.java
@@ -1,6 +1,7 @@
 package com.compilerprogramming.ezlang.compiler.nodes;
 
 import com.compilerprogramming.ezlang.compiler.Compiler;
+import com.compilerprogramming.ezlang.compiler.codegen.CodeGen;
 import com.compilerprogramming.ezlang.compiler.sontypes.*;
 import java.util.BitSet;
 
@@ -68,7 +69,9 @@ public CFGNode uctrlSkipEmpty() {
      * See {@link <a href="https://en.wikipedia.org/wiki/Dominator_(graph_theory)">...</a>}
      */
     public int _idepth;
-    public int idepth() { return _idepth==0 ? (_idepth=idom().idepth()+1) : _idepth; }
+    public int idepth() {
+        return CodeGen.CODE.validIDepth(_idepth) ? _idepth : (_idepth=CodeGen.CODE.iDepthFrom(idom().idepth()));
+    }
 
     // Return the immediate dominator of this Node and compute dom tree depth.
     public CFGNode idom(Node dep) { return cfg(0); }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CallEndNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CallEndNode.java
index 1dd1f50..f2db2a9 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CallEndNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CallEndNode.java
@@ -74,8 +74,9 @@ public Node idealize() {
                     assert fun.in(1) instanceof StartNode && fun.in(2)==call;
                     // Disallow self-recursive inlining (loop unrolling by another name)
                     CFGNode idom = call;
-                    while( !(idom instanceof FunNode fun2) )
+                    while( !(idom instanceof FunNode) )
                         idom = idom.idom();
+                    // Inline?
                     if( idom != fun ) {
                         // Trivial inline: rewrite
                         _folding = true;
@@ -85,6 +86,9 @@ public Node idealize() {
                         fun.setDef(2,call.ctrl());  // Bypass the Call;
                         fun.ret().setDef(3,null);   // Return is folding also
                         CodeGen.CODE.addAll(fun._outputs);
+                        // Inlining immediately blows all cache idepth fields past the inline point.
+                        // Bump the global version number invalidating them en-masse.
+                        CodeGen.CODE.invalidateIDepthCaches();
                         return this;
                     }
                 } else {
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/FunNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/FunNode.java
index 9d0eba1..7e12aaf 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/FunNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/FunNode.java
@@ -114,8 +114,11 @@ public Node idealize() {
         return null;
     }
 
-    // Bypass Region idom, always assume depth == 1, one more than Start
-    @Override public int idepth() { return (_idepth=1); }
+    // Bypass Region idom, always assume depth == 1, one more than Start,
+    // unless folding then just a ID on input#1
+    @Override public int idepth() {
+        return _folding ? super.idepth() : CodeGen.CODE.iDepthAt(1);
+    }
     // Bypass Region idom, always assume idom is Start
     @Override public CFGNode idom(Node dep) { return cfg(1); }
 
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/LoopNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/LoopNode.java
index 4ff533c..a912ba8 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/LoopNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/LoopNode.java
@@ -21,7 +21,9 @@ public SONType compute() {
     }
 
     // Bypass Region idom, same as the default idom() using use in(1) instead of in(0)
-    @Override public int idepth() { return _idepth==0 ? (_idepth=idom().idepth()+1) : _idepth; }
+    public int idepth() {
+        return CodeGen.CODE.validIDepth(_idepth) ? _idepth : (_idepth=CodeGen.CODE.iDepthFrom(idom().idepth()));
+    }
     // Bypass Region idom, same as the default idom() using use in(1) instead of in(0)
     @Override public CFGNode idom(Node dep) { return entry(); }
 
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/NewNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/NewNode.java
index 8048e02..1d7ca33 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/NewNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/NewNode.java
@@ -92,8 +92,8 @@ public SONTypeTuple compute() {
     private RegMask _retMask;
     private RegMask _kills;
     public void cacheRegs(CodeGen code) {
-        _arg2Reg  = code._mach.callArgMask(SONTypeFunPtr.CALLOC,2).firstReg();
-        _arg3Mask = code._mach.callArgMask(SONTypeFunPtr.CALLOC,3);
+        _arg2Reg  = code._mach.callArgMask(SONTypeFunPtr.CALLOC,2,0).firstReg();
+        _arg3Mask = code._mach.callArgMask(SONTypeFunPtr.CALLOC,3,0);
         // Return mask depends on TFP (either GPR or FPR)
         _retMask = code._mach.retMask(SONTypeFunPtr.CALLOC);
         // Kill mask is all caller-saves, and any mirror stack slots for args
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/PhiNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/PhiNode.java
index 903f792..9ac7bb8 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/PhiNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/PhiNode.java
@@ -14,7 +14,7 @@ public class PhiNode extends Node {
     final SONType _declaredType;
 
     public PhiNode(String label, SONType declaredType, Node... inputs) { super(inputs); _label = label;  assert declaredType!=null; _declaredType = declaredType; }
-    public PhiNode(PhiNode phi, String label, SONType declaredType) { super(phi); _label = label; _declaredType = declaredType; }
+    public PhiNode(PhiNode phi, String label, SONType declaredType) { super(phi); _label = label; _type = _declaredType = declaredType; }
     public PhiNode(PhiNode phi) { super(phi); _label = phi._label; _declaredType = phi._declaredType;  }
 
     public PhiNode(RegionNode r, Node sample) {
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/RegionNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/RegionNode.java
index 34fa3cd..f193b2c 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/RegionNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/RegionNode.java
@@ -137,12 +137,13 @@ boolean hasMidUser(RegionNode r) {
 
     // Immediate dominator of Region is a little more complicated.
     @Override public int idepth() {
-        if( _idepth!=0 ) return _idepth;
+        if( CodeGen.CODE.validIDepth(_idepth) )
+            return _idepth;
         int d=0;
         for( Node n : _inputs )
             if( n!=null )
-                d = Math.max(d,((CFGNode)n).idepth()+1);
-        return _idepth=d;
+                d = Math.max(d,CodeGen.CODE.iDepthFrom(((CFGNode)n).idepth()));
+        return _idepth = d;
     }
 
     @Override public CFGNode idom(Node dep) {
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StartNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StartNode.java
index ac9053f..5908301 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StartNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StartNode.java
@@ -51,7 +51,7 @@ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
     @Override public Node idealize() { return null; }
 
     // No immediate dominator, and idepth==0
-    @Override public int idepth() { return 0; }
+    @Override public int idepth() { return CodeGen.CODE.iDepthAt(0); }
     @Override public CFGNode idom(Node dep) { return null; }
 
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StoreNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StoreNode.java
index 8d2c679..a37af4f 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StoreNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StoreNode.java
@@ -86,6 +86,11 @@ public Node idealize() {
             }
         }
 
+        // Store of zero after alloc
+        if( mem() instanceof ProjNode prj && prj.in(0) instanceof NewNode &&
+            prj.in(0)==ptr().in(0) &&  // Same NewNode memory & pointer
+            (val()._type==SONTypeInteger.ZERO || val()._type==SONType.NIL ) )
+            return mem();
 
         return null;
     }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CallARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CallARM.java
index 94722aa..f85e62d 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CallARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CallARM.java
@@ -21,7 +21,7 @@ public class CallARM extends CallNode implements MachNode, RIPRelSize {
     @Override public String label() { return op(); }
     @Override public String name() { return _name; }
     @Override public SONTypeFunPtr tfp() { return _tfp; }
-    @Override public RegMask regmap(int i) { return arm.callInMask(_tfp,i); }
+    @Override public RegMask regmap(int i) { return arm.callInMask(_tfp,i,fun()._maxArgSlot); }
     @Override public RegMask outregmap() { return null; }
 
     @Override public void encoding( Encoding enc ) {
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CallRRARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CallRRARM.java
index 71f9382..7b32cbf 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CallRRARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CallRRARM.java
@@ -11,7 +11,7 @@ public class CallRRARM extends CallNode implements MachNode {
     @Override public RegMask regmap(int i) {
         return i==_inputs._len
             ? arm.RMASK                // Function call target
-            : arm.callInMask(tfp(),i); // Normal argument
+            : arm.callInMask(tfp(),i,fun()._maxArgSlot); // Normal argument
     }
     @Override public RegMask outregmap() { return null; }
 
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/ParmARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/ParmARM.java
index 2761799..acba277 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/ParmARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/ParmARM.java
@@ -9,7 +9,7 @@ public class ParmARM extends ParmNode implements MachNode {
     final RegMask _rmask;
     ParmARM(ParmNode parm) {
         super(parm);
-        _rmask = arm.callInMask(fun().sig(),_idx);
+        _rmask = arm.callInMask(fun().sig(),_idx,0);
     }
     @Override public RegMask regmap(int i) { return null; }
     @Override public RegMask outregmap() { return _rmask; }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/arm.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/arm.java
index 928d2e6..e9b6644 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/arm.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/arm.java
@@ -563,8 +563,8 @@ public static int b_calloc(int opcode, int delta) {
         return (opcode << 26) | delta;
     }
 
-    @Override public RegMask callArgMask(SONTypeFunPtr tfp, int idx ) { return callInMask(tfp,idx); }
-    static RegMask callInMask(SONTypeFunPtr tfp, int idx ) {
+    @Override public RegMask callArgMask(SONTypeFunPtr tfp, int idx, int maxArgSlot ) { return callInMask(tfp,idx,maxArgSlot); }
+    static RegMask callInMask(SONTypeFunPtr tfp, int idx, int maxArgSlot ) {
         if( idx==0 ) return CodeGen.CODE._rpcMask;
         if( idx==1 ) return null;
         // Count floats in signature up to index
@@ -788,8 +788,7 @@ private Node lsr(ShrNode lsr) {
     private static int off;
     private static Node idx;
     private Node st(StoreNode st) {
-        Node xval = st.val() instanceof ConstantNode con && con._con == SONTypeInteger.ZERO ? null : st.val();
-        return new StoreARM(address(st),st.ptr(),idx,off,xval);
+        return new StoreARM(address(st),st.ptr(),idx,off,st.val());
     }
 
     // Gather addressing mode bits prior to constructing.  This is a builder
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CallRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CallRISC.java
index 292cf6a..457ca77 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CallRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CallRISC.java
@@ -19,9 +19,9 @@ public class CallRISC extends CallNode implements MachNode, RIPRelSize {
     @Override public String op() { return "call"; }
     @Override public String label() { return op(); }
     @Override public RegMask regmap(int i) {
-        return riscv.callInMask(_tfp,i);
+        return riscv.callInMask(_tfp,i,fun()._maxArgSlot);
     }
-    @Override public RegMask outregmap() { return riscv.RPC_MASK; }
+    @Override public RegMask outregmap() { return null; }
     @Override public String name() { return _name; }
     @Override public SONTypeFunPtr tfp() { return _tfp; }
 
@@ -29,8 +29,7 @@ public class CallRISC extends CallNode implements MachNode, RIPRelSize {
         // Short form +/-4K:  beq r0,r0,imm12
         // Long form:  auipc rX,imm20/32; jal r0,[rX+imm12/32]
         enc.relo(this);
-        short rpc = enc.reg(this);
-        enc.add4(riscv.j_type(riscv.OP_JAL, rpc, 0));
+        enc.add4(riscv.j_type(riscv.OP_JAL, riscv.RPC, 0));
     }
 
     // Delta is from opcode start
@@ -42,9 +41,8 @@ public class CallRISC extends CallNode implements MachNode, RIPRelSize {
 
     // Delta is from opcode start
     @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) {
-        short rpc = enc.reg(this);
         if( opLen==4 ) {
-            enc.patch4(opStart,riscv.j_type(riscv.OP_JAL, rpc, delta));
+            enc.patch4(opStart,riscv.j_type(riscv.OP_JAL, riscv.RPC, delta));
         } else {
             throw Utils.TODO();
         }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CallRRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CallRRISC.java
index 22d08d6..d44d55f 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CallRRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CallRRISC.java
@@ -11,7 +11,7 @@ public class CallRRISC extends CallNode implements MachNode {
     @Override public RegMask regmap(int i) {
         return i==_inputs._len-1
             ? riscv.RMASK                // Function call target
-            : riscv.callInMask(tfp(),i); // Normal argument
+            : riscv.callInMask(tfp(),i,fun()._maxArgSlot); // Normal argument
     }
     @Override public RegMask outregmap() { return null; }
 
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/ParmRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/ParmRISC.java
index 07b6884..7bfc5fd 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/ParmRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/ParmRISC.java
@@ -9,7 +9,7 @@ public class ParmRISC extends ParmNode implements MachNode {
     final RegMask _rmask;
     ParmRISC(ParmNode parm) {
         super(parm);
-        _rmask = riscv.callInMask(fun().sig(),_idx);
+        _rmask = riscv.callInMask(fun().sig(),_idx,0);
     }
     @Override public RegMask regmap(int i) { return null; }
     @Override public RegMask outregmap() { return _rmask; }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/StoreRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/StoreRISC.java
index d732933..177aecf 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/StoreRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/StoreRISC.java
@@ -36,7 +36,7 @@ public class StoreRISC extends MemOpRISC {
         short ptr = enc.reg(ptr());
         int op = val >= riscv.F_OFFSET ? riscv.OP_STOREFP : riscv.OP_STORE;
         if( val >= riscv.F_OFFSET  ) val -= riscv.F_OFFSET;
-        enc.add4(riscv.s_type(op, func3()&7, ptr, val, _off));
+        enc.add4(riscv.s_type(op, func3()&7, ptr, val == -1 ? riscv.ZERO : val, _off));
     }
 
     @Override public void asm(CodeGen code, SB sb) {
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/riscv.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/riscv.java
index eb10fab..b051fef 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/riscv.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/riscv.java
@@ -253,8 +253,8 @@ static public int fsetop(String op) {
         };
     }
 
-    @Override public RegMask callArgMask( SONTypeFunPtr tfp, int idx ) { return callInMask(tfp,idx); }
-    static RegMask callInMask( SONTypeFunPtr tfp, int idx ) {
+    @Override public RegMask callArgMask( SONTypeFunPtr tfp, int idx, int maxArgSlot ) { return callInMask(tfp,idx,maxArgSlot); }
+    static RegMask callInMask( SONTypeFunPtr tfp, int idx, int maxArgSlot ) {
         if( idx==0 ) return RPC_MASK;
         if( idx==1 ) return null;
         // Count floats in signature up to index
@@ -272,6 +272,7 @@ static RegMask callInMask( SONTypeFunPtr tfp, int idx ) {
                 return cargs[idx-2-fcnt];
         }
         // Pass on stack slot(8 and higher)
+        if( maxArgSlot>0 ) throw Utils.TODO();
         return new RegMask(MAX_REG + 1 + (idx - 2));
     }
 
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CallRX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CallRX86.java
index 82a9999..3940630 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CallRX86.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CallRX86.java
@@ -12,7 +12,7 @@ public class CallRX86 extends CallNode implements MachNode {
     @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
+            : x86_64_v2.callInMask(tfp(),i,fun()._maxArgSlot); // Normal argument
     }
     @Override public RegMask outregmap() { return null; }
     @Override public void encoding( Encoding enc ) {
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CallX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CallX86.java
index 362f54e..ec9b60d 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CallX86.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CallX86.java
@@ -20,7 +20,7 @@ public class CallX86 extends CallNode implements MachNode, RIPRelSize {
     @Override public String label() { return op(); }
     @Override public String name() { return _name; }
     @Override public SONTypeFunPtr tfp() { return _tfp; }
-    @Override public RegMask regmap(int i) { return x86_64_v2.callInMask(_tfp,i);  }
+    @Override public RegMask regmap(int i) { return x86_64_v2.callInMask(_tfp,i,fun()._maxArgSlot);  }
     @Override public RegMask outregmap() { return null; }
     @Override public int nargs() { return nIns()-2; } // Minus control, memory, fptr
 
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/ParmX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/ParmX86.java
index 33995f1..d7ee657 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/ParmX86.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/ParmX86.java
@@ -9,7 +9,7 @@ public class ParmX86 extends ParmNode implements MachNode {
     final RegMask _rmask;
     ParmX86( ParmNode parm ) {
         super(parm);
-        _rmask = x86_64_v2.callInMask(fun().sig(),_idx);
+        _rmask = x86_64_v2.callInMask(fun().sig(),_idx,1/*RPC*/);
     }
     @Override public RegMask regmap(int i) { return null; }
     @Override public RegMask outregmap() { return _rmask; }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/SplitX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/SplitX86.java
index 058df1e..545d0f1 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/SplitX86.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/SplitX86.java
@@ -49,9 +49,16 @@ public class SplitX86 extends SplitNode {
 
         // Stack spills
         if( dst >= x86_64_v2.MAX_REG ) {
-            if( src >= x86_64_v2.MAX_REG )
-                throw Utils.TODO(); // Very rare stack-stack move
             int off = enc._fun.computeStackOffset(enc._code,dst);
+            if( src >= x86_64_v2.MAX_REG ) {
+                // Rare stack-stack move.  push [RSP+soff]; pop [RSP+doff]
+                int soff = enc._fun.computeStackOffset(enc._code,src);
+                enc.add1(0xFF);
+                x86_64_v2.indirectAdr(0, (short)-1/*index*/, (short)x86_64_v2.RSP, soff, 6, enc);
+                enc.add1(0x8F);
+                x86_64_v2.indirectAdr(0, (short)-1/*index*/, (short)x86_64_v2.RSP,  off, 0, enc);
+                return;
+            }
             StoreX86.encVal(enc, srcX ? SONTypeFloat.F64 : SONTypeInteger.BOT, (short)x86_64_v2.RSP, (short)-1/*index*/, src, off, 0);
             return;
         }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/StoreX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/StoreX86.java
index 49ec2bb..243f48b 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/StoreX86.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/StoreX86.java
@@ -44,7 +44,7 @@ public class StoreX86 extends MemOpX86 {
             case 0: enc.add1(_imm); break;
             case 1: enc.add2(_imm); break;
             case 2: enc.add4(_imm); break;
-            case 3: enc.add8(_imm); break;
+            case 3: enc.add4(_imm); break; // Limit of a 4 byte immediate
             }
         } else {
             encVal(enc,_declaredType,ptr,idx,src,_off,_scale);
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/x86_64_v2.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/x86_64_v2.java
index bee5d2e..547abda 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/x86_64_v2.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/x86_64_v2.java
@@ -185,13 +185,13 @@ public static void indirectAdr( int scale, short index, short base, int offset,
 
     // Map from function signature and argument index to register.
     // Used to set input registers to CallNodes, and ParmNode outputs.
-    @Override public RegMask callArgMask( SONTypeFunPtr tfp, int idx ) { return callInMask(tfp,idx); }
-    static RegMask callInMask( SONTypeFunPtr tfp, int idx ) {
+    @Override public RegMask callArgMask( SONTypeFunPtr tfp, int idx, int maxArgSlot ) { return callInMask(tfp,idx,maxArgSlot); }
+    static RegMask callInMask( SONTypeFunPtr tfp, int idx, int maxArgSlot ) {
         if( idx==0 ) return RPC_MASK;
         if( idx==1 ) return null;
         return switch( CodeGen.CODE._callingConv ) {
-        case "SystemV" -> callSys5 (tfp,idx);
-        case "Win64"   -> callWin64(tfp,idx);
+        case "SystemV" -> callSys5 (tfp,idx,maxArgSlot);
+        case "Win64"   -> callWin64(tfp,idx,maxArgSlot);
         default -> throw Utils.TODO();
         };
     }
@@ -230,11 +230,11 @@ static RegMask callInMask( SONTypeFunPtr tfp, int idx ) {
         R09_MASK,
     };
 
-    static RegMask callWin64(SONTypeFunPtr tfp, int idx ) {
+    static RegMask callWin64(SONTypeFunPtr tfp, int idx, int maxArgSlot ) {
         // idx 2,3,4,5 passed in registers, with stack slot mirrors.
         // idx >= 6 passed on stack, starting at slot#1 (#0 reserved for RPC).
         if( idx >= 6 )
-            return new RegMask(MAX_REG+1/*RPC*/+(idx-2));
+            return new RegMask(MAX_REG+maxArgSlot+(idx-2));
         return tfp.arg(idx-2) instanceof SONTypeFloat
             ? XMMS8     [idx-2]
             : WIN64_CALL[idx-2];
@@ -271,7 +271,7 @@ static short maxArgSlotWin64(SONTypeFunPtr tfp) {
         R09_MASK,
     };
 
-    static RegMask callSys5(SONTypeFunPtr tfp, int idx ) {
+    static RegMask callSys5(SONTypeFunPtr tfp, int idx, int maxArgSlot ) {
         // First 6 integers passed in registers: rdi,rsi,rdx,rcx,r08,r09
         // First 8 floats passed in registers: xmm0-xmm7
         int icnt=0, fcnt=0;     // Count of ints, floats
@@ -281,8 +281,8 @@ static RegMask callSys5(SONTypeFunPtr tfp, int idx ) {
         }
         int nstk = Math.max(icnt-6,0)+Math.max(fcnt-8,0);
         return tfp.arg(idx-2) instanceof SONTypeFloat
-            ? fcnt<8 ? XMMS8    [fcnt] : new RegMask(MAX_REG+1/*RPC*/+nstk)
-            : icnt<6 ? SYS5_CALL[icnt] : new RegMask(MAX_REG+1/*RPC*/+nstk);
+            ? fcnt<8 ? XMMS8    [fcnt] : new RegMask(MAX_REG+maxArgSlot+nstk)
+            : icnt<6 ? SYS5_CALL[icnt] : new RegMask(MAX_REG+maxArgSlot+nstk);
     }
     static short maxArgSlotSys5(SONTypeFunPtr tfp) {
         int icnt=0, fcnt=0;     // Count of ints, floats
diff --git a/seaofnodes/src/test/cases/mergsort/sort_ez.out b/seaofnodes/src/test/cases/mergsort/sort_ez.out
new file mode 100644
index 0000000..946e758
--- /dev/null
+++ b/seaofnodes/src/test/cases/mergsort/sort_ez.out
@@ -0,0 +1,716 @@
+START:                            [[   ]]
+  10 Start   ____ ____ ____       [[    15   52   55   61  158   12   67    9  102  179  ]]  [  Ctrl, #TOP, int]
+
+L9:                               [[  START      ]]
+   9 merge_s ____   10            [[    50   51   54   56   60  321  320  319  318  317  316  315  314  313  312  311  310  309  308  307  306  305  304  401  398  362  364  400  403  405    8  ]]  Ctrl
+  50 $mem       9   15            [[     8       ]]  #BOT
+  51 a          9   52            [[   398       ]]  *[int]
+  54 n          9   55            [[   401       ]]  int
+  56 b          9   52            [[   362       ]]  *[int]
+  60 $rpc       9   61            [[     2       ]]  $[ALL]
+ 405 #0         9                 [[     8       ]]  0
+ 403 mov        9  401            [[     8       ]]
+ 400 mov        9  398            [[     8       ]]
+ 364 mov        9  362            [[     8       ]]
+   8 call       9   50  400  405  403  364  [[     7  ]]  Ctrl
+ 362 mov        9   56            [[   363  364  ]]
+ 398 mov        9   51            [[   399  400  ]]
+ 401 mov        9   54            [[   402  403  ]]
+ 305 rbp        9                 [[     2       ]]
+ 309 r13        9                 [[     2       ]]
+ 310 r14        9                 [[     2       ]]
+ 311 r15        9                 [[     2       ]]
+ 312 xmm6       9                 [[     2       ]]
+ 313 xmm7       9                 [[     2       ]]
+ 314 xmm8       9                 [[     2       ]]
+ 315 xmm9       9                 [[     2       ]]
+ 316 xmm10      9                 [[     2       ]]
+ 317 xmm11      9                 [[     2       ]]
+ 318 xmm12      9                 [[     2       ]]
+ 319 xmm13      9                 [[     2       ]]
+ 320 xmm14      9                 [[     2       ]]
+ 321 xmm15      9                 [[     2       ]]
+ 304 rbx        9                 [[     2       ]]
+ 308 r12        9                 [[     2       ]]
+ 307 rdi        9                 [[     2       ]]
+ 306 rsi        9                 [[     2       ]]
+
+L12:                              [[  START      ]]
+  12 main    ____   10            [[    14  208  303  302  301  300  299  298  297  296  295  294  293  292  291  290  289  288  287  286  409  394   22  345  347   32  348   31  349   30  350   29  351   28  352   27  353   26  354   25  355   24  356   23  393   20  397  392   17  360   45   44   43   42   41   40   39   38   37   36   13  357  359  396   11  ]]  Ctrl
+  14 $mem      12   15            [[    13   22   22  ]]  #BOT
+ 208 $rpc      12   61            [[   203       ]]  $[ALL]
+ 347 mov       12  345            [[    32       ]]
+  32 st8       12   33  347 ____ ____  [[    31  ]]  #3:int
+ 348 mov       12  345            [[    31       ]]
+  31 st8       12   32  348 ____ ____  [[    30  ]]  #3:int
+ 349 mov       12  345            [[    30       ]]
+  30 st8       12   31  349 ____ ____  [[    29  ]]  #3:int
+ 350 mov       12  345            [[    29       ]]
+  45 st8       12   46  360 ____ ____  [[    44  ]]  #3:int
+  29 st8       12   30  350 ____ ____  [[    28  ]]  #3:int
+ 351 mov       12  345            [[    28       ]]
+  44 st8       12   45  360 ____ ____  [[    43  ]]  #3:int
+  28 st8       12   29  351 ____ ____  [[    27  ]]  #3:int
+  43 st8       12   44  360 ____ ____  [[    42  ]]  #3:int
+ 352 mov       12  345            [[    27       ]]
+  42 st8       12   43  360 ____ ____  [[    41  ]]  #3:int
+  27 st8       12   28  352 ____ ____  [[    26  ]]  #3:int
+ 353 mov       12  345            [[    26       ]]
+  41 st8       12   42  360 ____ ____  [[    40  ]]  #3:int
+  26 st8       12   27  353 ____ ____  [[    25  ]]  #3:int
+ 354 mov       12  345            [[    25       ]]
+  40 st8       12   41  360 ____ ____  [[    39  ]]  #3:int
+  25 st8       12   26  354 ____ ____  [[    24  ]]  #3:int
+ 355 mov       12  345            [[    24       ]]
+  39 st8       12   40  360 ____ ____  [[    38  ]]  #3:int
+  24 st8       12   25  355 ____ ____  [[    23  ]]  #3:int
+ 356 mov       12  345            [[    23       ]]
+  38 st8       12   39  360 ____ ____  [[    37  ]]  #3:int
+  23 st8       12   24  356 ____ ____  [[    20  ]]  #3:int
+ 393 #88       12                 [[    20       ]]  88
+  37 st8       12   38  360 ____ ____  [[    36  ]]  #3:int
+  20 new_ary   12  393   21   23  [[    19   35   48  ]]  [  Ctrl, *![int], #2:u32, #3:int]
+  19 $2        20                 [[    17       ]]  #2:u32
+  35 $3        20                 [[    17       ]]  #3:int
+  48 [int]     20                 [[   397       ]]  *[int]
+ 394 #88       12                 [[    22       ]]  88
+  36 st8       12   37  360 ____ ____  [[    13  ]]  #3:int
+ 392 #88       12                 [[    17       ]]  88
+ 397 mov       12   48            [[   359       ]]
+ 396 #10       12                 [[    11       ]]  10
+  17 new_ary   12  392   19   35  [[    16   46   47  ]]  [  Ctrl, *![int], #2:u32, #3:int]
+  16 $2        17                 [[    13       ]]  #2:u32
+  46 $3        17                 [[    45       ]]  #3:int
+  47 [int]     17                 [[   360       ]]  *[int]
+  13 ALLMEM    12   14   16   36  [[    11       ]]  #BOT
+ 357 mov       12  345            [[    11       ]]
+ 359 mov       12  397            [[    11       ]]
+  22 new_ary   12  394   14   14  [[    21   33   34  ]]  [  Ctrl, *![int], #2:u32, #3:int]
+  21 $2        22                 [[    20       ]]  #2:u32
+  33 $3        22                 [[    32       ]]  #3:int
+  34 [int]     22                 [[   345       ]]  *[int]
+  11 call      12   13  357  359  396  [[   182  ]]  Ctrl
+ 345 mov       12   34            [[   346  357  356  355  354  353  352  351  350  349  348  347  ]]
+ 360 mov       12   47            [[   361   36   37   38   39   40   41   42   43   44   45  ]]
+ 286 rbx       12                 [[   409       ]]
+ 409 mov       12  286            [[   410       ]]
+ 288 rsi       12                 [[   203       ]]
+ 289 rdi       12                 [[   203       ]]
+ 290 r12       12                 [[   203       ]]
+ 291 r13       12                 [[   203       ]]
+ 292 r14       12                 [[   203       ]]
+ 293 r15       12                 [[   203       ]]
+ 294 xmm6      12                 [[   203       ]]
+ 295 xmm7      12                 [[   203       ]]
+ 296 xmm8      12                 [[   203       ]]
+ 297 xmm9      12                 [[   203       ]]
+ 298 xmm10     12                 [[   203       ]]
+ 299 xmm11     12                 [[   203       ]]
+ 300 xmm12     12                 [[   203       ]]
+ 301 xmm13     12                 [[   203       ]]
+ 302 xmm14     12                 [[   203       ]]
+ 303 xmm15     12                 [[   203       ]]
+ 287 rbp       12                 [[   203       ]]
+
+L179:                             [[  START      ]]
+ 179 eq      ____   10            [[   189  190  194  197  201  285  284  283  282  281  280  279  278  277  276  275  274  273  272  271  270  269  268  390  178  ]]  Ctrl
+ 189 $mem     179   15            [[   187  193  199  199  ]]  #BOT
+ 190 a        179   52            [[   187       ]]  *[int]
+ 194 b        179   52            [[   193       ]]  *[int]
+ 197 n        179   55            [[   195       ]]  int
+ 201 $rpc     179   61            [[   174       ]]  $[ALL]
+ 390 #0       179                 [[   191       ]]  0
+ 272 r12      179                 [[   174       ]]
+ 273 r13      179                 [[   174       ]]
+ 274 r14      179                 [[   174       ]]
+ 275 r15      179                 [[   174       ]]
+ 276 xmm6     179                 [[   174       ]]
+ 277 xmm7     179                 [[   174       ]]
+ 278 xmm8     179                 [[   174       ]]
+ 279 xmm9     179                 [[   174       ]]
+ 280 xmm10    179                 [[   174       ]]
+ 281 xmm11    179                 [[   174       ]]
+ 282 xmm12    179                 [[   174       ]]
+ 283 xmm13    179                 [[   174       ]]
+ 284 xmm14    179                 [[   174       ]]
+ 285 xmm15    179                 [[   174       ]]
+ 271 rdi      179                 [[   174       ]]
+ 270 rsi      179                 [[   174       ]]
+ 269 rbp      179                 [[   174       ]]
+ 268 rbx      179                 [[   174       ]]
+
+L158:                             [[  START      ]]
+ 158 copy_ar ____   10            [[   163  165  167  170  172  173  249  248  247  246  245  244  243  242  241  240  239  238  237  236  235  234  233  232  157  ]]  Ctrl
+ 163 begin    158   55            [[   162       ]]  int
+ 165 end      158   55            [[   160       ]]  int
+ 167 $mem     158   15            [[   166  168  ]]  #BOT
+ 170 b        158   52            [[   169       ]]  *[int]
+ 172 a        158   52            [[   171       ]]  *[int]
+ 173 $rpc     158   61            [[   154       ]]  $[ALL]
+ 232 rbx      158                 [[   154       ]]
+ 238 r14      158                 [[   154       ]]
+ 239 r15      158                 [[   154       ]]
+ 240 xmm6     158                 [[   154       ]]
+ 241 xmm7     158                 [[   154       ]]
+ 242 xmm8     158                 [[   154       ]]
+ 243 xmm9     158                 [[   154       ]]
+ 244 xmm10    158                 [[   154       ]]
+ 245 xmm11    158                 [[   154       ]]
+ 246 xmm12    158                 [[   154       ]]
+ 247 xmm13    158                 [[   154       ]]
+ 248 xmm14    158                 [[   154       ]]
+ 249 xmm15    158                 [[   154       ]]
+ 237 r13      158                 [[   154       ]]
+ 236 r12      158                 [[   154       ]]
+ 235 rdi      158                 [[   154       ]]
+ 234 rsi      158                 [[   154       ]]
+ 233 rbp      158                 [[   154       ]]
+
+L67:                              [[  START      ]]
+  67 split_m ____   10            [[    70   73   74   75   78   95  267  266  265  264  263  262  261  260  259  258  257  256  255  254  253  252  251  250  383  332  328  324  338  387   84   82   66  ]]  Ctrl
+  70 $mem      67   15            [[    68   91  ]]  #BOT
+  73 a         67   52            [[   324       ]]  *[int]
+  74 b         67   52            [[   328       ]]  *[int]
+  75 begin     67   55            [[   383       ]]  int
+  78 end       67   55            [[   332       ]]  int
+  95 $rpc      67   61            [[    63       ]]  $[ALL]
+ 387 mov       67  383            [[    84       ]]
+ 328 mov       67   74            [[   331  330  329  ]]
+ 332 mov       67   78            [[   338  336  333  335  ]]
+ 383 mov       67   75            [[   387  386  384  385  ]]
+ 338 mov       67  332            [[    84       ]]
+ 324 mov       67   73            [[   327  325  326  ]]
+  84 sub       67  338  387       [[    82       ]]  int
+  82 cmp       67   84            [[    66       ]]  bool
+  66 j<=       67   82            [[    65   69  ]]  [  Ctrl, Ctrl]
+ 255 r13       67                 [[    63       ]]
+ 256 r14       67                 [[    63       ]]
+ 257 r15       67                 [[    63       ]]
+ 258 xmm6      67                 [[    63       ]]
+ 259 xmm7      67                 [[    63       ]]
+ 260 xmm8      67                 [[    63       ]]
+ 261 xmm9      67                 [[    63       ]]
+ 262 xmm10     67                 [[    63       ]]
+ 263 xmm11     67                 [[    63       ]]
+ 264 xmm12     67                 [[    63       ]]
+ 265 xmm13     67                 [[    63       ]]
+ 266 xmm14     67                 [[    63       ]]
+ 267 xmm15     67                 [[    63       ]]
+ 252 rsi       67                 [[    63       ]]
+ 251 rbp       67                 [[    63       ]]
+ 250 rbx       67                 [[    63       ]]
+ 253 rdi       67                 [[    63       ]]
+ 254 r12       67                 [[    63       ]]
+
+L102:                             [[  START      ]]
+ 102 merge   ____   10            [[   119  122  125  132  134  139  153  231  230  229  228  227  226  225  224  223  222  221  220  219  218  217  216  215  214  406  322  368  373  101  ]]  Ctrl
+ 119 begin    102   55            [[   368  135  ]]  int
+ 122 middle   102   55            [[   116  373  ]]  int
+ 125 end      102   55            [[   123  150  ]]  int
+ 132 $mem     102   15            [[   131  152  ]]  #BOT
+ 134 b        102   52            [[   133       ]]  *[int]
+ 139 a        102   52            [[   322       ]]  *[int]
+ 153 $rpc     102   61            [[    98       ]]  $[ALL]
+ 373 mov      102  122            [[   126       ]]
+ 368 mov      102  119            [[   118       ]]
+ 322 mov      102  139            [[   323  129  140  138  ]]
+ 214 rbx      102                 [[   406       ]]
+ 406 mov      102  214            [[   407       ]]
+ 217 rdi      102                 [[    98       ]]
+ 222 xmm6     102                 [[    98       ]]
+ 223 xmm7     102                 [[    98       ]]
+ 224 xmm8     102                 [[    98       ]]
+ 225 xmm9     102                 [[    98       ]]
+ 226 xmm10    102                 [[    98       ]]
+ 227 xmm11    102                 [[    98       ]]
+ 228 xmm12    102                 [[    98       ]]
+ 229 xmm13    102                 [[    98       ]]
+ 230 xmm14    102                 [[    98       ]]
+ 231 xmm15    102                 [[    98       ]]
+ 216 rsi      102                 [[    98       ]]
+ 215 rbp      102                 [[    98       ]]
+ 220 r14      102                 [[    98       ]]
+ 221 r15      102                 [[    98       ]]
+ 219 r13      102                 [[    98       ]]
+ 218 r12      102                 [[    98       ]]
+
+LOOP178:                          [[  L179      L184       ]]
+ 178 Loop    ____  179  419       [[   191  195  177  ]]  Ctrl
+ 191 Phi_i    178  390  192       [[   195  187  192  193  ]]  int
+ 195 cmp      178  191  197       [[   177       ]]  bool
+ 177 j>=      178  195            [[   176  186  ]]  [  Ctrl, Ctrl]
+
+LOOP157:                          [[  L158      L159       ]]
+ 157 Loop    ____  158  411       [[   162  168  160  156  ]]  Ctrl
+ 162 Phi_k    157  163  164       [[   160  164  169  171  ]]  int
+ 168 Phi_$3   157  167  169       [[   166  169  171  ]]  #BOT
+ 160 cmp      157  162  165       [[   156       ]]  bool
+ 156 j>=      157  160            [[   155  159  ]]  [  Ctrl, Ctrl]
+
+LOOP101:                          [[  L102      L103       ]]
+ 101 Loop    ____  102  413       [[   118  126  131  135  150  100  ]]  Ctrl
+ 118 Phi_i    101  368  120       [[   129  116  121  120  138  ]]  int
+ 126 Phi_j    101  373  127       [[   142  123  127  128  140  ]]  int
+ 131 Phi_$3   101  132  133       [[   129  133  138  140  142  152  ]]  #BOT
+ 135 Phi_k    101  119  136       [[   150  133  136  ]]  int
+ 150 cmp      101  135  125       [[   100       ]]  bool
+ 100 j>=      101  150            [[    99  115  ]]  [  Ctrl, Ctrl]
+
+L7:                               [[  L9         ]]
+   7 CallEnd    8                 [[     6   57  ]]  [  Ctrl, #BOT, int]
+  57 $mem       7                 [[     5       ]]  #BOT
+
+L182:                             [[  L12        ]]
+ 182 CallEnd   11                 [[   181  183  ]]  [  Ctrl, #BOT, int]
+ 183 $mem     182                 [[   180       ]]  #BOT
+
+L65:                              [[  L67        ]]
+  65 True      66                 [[    53   64  ]]  Ctrl
+  53 #0        65                 [[    93       ]]  0
+
+L69:                              [[  L67        ]]
+  69 False     66                 [[   335  386   77   79   76  341  326  331  344  385   68  ]]  Ctrl
+ 326 mov       69  324            [[    68       ]]
+ 331 mov       69  328            [[    68       ]]
+ 385 mov       69  383            [[    68       ]]
+ 344 mov       69  341            [[    68       ]]
+  68 call      69   70  326  385  344  331  [[    72  ]]  Ctrl
+ 335 mov       69  332            [[    77       ]]
+ 386 mov       69  383            [[    77       ]]
+  77 add       69  335  386       [[    76       ]]  int
+  79 #2        69                 [[    76       ]]  2
+  76 div       69   77   79       [[   341       ]]  int
+ 341 mov       69   76            [[   344  342  343  ]]
+
+L6:                               [[  L7         ]]
+   6 $ctrl      7                 [[   363  399  402  404    5  ]]  Ctrl
+ 363 mov        6  362            [[     5       ]]
+ 399 mov        6  398            [[     5       ]]
+ 404 #0         6                 [[     5       ]]  0
+ 402 mov        6  401            [[     5       ]]
+   5 call       6   57  399  404  402  363  [[     4  ]]  Ctrl
+
+L186:                             [[  LOOP178    ]]
+ 186 False    177                 [[   193  187  185  ]]  Ctrl
+ 193 ld8      186  189  194  191  [[   187       ]]  int
+ 187 cmp8     186  189  190  191  193  [[   185  ]]  bool
+ 185 j!=      186  187            [[   184  198  ]]  [  Ctrl, Ctrl]
+
+L176:                             [[  LOOP178    ]]
+ 176 True     177                 [[   149  175  ]]  Ctrl
+ 149 #1       176                 [[   200       ]]  1
+
+L181:                             [[  L182       ]]
+ 181 $ctrl    182                 [[   346  361  395  180  ]]  Ctrl
+ 346 mov      181  345            [[   180       ]]
+ 361 mov      181  360            [[   180       ]]
+ 395 #10      181                 [[   180       ]]  10
+ 180 call     181  183  346  361  395  [[   205  ]]  Ctrl
+
+L159:                             [[  LOOP157    ]]
+ 159 False    156                 [[   171  169  164  411  ]]  Ctrl
+ 164 inc      159  162            [[   162       ]]  int
+ 171 ld8      159  168  172  162  [[   169       ]]  int
+ 169 st8      159  168  170  162  171  [[   168  ]]  #3:Bot
+
+L115:                             [[  LOOP101    ]]
+ 115 False    100                 [[   116  114  ]]  Ctrl
+ 116 cmp      115  118  122       [[   114       ]]  bool
+ 114 j<       115  116            [[   113  143  ]]  [  Ctrl, Ctrl]
+
+L99:                              [[  LOOP101    ]]
+  99 True     100                 [[   152  407  408   98  ]]  Ctrl
+ 152 ALLMEM    99  132 ____  131  [[    98       ]]  #BOT
+ 407 mov       99  406            [[    98       ]]
+ 408 #0        99                 [[    98       ]]  0
+  98 Return    99  152  408  153  407  215  216  217  218  219  220  221  222  223  224  225  226  227  228  229  230  231  [[     1  ]]  [  Ctrl, #BOT, 0]
+
+L155:                             [[  LOOP157    ]]
+ 155 True     156                 [[   166  212  154  ]]  Ctrl
+ 166 ALLMEM   155  167 ____  168  [[   154       ]]  #BOT
+ 212 #0       155                 [[   154       ]]  0
+ 154 Return   155  166  212  173  232  233  234  235  236  237  238  239  240  241  242  243  244  245  246  247  248  249  [[     1  ]]  [  Ctrl, #BOT, 0]
+
+L4:                               [[  L6         ]]
+   4 CallEnd    5                 [[     3   58   59  ]]  [  Ctrl, #BOT, int]
+  58 $mem       4                 [[     2       ]]  #BOT
+  59 #2         4                 [[     2       ]]  int
+
+L198:                             [[  L186       ]]
+ 198 True     185                 [[   391  418  ]]  Ctrl
+ 391 #0       198                 [[   200       ]]  0
+ 418 jmp      198                 [[   175       ]]
+
+L184:                             [[  L186       ]]
+ 184 False    185                 [[   192  419  ]]  Ctrl
+ 192 inc      184  191            [[   191       ]]  int
+
+L143:                             [[  L115       ]]
+ 143 False    114                 [[   381  417  ]]  Ctrl
+ 381 #0       143                 [[   148       ]]  0
+ 417 jmp      143                 [[   108       ]]
+
+L205:                             [[  L181       ]]
+ 205 CallEnd  180                 [[   204  206  207  ]]  [  Ctrl, #BOT, bool]
+ 206 $mem     205                 [[   203       ]]  #BOT
+ 207 #2       205                 [[   203       ]]  bool
+
+L113:                             [[  L115       ]]
+ 113 True     114                 [[   123  112  ]]  Ctrl
+ 123 cmp      113  125  126       [[   112       ]]  bool
+ 112 j<=      113  123            [[   111  144  ]]  [  Ctrl, Ctrl]
+
+L72:                              [[  L69        ]]
+  72 CallEnd   68                 [[    71   81  ]]  [  Ctrl, #BOT, int]
+  71 $mem      72                 [[    80       ]]  #BOT
+
+L3:                               [[  L4         ]]
+   3 $ctrl      4                 [[     2       ]]  Ctrl
+   2 Return     3   58   59   60  304  305  306  307  308  309  310  311  312  313  314  315  316  317  318  319  320  321  [[     1  ]]  [  Ctrl, #BOT, int]
+
+L81:                              [[  L72        ]]
+  81 $ctrl     72                 [[   327  330  336  343   80  ]]  Ctrl
+ 327 mov       81  324            [[    80       ]]
+ 330 mov       81  328            [[    80       ]]
+ 343 mov       81  341            [[    80       ]]
+ 336 mov       81  332            [[    80       ]]
+  80 call      81   71  327  343  336  330  [[    89  ]]  Ctrl
+
+L204:                             [[  L205       ]]
+ 204 $ctrl    205                 [[   410  203  ]]  Ctrl
+ 410 mov      204  409            [[   203       ]]
+ 203 Return   204  206  207  208  410  287  288  289  290  291  292  293  294  295  296  297  298  299  300  301  302  303  [[     1  ]]  [  Ctrl, #BOT, bool]
+
+L175:                             [[  L176      L198       ]]
+ 175 Region  ____  176  418       [[   200  199  174  ]]  Ctrl
+ 200 Phi_res  175  149  391       [[   174       ]]  bool
+ 199 ALLMEM   175  189 ____  189  [[   174       ]]  #BOT
+ 174 Return   175  199  200  201  268  269  270  271  272  273  274  275  276  277  278  279  280  281  282  283  284  285  [[     1  ]]  [  Ctrl, #BOT, bool]
+
+L111:                             [[  L113       ]]
+ 111 False    112                 [[   323  142  129  110  ]]  Ctrl
+ 323 mov      111  322            [[   142       ]]
+ 142 ld8      111  131  323  126  [[   129       ]]  int
+ 129 cmp8     111  131  322  118  142  [[   110  ]]  bool
+ 110 j<=      111  129            [[   109  145  ]]  [  Ctrl, Ctrl]
+
+L144:                             [[  L113       ]]
+ 144 True     112                 [[   380  108  ]]  Ctrl
+ 380 #1       144                 [[   148       ]]  1
+
+L89:                              [[  L81        ]]
+  89 CallEnd   80                 [[    88   90  ]]  [  Ctrl, #BOT, int]
+  90 $mem      89                 [[    87       ]]  #BOT
+
+L88:                              [[  L89        ]]
+  88 $ctrl     89                 [[   325  329  333  342  384   87  ]]  Ctrl
+ 325 mov       88  324            [[    87       ]]
+ 329 mov       88  328            [[    87       ]]
+ 384 mov       88  383            [[    87       ]]
+ 342 mov       88  341            [[    87       ]]
+ 333 mov       88  332            [[    87       ]]
+  87 call      88   90  329  384  342  333  325  [[    86  ]]  Ctrl
+
+L109:                             [[  L111       ]]
+ 109 True     110                 [[   379  415  ]]  Ctrl
+ 379 #1       109                 [[   148       ]]  1
+ 415 jmp      109                 [[   108       ]]
+
+L145:                             [[  L111       ]]
+ 145 False    110                 [[   382  416  ]]  Ctrl
+ 382 #0       145                 [[   148       ]]  0
+ 416 jmp      145                 [[   108       ]]
+
+L108:                             [[  L109      L143      L144      L145       ]]
+ 108 Region  ____  415  417  144  416  [[   148  146  107  ]]  Ctrl
+ 148 Phi_con  108  379  381  380  382  [[   146  ]]  bool
+ 146 test     108  148            [[   107       ]]
+ 107 j!=      108  146            [[   104  141  ]]  [  Ctrl, Ctrl]
+
+L86:                              [[  L88        ]]
+  86 CallEnd   87                 [[    85   92   94  ]]  [  Ctrl, #BOT, int]
+  92 $mem      86                 [[    91       ]]  #BOT
+  94 #2        86                 [[    93       ]]  int
+
+L141:                             [[  L108       ]]
+ 141 False    107                 [[   140  128  414  ]]  Ctrl
+ 128 inc      141  126            [[   127       ]]  int
+ 140 ld8      141  131  322  126  [[   137       ]]  int
+ 414 jmp      141                 [[   103       ]]
+
+L104:                             [[  L108       ]]
+ 104 True     107                 [[   138  121  103  ]]  Ctrl
+ 121 inc      104  118            [[   120       ]]  int
+ 138 ld8      104  131  322  118  [[   137       ]]  int
+
+L85:                              [[  L86        ]]
+  85 $ctrl     86                 [[   412       ]]  Ctrl
+ 412 jmp       85                 [[    64       ]]
+
+L103:                             [[  L104      L141       ]]
+ 103 Region  ____  104  414       [[   120  127  137  133  136  413  ]]  Ctrl
+ 120 Phi_i    103  121  118       [[   118       ]]  int
+ 127 Phi_j    103  126  128       [[   126       ]]  int
+ 137 Phi_$3   103  138  140       [[   133       ]]  int
+ 136 inc      103  135            [[   135       ]]  int
+ 133 st8      103  131  134  135  137  [[   131  ]]  #3:Bot
+
+L64:                              [[  L65       L85        ]]
+  64 Region  ____   65  412       [[    91   93   63  ]]  Ctrl
+  91 Phi_$me   64   70   92       [[    63       ]]  #BOT
+  93 Phi_arg   64   53   94       [[    63       ]]  int
+  63 Return    64   91   93   95  250  251  252  253  254  255  256  257  258  259  260  261  262  263  264  265  266  267  [[     1  ]]  [  Ctrl, #BOT, int]
+
+L1:                               [[  L155      L175      L204       ]]
+   1 Stop       2   63   98  154  174  203  [[   ]]  Bot
+
+
+
+---eq { *![int] *![int] int -> int #1}---------------------------
+               a:rcx,b:rdx,n:r8
+0000 ??        xor   rax,rax
+0001 ??        JMP   L178
+LOOP178:
+               i:rax
+0002 ??        cmp   rax, r8
+0003 ??        j>=   L176                           // L186
+L186:
+0004 ??        ld8   r9,[rdx+rax*8+8]
+0005 ??        cmp8  r9, [rcx+rax*8+8]
+0006 ??        j!=   L198                           // L184
+L184:
+0007 ??        inc   rax += #1
+0008 ??        jmp   LOOP178
+L419:
+0009 ??        JMP   L178
+L198:
+000A ??        xor   rax,rax
+000B ??        jmp   L175
+L418:
+000C ??        JMP   L175
+L176:
+000D ??        ldi   rax = #1
+000E ??        JMP   L175
+L175:
+               result:rax
+000F ??        ALLME
+0010 ??        ret
+---{ *![int] *![int] int -> int #1}---------------------------
+
+---merge { *![int] int int int *![int] -> int #0}---------------------------
+               begin:rdx,middle:r8,end:r9,b:rcx,a:[stk#38]
+0011 ??        mov   xmm0 = rbx                     // def/loop #2
+0012 ??        mov   rbx = [rsp+0]                  // def/empty1 #0
+0013 ??        mov   r11 = rdx                      // use/self/phi #1
+0014 ??        mov   r10 = r8                       // use/self/use #1
+0015 ??        JMP   L101
+LOOP101:
+               i:r11,j:r10,k:rdx
+0016 ??        cmp   rdx, r9
+0017 ??        j>=   L99                            // L115
+L115:
+0018 ??        cmp   r11, r8
+0019 ??        j<    L113                           // L143
+L143:
+001A ??        xor   rax,rax
+001B ??        jmp   L108
+L417:
+001C ??        JMP   L108
+L113:
+001D ??        cmp   r9, r10
+001E ??        j<=   L144                           // L111
+L111:
+001F ??        mov   rax = rbx                      // use/empty1 #0
+0020 ??        ld8   rax,[rax+r10*8+8]
+0021 ??        cmp8  rax, [rbx+r11*8+8]
+0022 ??        j<=   L109                           // L145
+L145:
+0023 ??        xor   rax,rax
+0024 ??        jmp   L108
+L416:
+0025 ??        JMP   L108
+L109:
+0026 ??        ldi   rax = #1
+0027 ??        jmp   L108
+L415:
+0028 ??        JMP   L108
+L144:
+0029 ??        ldi   rax = #1
+002A ??        JMP   L108
+L108:
+               cond:rax
+002B ??        test  rax
+002C ??        j!=   L104                           // L141
+L141:
+002D ??        ld8   rax,[rbx+r10*8+8]
+002E ??        inc   r10 += #1
+002F ??        jmp   L103
+L414:
+0030 ??        JMP   L103
+L104:
+0031 ??        ld8   rax,[rbx+r11*8+8]
+0032 ??        inc   r11 += #1
+0033 ??        JMP   L103
+L103:
+               i:r11,j:r10,$3:rax
+0034 ??        st8   [rcx+rdx*8+8],rax
+0035 ??        inc   rdx += #1
+0036 ??        jmp   LOOP101
+L413:
+0037 ??        JMP   L101
+L99:
+0038 ??        ALLME
+0039 ??        mov   rbx = xmm0                     // use/loop/use #2
+003A ??        xor   rax,rax
+003B ??        ret
+---{ *![int] int int int *![int] -> int #0}---------------------------
+
+---merge_sort { *![int] *![int] int -> int #0}---------------------------
+               a:rcx,n:r8,b:rdx
+003C ??        mov   [rsp+24] = r8                  // def/loop #1
+003D ??        mov   [rsp+16] = rcx                 // def/loop #1
+003E ??        mov   [rsp+8] = rdx                  // def/loop #0
+003F ??        mov   r9 = [rsp+8]                   // use/loop/use #0
+0040 ??        mov   rcx = [rsp+16]                 // use/loop/use #1
+0041 ??        mov   r8 = [rsp+24]                  // use/loop/use #1
+0042 ??        xor   rdx,rdx
+0043 ??        call  copy_array  rcx  rdx  r8  r9
+0044 ??        mov   r9 = [rsp+8]                   // use/loop/use #0
+0045 ??        mov   rcx = [rsp+16]                 // use/loop/use #1
+0046 ??        mov   r8 = [rsp+24]                  // use/loop/use #1
+0047 ??        xor   rdx,rdx
+0048 ??        call  split_merge  rcx  rdx  r8  r9
+                 #2    rax
+0049 ??        addi  rsp += #40
+ret
+---{ *![int] *![int] int -> int #0}---------------------------
+
+---split_merge { *![int] int int *![int] -> int #0}---------------------------
+               a:r9,b:rcx,begin:rdx,end:r8
+004A ??        mov   [rsp+40] = rdx                 // def/loop #1
+004B ??        mov   [rsp+32] = r8                  // def/loop #0
+004C ??        mov   [rsp+24] = rcx                 // def/loop #0
+004D ??        mov   [rsp+16] = r9                  // def/loop #0
+004E ??        mov   r8 = [rsp+32]                  // use/loop/use #0
+004F ??        mov   r10 = [rsp+40]                 // use/loop/use #1
+0050 ??        sub   r8 -= r10
+0051 ??        cmp   r8, #1
+0052 ??        j<=   L65                            // L69
+L69:
+0053 ??        mov   rax = [rsp+32]                 // use/loop/use #0
+0054 ??        mov   rdx = [rsp+40]                 // use/loop/use #1
+0055 ??        add   rax += rdx
+0056 ??        ldi   rcx = #2
+0057 ??        div   rax = rax / rcx                // kill rdx
+0058 ??        mov   [rsp+8] = rax                  // def/loop #0
+0059 ??        mov   rcx = [rsp+16]                 // use/loop/use #0
+005A ??        mov   r9 = [rsp+24]                  // use/loop/use #0
+005B ??        mov   r8 = [rsp+8]                   // use/loop/use #0
+005C ??        mov   rdx = [rsp+40]                 // use/loop/use #1
+005D ??        call  split_merge  rcx  rdx  r8  r9
+005E ??        mov   rcx = [rsp+16]                 // use/loop/use #0
+005F ??        mov   r9 = [rsp+24]                  // use/loop/use #0
+0060 ??        mov   r8 = [rsp+32]                  // use/loop/use #0
+0061 ??        mov   rdx = [rsp+8]                  // use/loop/use #0
+0062 ??        call  split_merge  rcx  rdx  r8  r9
+0063 ??        mov   [rsp+32] = [rsp+16]            // use/loop/use #0
+0064 ??        mov   rcx = [rsp+24]                 // use/loop/use #0
+0065 ??        mov   r9 = [rsp+32]                  // use/loop/use #0
+0066 ??        mov   r8 = [rsp+8]                   // use/loop/use #0
+0067 ??        mov   rdx = [rsp+40]                 // use/loop/use #1
+0068 ??        call  merge  rcx  rdx  r8  r9  [stk#42]
+                 #2    rax
+0069 ??        jmp   L64
+L412:
+006A ??        JMP   L64
+L65:
+006B ??        xor   rax,rax
+006C ??        JMP   L64
+L64:
+               arg:rax
+006D ??        addi  rsp += #56
+ret
+---{ *![int] int int *![int] -> int #0}---------------------------
+
+---main { -> int #0}---------------------------
+
+006E ??        mov   [rsp+0] = rbx                  // def/loop #2
+006F ??        ldi   rdx = #88
+0070 ??        alloc ldi   rcx = #1
+call  #calloc
+                 [int] rax
+0071 ??        mov   [rsp+32] = rax                 // def/loop #0
+0072 ??        mov   rdx = [rsp+32]                 // use/loop/use #0
+0073 ??        st8   [rdx+8],#10
+0074 ??        mov   rcx = [rsp+32]                 // use/loop/use #0
+0075 ??        st8   [rcx+16],#9
+0076 ??        mov   rax = [rsp+32]                 // use/loop/use #0
+0077 ??        st8   [rax+24],#8
+0078 ??        mov   r11 = [rsp+32]                 // use/loop/use #0
+0079 ??        st8   [r11+32],#7
+007A ??        mov   r10 = [rsp+32]                 // use/loop/use #0
+007B ??        st8   [r10+40],#6
+007C ??        mov   r9 = [rsp+32]                  // use/loop/use #0
+007D ??        st8   [r9+48],#5
+007E ??        mov   r8 = [rsp+32]                  // use/loop/use #0
+007F ??        st8   [r8+56],#4
+0080 ??        mov   rdx = [rsp+32]                 // use/loop/use #0
+0081 ??        st8   [rdx+64],#3
+0082 ??        mov   rcx = [rsp+32]                 // use/loop/use #0
+0083 ??        st8   [rcx+72],#2
+0084 ??        mov   rax = [rsp+32]                 // use/loop/use #0
+0085 ??        st8   [rax+80],#1
+0086 ??        ldi   rdx = #88
+0087 ??        alloc ldi   rcx = #1
+call  #calloc
+                 [int] rax
+0088 ??        mov   [rsp+24] = rax                 // def/empty1 #1
+0089 ??        ldi   rdx = #88
+008A ??        alloc ldi   rcx = #1
+call  #calloc
+                 [int] rax
+008B ??        mov   rbx = rax                      // def/empty1 #0
+008C ??        st8   [rbx+8],#1
+008D ??        st8   [rbx+16],#2
+008E ??        st8   [rbx+24],#3
+008F ??        st8   [rbx+32],#4
+0090 ??        st8   [rbx+40],#5
+0091 ??        st8   [rbx+48],#6
+0092 ??        st8   [rbx+56],#7
+0093 ??        st8   [rbx+64],#8
+0094 ??        st8   [rbx+72],#9
+0095 ??        st8   [rbx+80],#10
+0096 ??        ALLME
+0097 ??        mov   rcx = [rsp+32]                 // use/loop/use #0
+0098 ??        mov   rdx = [rsp+24]                 // use/empty1 #0
+0099 ??        ldi   r8 = #10
+009A ??        call  merge_sort  rcx  rdx  r8
+009B ??        mov   rcx = [rsp+32]                 // use/loop/use #0
+009C ??        mov   rdx = rbx                      // use/empty1 #0
+009D ??        ldi   r8 = #10
+009E ??        call  eq  rcx  rdx  r8
+                 #2    rax
+009F ??        mov   rbx = [rsp+0]                  // use/loop/use #2
+00A0 ??        addi  rsp += #40
+ret
+---{ -> int #0}---------------------------
+
+---copy_array { *![int] int int *![int] -> int #1}---------------------------
+               begin:rdx,end:r8,b:r9,a:rcx
+00A1 ??        JMP   L157
+LOOP157:
+               k:rdx
+00A2 ??        cmp   rdx, r8
+00A3 ??        j>=   L155                           // L159
+L159:
+00A4 ??        ld8   rax,[rcx+rdx*8+8]
+00A5 ??        st8   [r9+rdx*8+8],rax
+00A6 ??        inc   rdx += #1
+00A7 ??        jmp   LOOP157
+L411:
+00A8 ??        JMP   L157
+L155:
+00A9 ??        ALLME
+00AA ??        xor   rax,rax
+00AB ??        ret
+---{ *![int] int int *![int] -> int #1}---------------------------
diff --git a/seaofnodes/src/test/cases/mergsort/sort_smp.out b/seaofnodes/src/test/cases/mergsort/sort_smp.out
new file mode 100644
index 0000000..e5edda1
--- /dev/null
+++ b/seaofnodes/src/test/cases/mergsort/sort_smp.out
@@ -0,0 +1,723 @@
+START:                            [[   ]]
+  10 Start   ____ ____ ____       [[    13   61   64   86   58    9  190  121   70   93  ]]  [  Ctrl, #TOP, int]
+
+L9:                               [[  START      ]]
+   9 main    ____   10            [[    12  212  325  324  323  322  321  320  319  318  317  316  315  314  313  312  311  310  309  308  416  408   23  355  358   34  359   33  360   32  361   31  362   30  363   29  364   28  365   27  366   26  357   21  367   25  407   20  411   18  406   16  371   48   47   46   45   44   43   42   41   40   14   39   11  368  370  410    8  ]]  Ctrl
+  12 $mem       9   13            [[    11   23   23  ]]  #BOT
+ 212 $rpc       9   86            [[     2       ]]  $[ALL]
+ 358 mov        9  355            [[    34       ]]
+  34 st8        9   35  358 ____ ____  [[    33  ]]  #3:int
+ 359 mov        9  355            [[    33       ]]
+  33 st8        9   34  359 ____ ____  [[    32  ]]  #3:int
+ 360 mov        9  355            [[    32       ]]
+  32 st8        9   33  360 ____ ____  [[    31  ]]  #3:int
+ 361 mov        9  355            [[    31       ]]
+  48 st8        9   49  371 ____ ____  [[    47  ]]  #3:int
+  31 st8        9   32  361 ____ ____  [[    30  ]]  #3:int
+ 362 mov        9  355            [[    30       ]]
+  47 st8        9   48  371 ____ ____  [[    46  ]]  #3:int
+  30 st8        9   31  362 ____ ____  [[    29  ]]  #3:int
+  46 st8        9   47  371 ____ ____  [[    45  ]]  #3:int
+ 363 mov        9  355            [[    29       ]]
+  45 st8        9   46  371 ____ ____  [[    44  ]]  #3:int
+  29 st8        9   30  363 ____ ____  [[    28  ]]  #3:int
+ 364 mov        9  355            [[    28       ]]
+  44 st8        9   45  371 ____ ____  [[    43  ]]  #3:int
+  28 st8        9   29  364 ____ ____  [[    27  ]]  #3:int
+ 365 mov        9  355            [[    27       ]]
+  43 st8        9   44  371 ____ ____  [[    42  ]]  #3:int
+  27 st8        9   28  365 ____ ____  [[    26  ]]  #3:int
+ 366 mov        9  355            [[    26       ]]
+  42 st8        9   43  371 ____ ____  [[    41  ]]  #3:int
+ 367 mov        9  355            [[    25       ]]
+ 357 mov        9  355            [[    21       ]]
+  26 st8        9   27  366 ____ ____  [[    25  ]]  #3:int
+  41 st8        9   42  371 ____ ____  [[    40  ]]  #3:int
+  25 st8        9   26  367 ____ ____  [[    20  ]]  #3:int
+ 407 #88        9                 [[    20       ]]  88
+  21 st4        9   22  357 ____ ____  [[    20  ]]  #2:u32
+  40 st8        9   41  371 ____ ____  [[    39  ]]  #3:int
+  20 new_ary    9  407   21   25  [[    19   36   37  ]]  [  Ctrl, *![int], #2:u32, #3:int]
+  19 $2        20                 [[    18       ]]  #2:u32
+  36 [int]     20                 [[   411   18  ]]  *[int]
+  37 $3        20                 [[    16       ]]  #3:int
+ 408 #88        9                 [[    23       ]]  88
+  39 st8        9   40  371 ____ ____  [[    11  ]]  #3:int
+  18 st4        9   19   36 ____ ____  [[    16  ]]  #2:u32
+ 411 mov        9   36            [[   370       ]]
+ 406 #88        9                 [[    16       ]]  88
+  14 st4        9   15  371 ____ ____  [[    11  ]]  #2:u32
+ 410 #10        9                 [[     8       ]]  10
+  16 new_ary    9  406   18   37  [[    15   38   49  ]]  [  Ctrl, *![int], #2:u32, #3:int]
+  15 $2        16                 [[    14       ]]  #2:u32
+  38 [int]     16                 [[   371       ]]  *[int]
+  49 $3        16                 [[    48       ]]  #3:int
+  11 ALLMEM     9   12   14   39  [[     8       ]]  #BOT
+ 368 mov        9  355            [[     8       ]]
+ 370 mov        9  411            [[     8       ]]
+  23 new_ary    9  408   12   12  [[    22   24   35  ]]  [  Ctrl, *![int], #2:u32, #3:int]
+  22 $2        23                 [[    21       ]]  #2:u32
+  24 [int]     23                 [[   355       ]]  *[int]
+  35 $3        23                 [[    34       ]]  #3:int
+   8 call       9   11  368  370  410  [[     7  ]]  Ctrl
+ 371 mov        9   38            [[   372   39   40   41   42   43   44   45   46   47   48   14  ]]
+ 355 mov        9   24            [[   356  368  367  366  365  364  363  362  361  360  359  358  357  ]]
+ 308 rbx        9                 [[   416       ]]
+ 416 mov        9  308            [[   417       ]]
+ 310 rsi        9                 [[     2       ]]
+ 311 rdi        9                 [[     2       ]]
+ 312 r12        9                 [[     2       ]]
+ 313 r13        9                 [[     2       ]]
+ 314 r14        9                 [[     2       ]]
+ 315 r15        9                 [[     2       ]]
+ 316 xmm6       9                 [[     2       ]]
+ 317 xmm7       9                 [[     2       ]]
+ 318 xmm8       9                 [[     2       ]]
+ 319 xmm9       9                 [[     2       ]]
+ 320 xmm10      9                 [[     2       ]]
+ 321 xmm11      9                 [[     2       ]]
+ 322 xmm12      9                 [[     2       ]]
+ 323 xmm13      9                 [[     2       ]]
+ 324 xmm14      9                 [[     2       ]]
+ 325 xmm15      9                 [[     2       ]]
+ 309 rbp        9                 [[     2       ]]
+
+L70:                              [[  START      ]]
+  70 copy_ar ____   10            [[    75   77   79   82   84   85  289  288  287  286  285  284  283  282  281  280  279  278  277  276  275  274  273  272   69  ]]  Ctrl
+  75 begin     70   64            [[    74       ]]  int
+  77 end       70   64            [[    72       ]]  int
+  79 $mem      70   13            [[    78   80  ]]  #BOT
+  82 b         70   61            [[    81       ]]  *[int]
+  84 a         70   61            [[    83       ]]  *[int]
+  85 $rpc      70   86            [[    66       ]]  $[ALL]
+ 272 rbx       70                 [[    66       ]]
+ 278 r14       70                 [[    66       ]]
+ 279 r15       70                 [[    66       ]]
+ 280 xmm6      70                 [[    66       ]]
+ 281 xmm7      70                 [[    66       ]]
+ 282 xmm8      70                 [[    66       ]]
+ 283 xmm9      70                 [[    66       ]]
+ 284 xmm10     70                 [[    66       ]]
+ 285 xmm11     70                 [[    66       ]]
+ 286 xmm12     70                 [[    66       ]]
+ 287 xmm13     70                 [[    66       ]]
+ 288 xmm14     70                 [[    66       ]]
+ 289 xmm15     70                 [[    66       ]]
+ 277 r13       70                 [[    66       ]]
+ 276 r12       70                 [[    66       ]]
+ 275 rdi       70                 [[    66       ]]
+ 274 rsi       70                 [[    66       ]]
+ 273 rbp       70                 [[    66       ]]
+
+L121:                             [[  START      ]]
+ 121 merge   ____   10            [[   138  141  144  151  154  158  173  235  234  233  232  231  230  229  228  227  226  225  224  223  222  221  220  219  218  413  326  376  381  120  ]]  Ctrl
+ 138 begin    121   64            [[   376  155  ]]  int
+ 141 middle   121   64            [[   135  381  ]]  int
+ 144 end      121   64            [[   142  170  ]]  int
+ 151 $mem     121   13            [[   150  172  ]]  #BOT
+ 154 b        121   61            [[   153  159  ]]  *[int]
+ 158 a        121   61            [[   326       ]]  *[int]
+ 173 $rpc     121   86            [[   117       ]]  $[ALL]
+ 381 mov      121  141            [[   145       ]]
+ 376 mov      121  138            [[   137       ]]
+ 326 mov      121  158            [[   327  148  161  157  ]]
+ 218 rbx      121                 [[   413       ]]
+ 413 mov      121  218            [[   414       ]]
+ 221 rdi      121                 [[   117       ]]
+ 226 xmm6     121                 [[   117       ]]
+ 227 xmm7     121                 [[   117       ]]
+ 228 xmm8     121                 [[   117       ]]
+ 229 xmm9     121                 [[   117       ]]
+ 230 xmm10    121                 [[   117       ]]
+ 231 xmm11    121                 [[   117       ]]
+ 232 xmm12    121                 [[   117       ]]
+ 233 xmm13    121                 [[   117       ]]
+ 234 xmm14    121                 [[   117       ]]
+ 235 xmm15    121                 [[   117       ]]
+ 220 rsi      121                 [[   117       ]]
+ 219 rbp      121                 [[   117       ]]
+ 224 r14      121                 [[   117       ]]
+ 225 r15      121                 [[   117       ]]
+ 223 r13      121                 [[   117       ]]
+ 222 r12      121                 [[   117       ]]
+
+L190:                             [[  START      ]]
+ 190 eq      ____   10            [[   196  197  201  204  208  271  270  269  268  267  266  265  264  263  262  261  260  259  258  257  256  255  254  398  189  ]]  Ctrl
+ 196 $mem     190   13            [[   194  200  206  206  ]]  #BOT
+ 197 a        190   61            [[   194       ]]  *[int]
+ 201 b        190   61            [[   200       ]]  *[int]
+ 204 n        190   64            [[   202       ]]  int
+ 208 $rpc     190   86            [[   185       ]]  $[ALL]
+ 398 #0       190                 [[   198       ]]  0
+ 258 r12      190                 [[   185       ]]
+ 259 r13      190                 [[   185       ]]
+ 260 r14      190                 [[   185       ]]
+ 261 r15      190                 [[   185       ]]
+ 262 xmm6     190                 [[   185       ]]
+ 263 xmm7     190                 [[   185       ]]
+ 264 xmm8     190                 [[   185       ]]
+ 265 xmm9     190                 [[   185       ]]
+ 266 xmm10    190                 [[   185       ]]
+ 267 xmm11    190                 [[   185       ]]
+ 268 xmm12    190                 [[   185       ]]
+ 269 xmm13    190                 [[   185       ]]
+ 270 xmm14    190                 [[   185       ]]
+ 271 xmm15    190                 [[   185       ]]
+ 257 rdi      190                 [[   185       ]]
+ 256 rsi      190                 [[   185       ]]
+ 255 rbp      190                 [[   185       ]]
+ 254 rbx      190                 [[   185       ]]
+
+L58:                              [[  START      ]]
+  58 merge_s ____   10            [[    59   60   63   65  182  307  306  305  304  303  302  301  300  299  298  297  296  295  294  293  292  291  290  403  400  349  351  353  402  405   57  ]]  Ctrl
+  59 $mem      58   13            [[    57       ]]  #BOT
+  60 a         58   61            [[   400       ]]  *[int]
+  63 n         58   64            [[   403       ]]  int
+  65 b         58   61            [[   349       ]]  *[int]
+ 182 $rpc      58   86            [[    51       ]]  $[ALL]
+ 405 mov       58  403            [[    57       ]]
+ 402 mov       58  400            [[    57       ]]
+ 353 #0        58                 [[    57       ]]  0
+ 351 mov       58  349            [[    57       ]]
+  57 call      58   59  402  353  405  351  [[    56  ]]  Ctrl
+ 349 mov       58   65            [[   350  351  ]]
+ 400 mov       58   60            [[   401  402  ]]
+ 403 mov       58   63            [[   404  405  ]]
+ 291 rbp       58                 [[    51       ]]
+ 295 r13       58                 [[    51       ]]
+ 296 r14       58                 [[    51       ]]
+ 297 r15       58                 [[    51       ]]
+ 298 xmm6      58                 [[    51       ]]
+ 299 xmm7      58                 [[    51       ]]
+ 300 xmm8      58                 [[    51       ]]
+ 301 xmm9      58                 [[    51       ]]
+ 302 xmm10     58                 [[    51       ]]
+ 303 xmm11     58                 [[    51       ]]
+ 304 xmm12     58                 [[    51       ]]
+ 305 xmm13     58                 [[    51       ]]
+ 306 xmm14     58                 [[    51       ]]
+ 307 xmm15     58                 [[    51       ]]
+ 290 rbx       58                 [[    51       ]]
+ 294 r12       58                 [[    51       ]]
+ 293 rdi       58                 [[    51       ]]
+ 292 rsi       58                 [[    51       ]]
+
+L93:                              [[  START      ]]
+  93 split_m ____   10            [[    99  101  102  103  106  177  253  252  251  250  249  248  247  246  245  244  243  242  241  240  239  238  237  236  391  336  332  328  342  395  110  108   92  ]]  Ctrl
+  99 $mem      93   13            [[    97  175  ]]  #BOT
+ 101 a         93   61            [[   328       ]]  *[int]
+ 102 b         93   61            [[   332       ]]  *[int]
+ 103 begin     93   64            [[   391       ]]  int
+ 106 end       93   64            [[   336       ]]  int
+ 177 $rpc      93   86            [[    89       ]]  $[ALL]
+ 395 mov       93  391            [[   110       ]]
+ 332 mov       93  102            [[   335  333  334  ]]
+ 336 mov       93  106            [[   342  340  337  339  ]]
+ 391 mov       93  103            [[   395  392  394  393  ]]
+ 342 mov       93  336            [[   110       ]]
+ 328 mov       93  101            [[   329  331  330  ]]
+ 110 sub       93  342  395       [[   108       ]]  int
+ 108 cmp       93  110            [[    92       ]]  bool
+  92 j<=       93  108            [[    91   98  ]]  [  Ctrl, Ctrl]
+ 241 r13       93                 [[    89       ]]
+ 242 r14       93                 [[    89       ]]
+ 243 r15       93                 [[    89       ]]
+ 244 xmm6      93                 [[    89       ]]
+ 245 xmm7      93                 [[    89       ]]
+ 246 xmm8      93                 [[    89       ]]
+ 247 xmm9      93                 [[    89       ]]
+ 248 xmm10     93                 [[    89       ]]
+ 249 xmm11     93                 [[    89       ]]
+ 250 xmm12     93                 [[    89       ]]
+ 251 xmm13     93                 [[    89       ]]
+ 252 xmm14     93                 [[    89       ]]
+ 253 xmm15     93                 [[    89       ]]
+ 238 rsi       93                 [[    89       ]]
+ 237 rbp       93                 [[    89       ]]
+ 236 rbx       93                 [[    89       ]]
+ 239 rdi       93                 [[    89       ]]
+ 240 r12       93                 [[    89       ]]
+
+LOOP120:                          [[  L121      L122       ]]
+ 120 Loop    ____  121  420       [[   137  145  150  155  170  119  ]]  Ctrl
+ 137 Phi_i    120  376  139       [[   148  135  140  139  157  ]]  int
+ 145 Phi_j    120  381  146       [[   162  142  146  147  161  ]]  int
+ 150 Phi_$3   120  151  152       [[   148  153  157  159  161  162  172  ]]  #BOT
+ 155 Phi_k    120  138  156       [[   170  153  156  159  ]]  int
+ 170 cmp      120  155  144       [[   119       ]]  bool
+ 119 j>=      120  170            [[   118  134  ]]  [  Ctrl, Ctrl]
+
+LOOP189:                          [[  L190      L191       ]]
+ 189 Loop    ____  190  419       [[   198  202  188  ]]  Ctrl
+ 198 Phi_i    189  398  199       [[   202  194  199  200  ]]  int
+ 202 cmp      189  198  204       [[   188       ]]  bool
+ 188 j>=      189  202            [[   187  193  ]]  [  Ctrl, Ctrl]
+
+LOOP69:                           [[  L70       L71        ]]
+  69 Loop    ____   70  425       [[    74   80   72   68  ]]  Ctrl
+  74 Phi_k     69   75   76       [[    72   76   81   83  ]]  int
+  80 Phi_$3    69   79   81       [[    78   81   83  ]]  #BOT
+  72 cmp       69   74   77       [[    68       ]]  bool
+  68 j>=       69   72            [[    67   71  ]]  [  Ctrl, Ctrl]
+
+L7:                               [[  L9         ]]
+   7 CallEnd    8                 [[     6  184  ]]  [  Ctrl, #BOT, 0]
+ 184 $mem       7                 [[     5       ]]  #BOT
+
+L98:                              [[  L93        ]]
+  98 False     92                 [[   339  394  105  107  104  345  330  335  348  393   97  ]]  Ctrl
+ 330 mov       98  328            [[    97       ]]
+ 335 mov       98  332            [[    97       ]]
+ 393 mov       98  391            [[    97       ]]
+ 348 mov       98  345            [[    97       ]]
+  97 call      98   99  330  393  348  335  [[    96  ]]  Ctrl
+ 339 mov       98  336            [[   105       ]]
+ 394 mov       98  391            [[   105       ]]
+ 105 add       98  339  394       [[   104       ]]  int
+ 107 #2        98                 [[   104       ]]  2
+ 104 div       98  105  107       [[   345       ]]  int
+ 345 mov       98  104            [[   348  347  346  ]]
+
+L56:                              [[  L58        ]]
+  56 CallEnd   57                 [[    55   88  ]]  [  Ctrl, #BOT, 0]
+  88 $mem      56                 [[    54       ]]  #BOT
+
+L91:                              [[  L93        ]]
+  91 True      92                 [[    90       ]]  Ctrl
+
+L6:                               [[  L7         ]]
+   6 $ctrl      7                 [[   356  372  409    5  ]]  Ctrl
+ 356 mov        6  355            [[     5       ]]
+ 372 mov        6  371            [[     5       ]]
+ 409 #10        6                 [[     5       ]]  10
+   5 call       6  184  356  372  409  [[     4  ]]  Ctrl
+
+L55:                              [[  L56        ]]
+  55 $ctrl     56                 [[   350  354  401  404   54  ]]  Ctrl
+ 350 mov       55  349            [[    54       ]]
+ 354 #0        55                 [[    54       ]]  0
+ 404 mov       55  403            [[    54       ]]
+ 401 mov       55  400            [[    54       ]]
+  54 call      55   88  401  354  404  350  [[    53  ]]  Ctrl
+
+L118:                             [[  LOOP120    ]]
+ 118 True     119                 [[   172  414  415  117  ]]  Ctrl
+ 172 ALLMEM   118  151 ____  150  [[   117       ]]  #BOT
+ 414 mov      118  413            [[   117       ]]
+ 415 #0       118                 [[   117       ]]  0
+ 117 Return   118  172  415  173  414  219  220  221  222  223  224  225  226  227  228  229  230  231  232  233  234  235  [[     1  ]]  [  Ctrl, #BOT, 0]
+
+L134:                             [[  LOOP120    ]]
+ 134 False    119                 [[   135  133  ]]  Ctrl
+ 135 cmp      134  137  141       [[   133       ]]  bool
+ 133 j<       134  135            [[   132  163  ]]  [  Ctrl, Ctrl]
+
+L187:                             [[  LOOP189    ]]
+ 187 True     188                 [[   169  186  ]]  Ctrl
+ 169 #1       187                 [[   207       ]]  1
+
+L193:                             [[  LOOP189    ]]
+ 193 False    188                 [[   200  194  192  ]]  Ctrl
+ 200 ld8      193  196  201  198  [[   194       ]]  int
+ 194 cmp8     193  196  197  198  200  [[   192  ]]  bool
+ 192 j!=      193  194            [[   191  205  ]]  [  Ctrl, Ctrl]
+
+L67:                              [[  LOOP69     ]]
+  67 True      68                 [[    78   62   66  ]]  Ctrl
+  78 ALLMEM    67   79 ____   80  [[    66       ]]  #BOT
+  62 #0        67                 [[    66       ]]  0
+  66 Return    67   78   62   85  272  273  274  275  276  277  278  279  280  281  282  283  284  285  286  287  288  289  [[     1  ]]  [  Ctrl, #BOT, 0]
+
+L71:                              [[  LOOP69     ]]
+  71 False     68                 [[    83   81   76  425  ]]  Ctrl
+  76 inc       71   74            [[    74       ]]  int
+  83 ld8       71   80   84   74  [[    81       ]]  int
+  81 st8       71   80   82   74   83  [[    80  ]]  #3:Bot
+
+L96:                              [[  L98        ]]
+  96 CallEnd   97                 [[    95  100  ]]  [  Ctrl, #BOT, 0]
+ 100 $mem      96                 [[    94       ]]  #BOT
+
+L4:                               [[  L6         ]]
+   4 CallEnd    5                 [[     3  210  211  ]]  [  Ctrl, #BOT, bool]
+ 210 $mem       4                 [[     2       ]]  #BOT
+ 211 #2         4                 [[     2       ]]  bool
+
+L95:                              [[  L96        ]]
+  95 $ctrl     96                 [[   331  334  340  347   94  ]]  Ctrl
+ 331 mov       95  328            [[    94       ]]
+ 334 mov       95  332            [[    94       ]]
+ 347 mov       95  345            [[    94       ]]
+ 340 mov       95  336            [[    94       ]]
+  94 call      95  100  331  347  340  334  [[   115  ]]  Ctrl
+
+L132:                             [[  L134       ]]
+ 132 True     133                 [[   142  131  ]]  Ctrl
+ 142 cmp      132  144  145       [[   131       ]]  bool
+ 131 j<=      132  142            [[   130  164  ]]  [  Ctrl, Ctrl]
+
+L53:                              [[  L55        ]]
+  53 CallEnd   54                 [[    52  181  ]]  [  Ctrl, #BOT, 0]
+ 181 $mem      53                 [[    51       ]]  #BOT
+
+L163:                             [[  L134       ]]
+ 163 False    133                 [[   389  424  ]]  Ctrl
+ 389 #0       163                 [[   168       ]]  0
+ 424 jmp      163                 [[   127       ]]
+
+L191:                             [[  L193       ]]
+ 191 False    192                 [[   199  419  ]]  Ctrl
+ 199 inc      191  198            [[   198       ]]  int
+
+L205:                             [[  L193       ]]
+ 205 True     192                 [[   399  418  ]]  Ctrl
+ 399 #0       205                 [[   207       ]]  0
+ 418 jmp      205                 [[   186       ]]
+
+L3:                               [[  L4         ]]
+   3 $ctrl      4                 [[   417    2  ]]  Ctrl
+ 417 mov        3  416            [[     2       ]]
+   2 Return     3  210  211  212  417  309  310  311  312  313  314  315  316  317  318  319  320  321  322  323  324  325  [[     1  ]]  [  Ctrl, #BOT, bool]
+
+L186:                             [[  L187      L205       ]]
+ 186 Region  ____  187  418       [[   207  206  185  ]]  Ctrl
+ 207 Phi_res  186  169  399       [[   185       ]]  bool
+ 206 ALLMEM   186  196 ____  196  [[   185       ]]  #BOT
+ 185 Return   186  206  207  208  254  255  256  257  258  259  260  261  262  263  264  265  266  267  268  269  270  271  [[     1  ]]  [  Ctrl, #BOT, bool]
+
+L52:                              [[  L53        ]]
+  52 $ctrl     53                 [[   352   51  ]]  Ctrl
+ 352 #0        52                 [[    51       ]]  0
+  51 Return    52  181  352  182  290  291  292  293  294  295  296  297  298  299  300  301  302  303  304  305  306  307  [[     1  ]]  [  Ctrl, #BOT, 0]
+
+L115:                             [[  L95        ]]
+ 115 CallEnd   94                 [[   114  116  ]]  [  Ctrl, #BOT, 0]
+ 116 $mem     115                 [[   113       ]]  #BOT
+
+L130:                             [[  L132       ]]
+ 130 False    131                 [[   327  162  148  129  ]]  Ctrl
+ 327 mov      130  326            [[   162       ]]
+ 162 ld8      130  150  327  145  [[   148       ]]  int
+ 148 cmp8     130  150  326  137  162  [[   129  ]]  bool
+ 129 j<=      130  148            [[   128  165  ]]  [  Ctrl, Ctrl]
+
+L164:                             [[  L132       ]]
+ 164 True     131                 [[   388  127  ]]  Ctrl
+ 388 #1       164                 [[   168       ]]  1
+
+L114:                             [[  L115       ]]
+ 114 $ctrl    115                 [[   329  333  337  346  392  113  ]]  Ctrl
+ 329 mov      114  328            [[   113       ]]
+ 333 mov      114  332            [[   113       ]]
+ 392 mov      114  391            [[   113       ]]
+ 346 mov      114  345            [[   113       ]]
+ 337 mov      114  336            [[   113       ]]
+ 113 call     114  116  333  392  346  337  329  [[   112  ]]  Ctrl
+
+L128:                             [[  L130       ]]
+ 128 True     129                 [[   387  422  ]]  Ctrl
+ 387 #1       128                 [[   168       ]]  1
+ 422 jmp      128                 [[   127       ]]
+
+L165:                             [[  L130       ]]
+ 165 False    129                 [[   390  423  ]]  Ctrl
+ 390 #0       165                 [[   168       ]]  0
+ 423 jmp      165                 [[   127       ]]
+
+L112:                             [[  L114       ]]
+ 112 CallEnd  113                 [[   111  176  ]]  [  Ctrl, #BOT, 0]
+ 176 $mem     112                 [[   175       ]]  #BOT
+
+L127:                             [[  L128      L163      L164      L165       ]]
+ 127 Region  ____  422  424  164  423  [[   168  166  126  ]]  Ctrl
+ 168 Phi_con  127  387  389  388  390  [[   166  ]]  bool
+ 166 test     127  168            [[   126       ]]
+ 126 j!=      127  166            [[   123  160  ]]  [  Ctrl, Ctrl]
+
+L111:                             [[  L112       ]]
+ 111 $ctrl    112                 [[    90       ]]  Ctrl
+
+L90:                              [[  L91       L111       ]]
+  90 Region  ____   91  111       [[   175  215   89  ]]  Ctrl
+ 175 Phi_$me   90   99  176       [[    89       ]]  #BOT
+ 215 #0        90                 [[    89       ]]  0
+  89 Return    90  175  215  177  236  237  238  239  240  241  242  243  244  245  246  247  248  249  250  251  252  253  [[     1  ]]  [  Ctrl, #BOT, 0]
+
+L160:                             [[  L127       ]]
+ 160 False    126                 [[   161  159  147  421  ]]  Ctrl
+ 147 inc      160  145            [[   146       ]]  int
+ 161 ld8      160  150  326  145  [[   159       ]]  int
+ 421 jmp      160                 [[   122       ]]
+ 159 st8      160  150  154  155  161  [[   152  ]]  #3:Bot
+
+L123:                             [[  L127       ]]
+ 123 True     126                 [[   157  153  140  122  ]]  Ctrl
+ 140 inc      123  137            [[   139       ]]  int
+ 157 ld8      123  150  326  137  [[   153       ]]  int
+ 153 st8      123  150  154  155  157  [[   152  ]]  #3:Bot
+
+L122:                             [[  L123      L160       ]]
+ 122 Region  ____  123  421       [[   139  146  152  156  420  ]]  Ctrl
+ 139 Phi_i    122  140  137       [[   137       ]]  int
+ 146 Phi_j    122  145  147       [[   145       ]]  int
+ 152 Phi_$3   122  153  159       [[   150       ]]  #3:Bot
+ 156 inc      122  155            [[   155       ]]  int
+
+L1:                               [[  L118      L67       L186       ]]
+   1 Stop       2   51   89  117   66  185  [[   ]]  Bot
+
+
+
+---split_merge { *![int] int int *![int] -> 0 #0}---------------------------
+0000 4883EC38    subi  rsp -= #56
+                 a:r9,b:rcx,begin:rdx,end:r8
+0004 4889542428  mov   [rsp+40] = rdx                 // def/loop #1
+0009 4C89442458  mov   [rsp+88] = r8                  // def/loop #0
+000E 48894C2450  mov   [rsp+80] = rcx                 // def/loop #0
+0013 4C894C2448  mov   [rsp+72] = r9                  // def/loop #0
+0018 4C8B442458  mov   r8 = [rsp+88]                  // use/loop/use #0
+001D 4C8B542428  mov   r10 = [rsp+40]                 // use/loop/use #1
+0022 4D2BC2      sub   r8 -= r10
+0025 4983F801    cmp   r8, #1
+0029 7E6F        j<=   L90                            // L98
+L98:
+002B 488B442458  mov   rax = [rsp+88]                 // use/loop/use #0
+0030 488B542428  mov   rdx = [rsp+40]                 // use/loop/use #1
+0035 4803C2      add   rax += rdx
+0038 B902000000  ldi   rcx = #2
+003D 489948F7F9  div   rax = rax / rcx                // kill rdx
+0042 4889442440  mov   [rsp+64] = rax                 // def/loop #0
+0047 488B4C2448  mov   rcx = [rsp+72]                 // use/loop/use #0
+004C 4C8B4C2450  mov   r9 = [rsp+80]                  // use/loop/use #0
+0051 4C8B442440  mov   r8 = [rsp+64]                  // use/loop/use #0
+0056 488B542428  mov   rdx = [rsp+40]                 // use/loop/use #1
+005B E8A0FFFFFF  call  split_merge  rcx  rdx  r8  r9
+0060 488B4C2448  mov   rcx = [rsp+72]                 // use/loop/use #0
+0065 4C8B4C2450  mov   r9 = [rsp+80]                  // use/loop/use #0
+006A 4C8B442458  mov   r8 = [rsp+88]                  // use/loop/use #0
+006F 488B542440  mov   rdx = [rsp+64]                 // use/loop/use #0
+0074 E887FFFFFF  call  split_merge  rcx  rdx  r8  r9
+0079 FF7424488F  mov   [rsp+32] = [rsp+72]            // use/loop/use #0
+007E 442420
+0081 488B4C2450  mov   rcx = [rsp+80]                 // use/loop/use #0
+0086 4C8B4C2458  mov   r9 = [rsp+88]                  // use/loop/use #0
+008B 4C8B442440  mov   r8 = [rsp+64]                  // use/loop/use #0
+0090 488B542428  mov   rdx = [rsp+40]                 // use/loop/use #1
+0095 E836000000  call  merge  rcx  rdx  r8  r9  [stk#42]
+L91:
+L90:
+009A 33C0        xor   rax,rax
+009C 4883C438C3  addi  rsp += #56
+00A1             ret
+---{ *![int] int int *![int] -> 0 #0}---------------------------
+
+---copy_array { *![int] int int *![int] -> 0 #1}---------------------------
+                 begin:rdx,end:r8,b:r9,a:rcx
+LOOP69:
+                 k:rdx
+00B0 493BD0      cmp   rdx, r8
+00B3 7D10        j>=   L67                            // L71
+L71:
+00B5 488B44D108  ld8   rax,[rcx+rdx*8+8]
+00BA 498944D108  st8   [r9+rdx*8+8],rax
+00BF 4883C201    inc   rdx += #1
+00C3 EBEB        jmp   LOOP69
+L425:
+L67:
+00C5 33C0        xor   rax,rax
+00C7 C3          ret
+---{ *![int] int int *![int] -> 0 #1}---------------------------
+
+---merge { *![int] int int int *![int] -> 0 #0}---------------------------
+                 begin:rdx,middle:r8,end:r9,b:rcx,a:[rsp+40]
+00D0 66480F6EC3  mov   xmm0 = rbx                     // def/loop #2
+00D5 488B5C2428  mov   rbx = [rsp+40]                 // def/empty1 #0
+00DA 4C8BDA      mov   r11 = rdx                      // use/self/phi #1
+00DD 4D8BD0      mov   r10 = r8                       // use/self/use #1
+LOOP120:
+                 i:r11,j:r10,k:rdx
+00E0 493BD1      cmp   rdx, r9
+00E3 7D57        j>=   L118                           // L134
+L134:
+00E5 4D3BD8      cmp   r11, r8
+00E8 7C04        j<    L132                           // L163
+L163:
+00EA 33C0        xor   rax,rax
+00EC EB24        jmp   L127
+L424:
+L132:
+00EE 4D3BCA      cmp   r9, r10
+00F1 7E1A        j<=   L164                           // L130
+L130:
+00F3 488BC3      mov   rax = rbx                      // use/empty1 #0
+00F6 4A8B44D008  ld8   rax,[rax+r10*8+8]
+00FB 4A3944DB08  cmp8  rax, [rbx+r11*8+8]
+0100 7E04        j<=   L128                           // L165
+L165:
+0102 33C0        xor   rax,rax
+0104 EB0C        jmp   L127
+L423:
+L128:
+0106 B801000000  ldi   rax = #1
+010B EB05        jmp   L127
+L422:
+L164:
+010D B801000000  ldi   rax = #1
+L127:
+                 cond:rax
+0112 4883F800    test  rax
+0116 7510        j!=   L123                           // L160
+L160:
+0118 4A8B44D308  ld8   rax,[rbx+r10*8+8]
+011D 488944D108  st8   [rcx+rdx*8+8],rax
+0122 4983C201    inc   r10 += #1
+0126 EB0E        jmp   L122
+L421:
+L123:
+0128 4A8B44DB08  ld8   rax,[rbx+r11*8+8]
+012D 488944D108  st8   [rcx+rdx*8+8],rax
+0132 4983C301    inc   r11 += #1
+L122:
+                 i:r11,j:r10
+0136 4883C201    inc   rdx += #1
+013A EBA4        jmp   LOOP120
+L420:
+L118:
+013C 66480F7EC3  mov   rbx = xmm0                     // use/loop/use #2
+0141 33C0        xor   rax,rax
+0143 C3          ret
+---{ *![int] int int int *![int] -> 0 #0}---------------------------
+
+---eq { *![int] *![int] int -> bool #1}---------------------------
+                 a:rcx,b:rdx,n:r8
+0150 33C0        xor   rax,rax
+LOOP189:
+                 i:rax
+0152 493BC0      cmp   rax, r8
+0155 7D16        j>=   L187                           // L193
+L193:
+0157 4C8B4CC208  ld8   r9,[rdx+rax*8+8]
+015C 4C394CC108  cmp8  r9, [rcx+rax*8+8]
+0161 7506        j!=   L205                           // L191
+L191:
+0163 4883C001    inc   rax += #1
+0167 EBE9        jmp   LOOP189
+L419:
+L205:
+0169 33C0        xor   rax,rax
+016B EB05        jmp   L186
+L418:
+L187:
+016D B801000000  ldi   rax = #1
+L186:
+                 result:rax
+0172 C3          ret
+---{ *![int] *![int] int -> bool #1}---------------------------
+
+---main { -> bool #0}---------------------------
+0180 4883EC28    subi  rsp -= #40
+0184 48895C2420  mov   [rsp+32] = rbx                 // def/loop #2
+0189 BA58000000  ldi   rdx = #88
+018E B901000000  alloc ldi   rcx = #1
+0193 E800000000  call  #calloc
+0198 4889442418  mov   [rsp+24] = rax                 // def/loop #0
+019D 4C8B4C2418  mov   r9 = [rsp+24]                  // use/loop/use #0
+01A2 49C741080A  st8   [r9+8],#10
+01A7 000000
+01AA 4C8B442418  mov   r8 = [rsp+24]                  // use/loop/use #0
+01AF 49C7401009  st8   [r8+16],#9
+01B4 000000
+01B7 488B542418  mov   rdx = [rsp+24]                 // use/loop/use #0
+01BC 48C7421808  st8   [rdx+24],#8
+01C1 000000
+01C4 488B4C2418  mov   rcx = [rsp+24]                 // use/loop/use #0
+01C9 48C7412007  st8   [rcx+32],#7
+01CE 000000
+01D1 488B442418  mov   rax = [rsp+24]                 // use/loop/use #0
+01D6 48C7402806  st8   [rax+40],#6
+01DB 000000
+01DE 4C8B5C2418  mov   r11 = [rsp+24]                 // use/loop/use #0
+01E3 49C7433005  st8   [r11+48],#5
+01E8 000000
+01EB 4C8B542418  mov   r10 = [rsp+24]                 // use/loop/use #0
+01F0 49C7423804  st8   [r10+56],#4
+01F5 000000
+01F8 4C8B4C2418  mov   r9 = [rsp+24]                  // use/loop/use #0
+01FD 49C7414003  st8   [r9+64],#3
+0202 000000
+0205 4C8B442418  mov   r8 = [rsp+24]                  // use/loop/use #0
+020A 49C7404802  st8   [r8+72],#2
+020F 000000
+0212 488B542418  mov   rdx = [rsp+24]                 // use/loop/use #0
+0217 C7020A0000  st4   [rdx],#10
+021C 00
+021D 488B4C2418  mov   rcx = [rsp+24]                 // use/loop/use #0
+0222 48C7415001  st8   [rcx+80],#1
+0227 000000
+022A BA58000000  ldi   rdx = #88
+022F B901000000  alloc ldi   rcx = #1
+0234 E800000000  call  #calloc
+0239 4889442410  mov   [rsp+16] = rax                 // def/empty1 #1
+023E C7000A0000  st4   [rax],#10
+0243 00
+0244 BA58000000  ldi   rdx = #88
+0249 B901000000  alloc ldi   rcx = #1
+024E E800000000  call  #calloc
+0253 488BD8      mov   rbx = rax                      // def/empty1 #0
+0256 48C7430801  st8   [rbx+8],#1
+025B 000000
+025E 48C7431002  st8   [rbx+16],#2
+0263 000000
+0266 48C7431803  st8   [rbx+24],#3
+026B 000000
+026E 48C7432004  st8   [rbx+32],#4
+0273 000000
+0276 48C7432805  st8   [rbx+40],#5
+027B 000000
+027E 48C7433006  st8   [rbx+48],#6
+0283 000000
+0286 48C7433807  st8   [rbx+56],#7
+028B 000000
+028E 48C7434008  st8   [rbx+64],#8
+0293 000000
+0296 48C7434809  st8   [rbx+72],#9
+029B 000000
+029E C7030A0000  st4   [rbx],#10
+02A3 00
+02A4 48C743500A  st8   [rbx+80],#10
+02A9 000000
+02AC 488B4C2418  mov   rcx = [rsp+24]                 // use/loop/use #0
+02B1 488B542410  mov   rdx = [rsp+16]                 // use/empty1 #0
+02B6 41B80A0000  ldi   r8 = #10
+02BB 00
+02BC E81F000000  call  merge_sort  rcx  rdx  r8
+02C1 488B4C2418  mov   rcx = [rsp+24]                 // use/loop/use #0
+02C6 488BD3      mov   rdx = rbx                      // use/empty1 #0
+02C9 41B80A0000  ldi   r8 = #10
+02CE 00
+02CF E87CFEFFFF  call  eq  rcx  rdx  r8
+02D4 488B5C2420  mov   rbx = [rsp+32]                 // use/loop/use #2
+02D9 4883C428C3  addi  rsp += #40
+02DE             ret
+---{ -> bool #0}---------------------------
+
+---merge_sort { *![int] *![int] int -> 0 #0}---------------------------
+02E0 4883EC28    subi  rsp -= #40
+                 a:rcx,n:r8,b:rdx
+02E4 4C89442440  mov   [rsp+64] = r8                  // def/loop #1
+02E9 48894C2438  mov   [rsp+56] = rcx                 // def/loop #1
+02EE 4889542430  mov   [rsp+48] = rdx                 // def/loop #0
+02F3 4C8B4C2430  mov   r9 = [rsp+48]                  // use/loop/use #0
+02F8 33D2        xor   rdx,rdx
+02FA 488B4C2438  mov   rcx = [rsp+56]                 // use/loop/use #1
+02FF 4C8B442440  mov   r8 = [rsp+64]                  // use/loop/use #1
+0304 E8A7FDFFFF  call  copy_array  rcx  rdx  r8  r9
+0309 4C8B4C2430  mov   r9 = [rsp+48]                  // use/loop/use #0
+030E 33D2        xor   rdx,rdx
+0310 488B4C2438  mov   rcx = [rsp+56]                 // use/loop/use #1
+0315 4C8B442440  mov   r8 = [rsp+64]                  // use/loop/use #1
+031A E8E1FCFFFF  call  split_merge  rcx  rdx  r8  r9
+031F 33C0        xor   rax,rax
+0321 4883C428C3  addi  rsp += #40
+0326             ret
+---{ *![int] *![int] int -> 0 #0}---------------------------

From b07a059e96f9ef38bfa1246d7bd4a266ecac3b4b Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Wed, 30 Apr 2025 23:21:09 +0100
Subject: [PATCH 069/119] Apply fixes from Simple project

---
 .../ezlang/compiler/IterPeeps.java            |   2 +
 .../ezlang/compiler/SB.java                   |  23 +-
 .../ezlang/compiler/print/ASMPrinter.java     | 222 ++++-
 .../ezlang/compiler/print/IRPrinter.java      |   7 +-
 .../compiler/sontypes/SONTypeFunPtr.java      |   3 +-
 .../src/test/cases/mergsort/sort_ez.out       | 910 +++++-------------
 .../src/test/cases/mergsort/sort_smp.out      | 470 ---------
 7 files changed, 419 insertions(+), 1218 deletions(-)

diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/IterPeeps.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/IterPeeps.java
index f58ae9f..c18aa90 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/IterPeeps.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/IterPeeps.java
@@ -49,6 +49,7 @@ public class IterPeeps {
 
     public IterPeeps( long seed ) { _work = new WorkList<>(seed); }
 
+    @SuppressWarnings("unchecked")
     public <N extends Node> N add( N n ) { return (N)_work.push(n); }
 
     public void addAll( Ary<Node> ary ) { _work.addAll(ary); }
@@ -126,6 +127,7 @@ private boolean progressOnList(CodeGen code) {
      * Classic WorkList, with a fast add/remove, dup removal, random pull.
      * The Node's nid is used to check membership in the worklist.
      */
+    @SuppressWarnings("unchecked")
     public static class WorkList<E extends Node> {
 
         private Node[] _es;
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/SB.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/SB.java
index fe7f97d..0ed652d 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/SB.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/SB.java
@@ -29,21 +29,28 @@ else if( Double.isInfinite(s) ) {
   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));
-    }
+  // 1 byte, 2 hex digits, 8 bits
+  public SB hex1(int s) {
+    int digit = (s>>4) & 0xf;
+    _sb.append((char)((digit <= 9 ? '0' : ('A'-10))+digit));
+    digit = s & 0xf;
+    _sb.append((char)((digit <= 9 ? '0' : ('A'-10))+digit));
     return this;
   }
+  // 2 bytes, 4 hex digits, 16 bits, Big Endian
+  public SB hex2(int s) { return hex1(s>> 8).hex1(s); }
+  // 4 bytes, 8 hex digits, 32 bits, Big Endian
+  public SB hex4(int s) { return hex2(s>>16).hex2(s); }
+  // 8 bytes, 16 hex digits, 64 bits, Big Endian
+    public SB hex8(long s) { return hex4((int)(s>>32)).hex4((int)s); }
+
   // Fixed width field
   public SB fix( int sz, String s ) {
     for( int i=0; i<sz; i++ )
       _sb.append( i < s.length() ? s.charAt(i) : ' ');
     return this;
   }
+  public char at(int idx ) { return _sb.charAt(idx); }
 
   // Not spelled "p" on purpose: too easy to accidentally say "p(1.0)" and
   // suddenly call the autoboxed version.
@@ -83,6 +90,8 @@ public boolean was_nl() {
   //
   public SB unchar() { return unchar(1); }
   public SB unchar(int x) { _sb.setLength(_sb.length()-x); return this; }
+  public SB setLen(int len) { _sb.setLength(len); return this; }
+  public String subString(int start, int end ) { return _sb.substring(start,end); }
 
   public SB clear() { _sb.setLength(0); return this; }
   public int len() { return _sb.length(); }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/print/ASMPrinter.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/print/ASMPrinter.java
index f620244..1a93013 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/print/ASMPrinter.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/print/ASMPrinter.java
@@ -6,6 +6,8 @@
 import com.compilerprogramming.ezlang.compiler.nodes.*;
 import com.compilerprogramming.ezlang.compiler.sontypes.*;
 
+import java.util.HashSet;
+
 public abstract class ASMPrinter {
 
     public static SB print(SB sb, CodeGen code) {
@@ -17,17 +19,70 @@ public static SB print(SB sb, CodeGen code) {
         for( int i=0; i<code._cfg._len; i++ )
             if( code._cfg.at(i) instanceof FunNode fun )
                 iadr = print(iadr,sb,code,fun,i);
+
+        // Skip padding
+        while( ((iadr+7) & -8) > iadr )
+            iadr++;
+
+        // constant pool
+        Encoding enc = code._encoding;
+        if(  enc!=null && !enc._bigCons.isEmpty() ) {
+            iadr = (iadr+15)&-16; // pad to 16
+            HashSet<SONType> targets = new HashSet<>();
+            sb.p("--- Constant Pool ------").nl();
+            // By log size
+            for( int log = 3; log >= 0; log-- ) {
+                for( Node op : enc._bigCons.keySet() ) {
+                    Encoding.Relo relo = enc._bigCons.get(op);
+                    if( targets.contains(relo._t) ) continue;
+                    targets.add(relo._t);
+                    if( relo._t.log_size()==log ) {
+                        sb.hex2(iadr).p("  ");
+                        if( relo._t instanceof SONTypeTuple tt ) {
+                            for( SONType tx : tt._types ) {
+                                switch( log ) {
+                                case 0: sb.hex1(enc.read1(iadr)); break;
+                                case 1: sb.hex2(enc.read2(iadr)); break;
+                                case 2: sb.hex4(enc.read4(iadr)); break;
+                                case 3: sb.hex8(enc.read8(iadr)); break;
+                                }
+                                iadr += (1<<log);
+                                sb.p(" ");
+                            }
+                        } else {
+                            switch( log ) {
+                            case 0: sb.hex1(enc.read1(iadr)).fix(9-1,""); break;
+                            case 1: sb.hex2(enc.read2(iadr)).fix(9-2,""); break;
+                            case 2: sb.hex4(enc.read4(iadr)).fix(9-4,""); break;
+                            case 3: sb.hex8(enc.read8(iadr)).p(" "); break;
+                            }
+                            iadr += (1<<log);
+                        }
+                        relo._t.print(sb).nl();
+                    }
+                }
+            }
+        }
+
+
         return sb;
     }
 
     private static int print(int iadr, SB sb, CodeGen code, FunNode fun, int cfgidx) {
+        FunNode old=null;
+        if( code._encoding!=null ) {
+            old = code._encoding._fun;
+            code._encoding._fun = fun; // Useful printing after RA
+        }
         // Function header
         sb.nl().p("---");
         if( fun._name != null ) sb.p(fun._name).p(" ");
         fun.sig().print(sb);
         sb.p("---------------------------").nl();
+        iadr = (iadr + 15)&-16; // All function entries padded to 16 align
 
-
+        if( fun._frameAdjust != 0 )
+            iadr = doInst(iadr,sb,code,fun,cfgidx,fun,true,true);
         while( !(code._cfg.at(cfgidx) instanceof ReturnNode) )
             iadr = doBlock(iadr,sb,code,fun,cfgidx++);
 
@@ -35,69 +90,74 @@ private static int print(int iadr, SB sb, CodeGen code, FunNode fun, int cfgidx)
         sb.p("---");
         fun.sig().print(sb);
         sb.p("---------------------------").nl();
+        if( code._encoding != null )
+            code._encoding._fun = old;
         return iadr;
     }
 
-    static private final int encWidth = 8;
     static private final int opWidth = 5;
     static private final int argWidth = 30;
     static int doBlock(int iadr, SB sb, CodeGen code, FunNode fun, int cfgidx) {
+        final int encWidth = code._mach==null ? 2 : code._mach.defaultOpSize()*2;
         CFGNode bb = code._cfg.at(cfgidx);
         if( bb != fun && !(bb instanceof IfNode) && !(bb instanceof CallEndNode) && !(bb instanceof CallNode)  && !(bb instanceof CProjNode && bb.in(0) instanceof CallEndNode ))
-            sb.p(bb instanceof LoopNode ? "LOOP" : "L").p(bb._nid).p(":").nl();
+            sb.p(label(bb)).p(":").nl();
         if( bb instanceof CallNode ) return iadr;
         final boolean postAlloc = code._phase.ordinal() > CodeGen.Phase.RegAlloc.ordinal();
+        final boolean postEncode= code._phase.ordinal() >=CodeGen.Phase.Encoding.ordinal();
 
-        // Count Phis
-        int nPhi=0;
-        for( ; nPhi<bb.nOuts(); nPhi++ )
-            if( !(bb.out(nPhi) instanceof PhiNode) )
-                break;
-
-        if( nPhi>0 ) {
-            // Post-alloc phi prints all on one line
+        boolean once=false;
+        for( Node n : bb.outs() ) {
+            if( !(n instanceof PhiNode phi) ) continue;
+            if( phi._type instanceof SONTypeMem || phi._type instanceof SONTypeRPC ) continue; // Nothing for the hidden ones
+            // Post-RegAlloc phi prints all on one line
             if( postAlloc ) {
-                sb.fix(4," ").p(" ").fix(encWidth,"").p("  ");
-                for( int i=0; i<nPhi; i++ ) {
-                    PhiNode phi = (PhiNode)bb.out(i);
-                    if( !(phi._type instanceof SONTypeMem || phi._type instanceof SONTypeRPC) ) // Nothing for the hidden ones
-                        sb.p(phi._label).p(':').p(code.reg(phi)).p(',');
-                }
-                sb.unchar().nl();
-
+                if( !once ) { once=true; sb.fix(4," ").p(" ").fix(encWidth,"").p("  "); }
+                sb.p(phi._label).p(':').p(code.reg(phi,fun)).p(',');
             } else {
-                for( int j=0; j<nPhi; j++ ) {
-                    PhiNode phi = (PhiNode)bb.out(j);
-                    if( phi._type instanceof SONTypeMem || phi._type instanceof SONTypeRPC ) continue; // Nothing for the hidden ones
-                    sb.fix(4," ").p(" ").fix(encWidth,"").p("  ").fix(opWidth,phi._label).p(" ");
-                    sb.p(code.reg(phi));
-                    if( !(phi instanceof ParmNode) ) {
-                        sb.p(" = phi( ");
-                        for( int i=1; i<phi.nIns(); i++ )
-                            sb.p("N").p(phi.in(i)._nid).p(",");
-                        sb.unchar().p(" )");
-                    }
-                    sb.nl();
+                // Pre-RegAlloc phi prints one line per
+                sb.fix(4," ").p(" ").fix(encWidth,"").p("  ").fix(opWidth,phi._label).p(" ").p(code.reg(phi,fun));
+                if( phi.getClass() == PhiNode.class ) {
+                    sb.p(" = phi( ");
+                    for( int i=1; i<phi.nIns(); i++ )
+                        sb.p("N").p(phi.in(i)._nid).p(",");
+                    sb.unchar().p(" )");
                 }
+                sb.nl();
             }
         }
+        if( once ) sb.unchar().nl();
 
         // All the non-phis
-        for( int i=nPhi; i<bb.nOuts(); i++ )
-            iadr = doInst(iadr, sb,code,fun,bb,bb.out(i),postAlloc );
+        for( int i=0; i<bb.nOuts(); i++ )
+            if( !(bb.out(i) instanceof PhiNode) )
+                iadr = doInst(iadr, sb,code, fun, cfgidx, bb.out(i),postAlloc, postEncode );
 
         return iadr;
     }
 
-    static int doInst(int iadr, SB sb, CodeGen code, FunNode fun, CFGNode bb, Node n, boolean postAlloc ) {
-        if( n instanceof CProjNode ) return iadr;
-        if( n instanceof CalleeSaveNode && postAlloc ) return iadr;
+    static int doInst( int iadr, SB sb, CodeGen code, FunNode fun, int cfgidx, Node n, boolean postAlloc, boolean postEncode ) {
+        if( n==null || n instanceof CProjNode ) return iadr;
+        if( postAlloc && n instanceof CalleeSaveNode ) return iadr;
+        if( postEncode && n instanceof ProjNode ) return iadr;
+        if( n instanceof MemMergeNode ) return iadr;
+        if( n.getClass() == ConstantNode.class ) return iadr; // Default placeholders
+        final int dopz = code._mach==null ? 2 : code._mach.defaultOpSize();
+        final int encWidth = dopz*2;
 
         // All blocks ending in a Region will need to either fall into or jump
         // to this block.  Until the post-reg-alloc block layout cleanup, we
         // need to assume a jump.  There's no real hardware op here, yet.
-        if( n instanceof RegionNode && !(n instanceof FunNode) ) {
-            sb.hex4(iadr++).p(" ").fix(encWidth,"??").p("  ").fix(opWidth,"JMP").p(" ").fix(argWidth,"L"+n._nid).nl();
+        if( n instanceof RegionNode cfg && !(n instanceof FunNode) ) {
+            if( postEncode ) return iadr; // All jumps inserted already
+            while( cfgidx < code._cfg._len-1 ) {
+                CFGNode next = code._cfg.at(++cfgidx);
+                if( next == n ) return iadr; // Fall-through, no branch
+                if( next.nOuts()>1 )
+                    break;      // Has code in the block, need to jump around
+                // No code in the block, can fall through it
+            }
+            sb.hex2(iadr++).p(" ").fix(encWidth,"??").p("  ").fix(opWidth,"JMP").p(" ").fix(argWidth,label(cfg)).nl();
             return iadr;
         }
 
@@ -105,30 +165,44 @@ static int doInst(int iadr, SB sb, CodeGen code, FunNode fun, CFGNode bb, Node n
         // get indent slightly and just print their index & node#
         if( n instanceof ProjNode proj ) {
             if( proj._type instanceof SONTypeMem ) return iadr; // Nothing for the hidden ones
-            sb.fix(4," ").p(" ").fix(encWidth,"").p("    ").fix(opWidth,proj._label==null ? "---" : proj._label).p(" ").p(code.reg(n)).nl();
+            sb.fix(4," ").p(" ").fix(encWidth,"").p("    ").fix(opWidth,proj._label==null ? "---" : proj._label).p(" ").p(code.reg(n,fun)).nl();
             return iadr;
         }
 
         // ADDR ENCODING  Op--- dst = src op src       // Comment
         // 1234 abcdefgh  ld4   RAX = [Rbase + off]    // Comment
-        sb.hex4(iadr);
-        sb.p(" ");
+        sb.hex2(iadr).p(" ");
 
         // Encoding
-        int size = 1;           // TODO: Fake encoding size
-        iadr += size;
-        sb.fix(encWidth,"??");
+        int fatEncoding = 0;
+        if( code._encoding != null ) {
+            int size = code._encoding._opLen[n._nid];
+            if( code._asmLittle )
+                for( int i=0; i<Math.min(size,dopz); i++ )
+                    sb.hex1(code._encoding._bits.buf()[iadr++]);
+            else {
+                iadr += Math.min(size,dopz);
+                for( int i=0; i<Math.min(size,dopz); i++ )
+                    sb.hex1(code._encoding._bits.buf()[iadr-i-1]);
+            }
+            for( int i=size*2; i<encWidth; i++ )
+                sb.p(" ");
+            fatEncoding = size - (encWidth>>1); // Not-printed parts of encoding
+        } else
+            sb.fix(encWidth,"");
         sb.p("  ");
 
         // Op; generally "ld4" or "call"
-        sb.fix(opWidth, n instanceof MachNode mach ? mach.op() : n.label());
-        sb.p(" ");
+        sb.fix(opWidth, n instanceof MachNode mach ? mach.op() : n.label()).p(" ");
 
         // General asm args
+        String isMultiOp = null;
         if( n instanceof MachNode mach ) {
             int old = sb.len();
             mach.asm(code,sb);
-            sb.fix(argWidth-(sb.len()-old),""); // Pad out
+            int len = sb.len();
+            isMultiOp = isMultiOp(sb,old,len);
+            sb.fix(argWidth-(len-old),""); // Pad out
 
         } else if( !(n._type instanceof SONTypeMem) ) {
             // Room for some inputs
@@ -149,14 +223,64 @@ static int doInst(int iadr, SB sb, CodeGen code, FunNode fun, CFGNode bb, Node n
 
         sb.nl();
 
+        // Printing more op bits than fit
+        if( isMultiOp != null && code._encoding != null ) {
+            // Multiple ops, template style, no RA, no scheduling.  Print out
+            // one-line-per-newline, with encoding bits up front.
+            int size = code._encoding._opLen[n._nid];
+            int off = Math.min(size,dopz);
+            while( isMultiOp!=null ) {
+                sb.hex2(iadr).p(" ");
+                int len = Math.min(size-off,dopz);
+                for( int i=0; i<len; i++ )
+                    sb.hex1(code._encoding._bits.buf()[iadr++]);
+                off += len;
+                for( int i=len; i<dopz; i++ ) sb.p("  ");
+                sb.p("  ");
+                int x = isMultiOp.indexOf('\n');
+                if( x== -1 ) {  // Last line
+                    sb.p(isMultiOp).nl();
+                    isMultiOp = null;
+                } else {
+                    sb.p(isMultiOp.substring(0,x+1)); // Includes the newline
+                    isMultiOp = isMultiOp.substring(x+1);
+                }
+
+            }
+
+        } else if( fatEncoding > 0 ) {
+            // Extra bytes past the default encoding width, all put on a line by
+            // themselves.  X86 special for super long encodings
+            sb.hex2(iadr).p(" ");
+            for( int i=0; i<fatEncoding; i++ )
+                sb.hex1(code._encoding._bits.buf()[iadr++]);
+            sb.nl();
+        }
+
+
         // MultiNodes are immediately followed by projection(s)
         if( !(n instanceof CFGNode) && n instanceof MultiNode ) {
             for( Node proj : n._outputs ) {
-                assert proj instanceof ProjNode;
-                doInst(iadr,sb,code,fun,bb,proj,postAlloc);
+                if( proj instanceof ProjNode ) // it could also be an ante-dep
+                    doInst(iadr,sb,code,fun, cfgidx, proj,postAlloc,postEncode);
             }
         }
 
         return iadr;
     }
+
+    private static String label( CFGNode bb ) {
+        return (bb instanceof LoopNode ? "LOOP" : "L")+bb._nid;
+    }
+
+    private static String isMultiOp(SB sb, int old, int len) {
+        for( int i=old; i<len; i++ )
+            if( sb.at(i)=='\n' ) {
+                String s = sb.subString(i+1,len);
+                sb.setLen(i);
+                return s;
+            }
+        return null;
+    }
+
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/print/IRPrinter.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/print/IRPrinter.java
index a69581d..0d40c70 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/print/IRPrinter.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/print/IRPrinter.java
@@ -15,6 +15,7 @@ public abstract class IRPrinter {
     // NNID NNAME DDEF DDEF  [[  UUSE UUSE  ]]  TYPE
     // 1234 sssss 1234 1234 1234 1234 1234 1234 tttttt
     public static SB printLine( Node n, SB sb ) {
+        if( n==null ) return sb;
         sb.p("%4d %-7.7s ".formatted(n._nid,n.label()));
         if( n._inputs==null )
             return sb.p("DEAD\n");
@@ -210,7 +211,8 @@ public static String _prettyPrint( CodeGen code ) {
                 printLine(n,sb);
                 if( !(n instanceof CFGNode) && n instanceof MultiNode )
                     for( Node use : n._outputs )
-                        printLine(use,sb);
+                        if( use instanceof ProjNode )
+                            printLine(use,sb);
             }
         }
 
@@ -280,7 +282,8 @@ else if( blk instanceof RegionNode || blk instanceof StopNode )
                         printLine( n, sb, bns, i--, ds,ns );
                         if( n instanceof MultiNode && !(n instanceof CFGNode) ) {
                             for( Node use : n._outputs ) {
-                                printLine(use,sb,bns,bns.indexOf(use),ds,ns);
+                                if( use instanceof ProjNode )
+                                    printLine(use,sb,bns,bns.indexOf(use),ds,ns);
                             }
                         }
                     }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeFunPtr.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeFunPtr.java
index 07b06df..484c1d7 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeFunPtr.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeFunPtr.java
@@ -35,8 +35,7 @@ public SONTypeFunPtr(byte nil, SONTypeTuple sig, SONType ret, long fidxs) {
 
     public static SONTypeFunPtr make(byte nil, SONTypeTuple sig, SONType ret, long fidxs ) { return new SONTypeFunPtr(nil,sig,ret,fidxs).intern(); }
     public static SONTypeFunPtr make(boolean nil, SONTypeTuple sig, SONType ret ) { return make((byte)(nil ? 3 : 2),sig,ret,-1); }
-    @Override
-    SONTypeFunPtr makeFrom(byte nil ) { return  nil ==_nil   ? this : make(  nil,_sig,_ret,   _fidxs); }
+    @Override SONTypeFunPtr makeFrom(byte nil ) { return  nil ==_nil   ? this : make(  nil,_sig,_ret,   _fidxs); }
     public SONTypeFunPtr makeFrom(SONType ret ) { return     ret ==_ret   ? this : make( _nil,_sig, ret,   _fidxs); }
     public SONTypeFunPtr makeFrom(int fidx ) { return make((byte)2, _sig,_ret,1L<<fidx ); }
 
diff --git a/seaofnodes/src/test/cases/mergsort/sort_ez.out b/seaofnodes/src/test/cases/mergsort/sort_ez.out
index 946e758..b4cc2a5 100644
--- a/seaofnodes/src/test/cases/mergsort/sort_ez.out
+++ b/seaofnodes/src/test/cases/mergsort/sort_ez.out
@@ -1,716 +1,250 @@
-START:                            [[   ]]
-  10 Start   ____ ____ ____       [[    15   52   55   61  158   12   67    9  102  179  ]]  [  Ctrl, #TOP, int]
-
-L9:                               [[  START      ]]
-   9 merge_s ____   10            [[    50   51   54   56   60  321  320  319  318  317  316  315  314  313  312  311  310  309  308  307  306  305  304  401  398  362  364  400  403  405    8  ]]  Ctrl
-  50 $mem       9   15            [[     8       ]]  #BOT
-  51 a          9   52            [[   398       ]]  *[int]
-  54 n          9   55            [[   401       ]]  int
-  56 b          9   52            [[   362       ]]  *[int]
-  60 $rpc       9   61            [[     2       ]]  $[ALL]
- 405 #0         9                 [[     8       ]]  0
- 403 mov        9  401            [[     8       ]]
- 400 mov        9  398            [[     8       ]]
- 364 mov        9  362            [[     8       ]]
-   8 call       9   50  400  405  403  364  [[     7  ]]  Ctrl
- 362 mov        9   56            [[   363  364  ]]
- 398 mov        9   51            [[   399  400  ]]
- 401 mov        9   54            [[   402  403  ]]
- 305 rbp        9                 [[     2       ]]
- 309 r13        9                 [[     2       ]]
- 310 r14        9                 [[     2       ]]
- 311 r15        9                 [[     2       ]]
- 312 xmm6       9                 [[     2       ]]
- 313 xmm7       9                 [[     2       ]]
- 314 xmm8       9                 [[     2       ]]
- 315 xmm9       9                 [[     2       ]]
- 316 xmm10      9                 [[     2       ]]
- 317 xmm11      9                 [[     2       ]]
- 318 xmm12      9                 [[     2       ]]
- 319 xmm13      9                 [[     2       ]]
- 320 xmm14      9                 [[     2       ]]
- 321 xmm15      9                 [[     2       ]]
- 304 rbx        9                 [[     2       ]]
- 308 r12        9                 [[     2       ]]
- 307 rdi        9                 [[     2       ]]
- 306 rsi        9                 [[     2       ]]
-
-L12:                              [[  START      ]]
-  12 main    ____   10            [[    14  208  303  302  301  300  299  298  297  296  295  294  293  292  291  290  289  288  287  286  409  394   22  345  347   32  348   31  349   30  350   29  351   28  352   27  353   26  354   25  355   24  356   23  393   20  397  392   17  360   45   44   43   42   41   40   39   38   37   36   13  357  359  396   11  ]]  Ctrl
-  14 $mem      12   15            [[    13   22   22  ]]  #BOT
- 208 $rpc      12   61            [[   203       ]]  $[ALL]
- 347 mov       12  345            [[    32       ]]
-  32 st8       12   33  347 ____ ____  [[    31  ]]  #3:int
- 348 mov       12  345            [[    31       ]]
-  31 st8       12   32  348 ____ ____  [[    30  ]]  #3:int
- 349 mov       12  345            [[    30       ]]
-  30 st8       12   31  349 ____ ____  [[    29  ]]  #3:int
- 350 mov       12  345            [[    29       ]]
-  45 st8       12   46  360 ____ ____  [[    44  ]]  #3:int
-  29 st8       12   30  350 ____ ____  [[    28  ]]  #3:int
- 351 mov       12  345            [[    28       ]]
-  44 st8       12   45  360 ____ ____  [[    43  ]]  #3:int
-  28 st8       12   29  351 ____ ____  [[    27  ]]  #3:int
-  43 st8       12   44  360 ____ ____  [[    42  ]]  #3:int
- 352 mov       12  345            [[    27       ]]
-  42 st8       12   43  360 ____ ____  [[    41  ]]  #3:int
-  27 st8       12   28  352 ____ ____  [[    26  ]]  #3:int
- 353 mov       12  345            [[    26       ]]
-  41 st8       12   42  360 ____ ____  [[    40  ]]  #3:int
-  26 st8       12   27  353 ____ ____  [[    25  ]]  #3:int
- 354 mov       12  345            [[    25       ]]
-  40 st8       12   41  360 ____ ____  [[    39  ]]  #3:int
-  25 st8       12   26  354 ____ ____  [[    24  ]]  #3:int
- 355 mov       12  345            [[    24       ]]
-  39 st8       12   40  360 ____ ____  [[    38  ]]  #3:int
-  24 st8       12   25  355 ____ ____  [[    23  ]]  #3:int
- 356 mov       12  345            [[    23       ]]
-  38 st8       12   39  360 ____ ____  [[    37  ]]  #3:int
-  23 st8       12   24  356 ____ ____  [[    20  ]]  #3:int
- 393 #88       12                 [[    20       ]]  88
-  37 st8       12   38  360 ____ ____  [[    36  ]]  #3:int
-  20 new_ary   12  393   21   23  [[    19   35   48  ]]  [  Ctrl, *![int], #2:u32, #3:int]
-  19 $2        20                 [[    17       ]]  #2:u32
-  35 $3        20                 [[    17       ]]  #3:int
-  48 [int]     20                 [[   397       ]]  *[int]
- 394 #88       12                 [[    22       ]]  88
-  36 st8       12   37  360 ____ ____  [[    13  ]]  #3:int
- 392 #88       12                 [[    17       ]]  88
- 397 mov       12   48            [[   359       ]]
- 396 #10       12                 [[    11       ]]  10
-  17 new_ary   12  392   19   35  [[    16   46   47  ]]  [  Ctrl, *![int], #2:u32, #3:int]
-  16 $2        17                 [[    13       ]]  #2:u32
-  46 $3        17                 [[    45       ]]  #3:int
-  47 [int]     17                 [[   360       ]]  *[int]
-  13 ALLMEM    12   14   16   36  [[    11       ]]  #BOT
- 357 mov       12  345            [[    11       ]]
- 359 mov       12  397            [[    11       ]]
-  22 new_ary   12  394   14   14  [[    21   33   34  ]]  [  Ctrl, *![int], #2:u32, #3:int]
-  21 $2        22                 [[    20       ]]  #2:u32
-  33 $3        22                 [[    32       ]]  #3:int
-  34 [int]     22                 [[   345       ]]  *[int]
-  11 call      12   13  357  359  396  [[   182  ]]  Ctrl
- 345 mov       12   34            [[   346  357  356  355  354  353  352  351  350  349  348  347  ]]
- 360 mov       12   47            [[   361   36   37   38   39   40   41   42   43   44   45  ]]
- 286 rbx       12                 [[   409       ]]
- 409 mov       12  286            [[   410       ]]
- 288 rsi       12                 [[   203       ]]
- 289 rdi       12                 [[   203       ]]
- 290 r12       12                 [[   203       ]]
- 291 r13       12                 [[   203       ]]
- 292 r14       12                 [[   203       ]]
- 293 r15       12                 [[   203       ]]
- 294 xmm6      12                 [[   203       ]]
- 295 xmm7      12                 [[   203       ]]
- 296 xmm8      12                 [[   203       ]]
- 297 xmm9      12                 [[   203       ]]
- 298 xmm10     12                 [[   203       ]]
- 299 xmm11     12                 [[   203       ]]
- 300 xmm12     12                 [[   203       ]]
- 301 xmm13     12                 [[   203       ]]
- 302 xmm14     12                 [[   203       ]]
- 303 xmm15     12                 [[   203       ]]
- 287 rbp       12                 [[   203       ]]
-
-L179:                             [[  START      ]]
- 179 eq      ____   10            [[   189  190  194  197  201  285  284  283  282  281  280  279  278  277  276  275  274  273  272  271  270  269  268  390  178  ]]  Ctrl
- 189 $mem     179   15            [[   187  193  199  199  ]]  #BOT
- 190 a        179   52            [[   187       ]]  *[int]
- 194 b        179   52            [[   193       ]]  *[int]
- 197 n        179   55            [[   195       ]]  int
- 201 $rpc     179   61            [[   174       ]]  $[ALL]
- 390 #0       179                 [[   191       ]]  0
- 272 r12      179                 [[   174       ]]
- 273 r13      179                 [[   174       ]]
- 274 r14      179                 [[   174       ]]
- 275 r15      179                 [[   174       ]]
- 276 xmm6     179                 [[   174       ]]
- 277 xmm7     179                 [[   174       ]]
- 278 xmm8     179                 [[   174       ]]
- 279 xmm9     179                 [[   174       ]]
- 280 xmm10    179                 [[   174       ]]
- 281 xmm11    179                 [[   174       ]]
- 282 xmm12    179                 [[   174       ]]
- 283 xmm13    179                 [[   174       ]]
- 284 xmm14    179                 [[   174       ]]
- 285 xmm15    179                 [[   174       ]]
- 271 rdi      179                 [[   174       ]]
- 270 rsi      179                 [[   174       ]]
- 269 rbp      179                 [[   174       ]]
- 268 rbx      179                 [[   174       ]]
-
-L158:                             [[  START      ]]
- 158 copy_ar ____   10            [[   163  165  167  170  172  173  249  248  247  246  245  244  243  242  241  240  239  238  237  236  235  234  233  232  157  ]]  Ctrl
- 163 begin    158   55            [[   162       ]]  int
- 165 end      158   55            [[   160       ]]  int
- 167 $mem     158   15            [[   166  168  ]]  #BOT
- 170 b        158   52            [[   169       ]]  *[int]
- 172 a        158   52            [[   171       ]]  *[int]
- 173 $rpc     158   61            [[   154       ]]  $[ALL]
- 232 rbx      158                 [[   154       ]]
- 238 r14      158                 [[   154       ]]
- 239 r15      158                 [[   154       ]]
- 240 xmm6     158                 [[   154       ]]
- 241 xmm7     158                 [[   154       ]]
- 242 xmm8     158                 [[   154       ]]
- 243 xmm9     158                 [[   154       ]]
- 244 xmm10    158                 [[   154       ]]
- 245 xmm11    158                 [[   154       ]]
- 246 xmm12    158                 [[   154       ]]
- 247 xmm13    158                 [[   154       ]]
- 248 xmm14    158                 [[   154       ]]
- 249 xmm15    158                 [[   154       ]]
- 237 r13      158                 [[   154       ]]
- 236 r12      158                 [[   154       ]]
- 235 rdi      158                 [[   154       ]]
- 234 rsi      158                 [[   154       ]]
- 233 rbp      158                 [[   154       ]]
-
-L67:                              [[  START      ]]
-  67 split_m ____   10            [[    70   73   74   75   78   95  267  266  265  264  263  262  261  260  259  258  257  256  255  254  253  252  251  250  383  332  328  324  338  387   84   82   66  ]]  Ctrl
-  70 $mem      67   15            [[    68   91  ]]  #BOT
-  73 a         67   52            [[   324       ]]  *[int]
-  74 b         67   52            [[   328       ]]  *[int]
-  75 begin     67   55            [[   383       ]]  int
-  78 end       67   55            [[   332       ]]  int
-  95 $rpc      67   61            [[    63       ]]  $[ALL]
- 387 mov       67  383            [[    84       ]]
- 328 mov       67   74            [[   331  330  329  ]]
- 332 mov       67   78            [[   338  336  333  335  ]]
- 383 mov       67   75            [[   387  386  384  385  ]]
- 338 mov       67  332            [[    84       ]]
- 324 mov       67   73            [[   327  325  326  ]]
-  84 sub       67  338  387       [[    82       ]]  int
-  82 cmp       67   84            [[    66       ]]  bool
-  66 j<=       67   82            [[    65   69  ]]  [  Ctrl, Ctrl]
- 255 r13       67                 [[    63       ]]
- 256 r14       67                 [[    63       ]]
- 257 r15       67                 [[    63       ]]
- 258 xmm6      67                 [[    63       ]]
- 259 xmm7      67                 [[    63       ]]
- 260 xmm8      67                 [[    63       ]]
- 261 xmm9      67                 [[    63       ]]
- 262 xmm10     67                 [[    63       ]]
- 263 xmm11     67                 [[    63       ]]
- 264 xmm12     67                 [[    63       ]]
- 265 xmm13     67                 [[    63       ]]
- 266 xmm14     67                 [[    63       ]]
- 267 xmm15     67                 [[    63       ]]
- 252 rsi       67                 [[    63       ]]
- 251 rbp       67                 [[    63       ]]
- 250 rbx       67                 [[    63       ]]
- 253 rdi       67                 [[    63       ]]
- 254 r12       67                 [[    63       ]]
-
-L102:                             [[  START      ]]
- 102 merge   ____   10            [[   119  122  125  132  134  139  153  231  230  229  228  227  226  225  224  223  222  221  220  219  218  217  216  215  214  406  322  368  373  101  ]]  Ctrl
- 119 begin    102   55            [[   368  135  ]]  int
- 122 middle   102   55            [[   116  373  ]]  int
- 125 end      102   55            [[   123  150  ]]  int
- 132 $mem     102   15            [[   131  152  ]]  #BOT
- 134 b        102   52            [[   133       ]]  *[int]
- 139 a        102   52            [[   322       ]]  *[int]
- 153 $rpc     102   61            [[    98       ]]  $[ALL]
- 373 mov      102  122            [[   126       ]]
- 368 mov      102  119            [[   118       ]]
- 322 mov      102  139            [[   323  129  140  138  ]]
- 214 rbx      102                 [[   406       ]]
- 406 mov      102  214            [[   407       ]]
- 217 rdi      102                 [[    98       ]]
- 222 xmm6     102                 [[    98       ]]
- 223 xmm7     102                 [[    98       ]]
- 224 xmm8     102                 [[    98       ]]
- 225 xmm9     102                 [[    98       ]]
- 226 xmm10    102                 [[    98       ]]
- 227 xmm11    102                 [[    98       ]]
- 228 xmm12    102                 [[    98       ]]
- 229 xmm13    102                 [[    98       ]]
- 230 xmm14    102                 [[    98       ]]
- 231 xmm15    102                 [[    98       ]]
- 216 rsi      102                 [[    98       ]]
- 215 rbp      102                 [[    98       ]]
- 220 r14      102                 [[    98       ]]
- 221 r15      102                 [[    98       ]]
- 219 r13      102                 [[    98       ]]
- 218 r12      102                 [[    98       ]]
-
-LOOP178:                          [[  L179      L184       ]]
- 178 Loop    ____  179  419       [[   191  195  177  ]]  Ctrl
- 191 Phi_i    178  390  192       [[   195  187  192  193  ]]  int
- 195 cmp      178  191  197       [[   177       ]]  bool
- 177 j>=      178  195            [[   176  186  ]]  [  Ctrl, Ctrl]
-
-LOOP157:                          [[  L158      L159       ]]
- 157 Loop    ____  158  411       [[   162  168  160  156  ]]  Ctrl
- 162 Phi_k    157  163  164       [[   160  164  169  171  ]]  int
- 168 Phi_$3   157  167  169       [[   166  169  171  ]]  #BOT
- 160 cmp      157  162  165       [[   156       ]]  bool
- 156 j>=      157  160            [[   155  159  ]]  [  Ctrl, Ctrl]
-
-LOOP101:                          [[  L102      L103       ]]
- 101 Loop    ____  102  413       [[   118  126  131  135  150  100  ]]  Ctrl
- 118 Phi_i    101  368  120       [[   129  116  121  120  138  ]]  int
- 126 Phi_j    101  373  127       [[   142  123  127  128  140  ]]  int
- 131 Phi_$3   101  132  133       [[   129  133  138  140  142  152  ]]  #BOT
- 135 Phi_k    101  119  136       [[   150  133  136  ]]  int
- 150 cmp      101  135  125       [[   100       ]]  bool
- 100 j>=      101  150            [[    99  115  ]]  [  Ctrl, Ctrl]
-
-L7:                               [[  L9         ]]
-   7 CallEnd    8                 [[     6   57  ]]  [  Ctrl, #BOT, int]
-  57 $mem       7                 [[     5       ]]  #BOT
-
-L182:                             [[  L12        ]]
- 182 CallEnd   11                 [[   181  183  ]]  [  Ctrl, #BOT, int]
- 183 $mem     182                 [[   180       ]]  #BOT
-
-L65:                              [[  L67        ]]
-  65 True      66                 [[    53   64  ]]  Ctrl
-  53 #0        65                 [[    93       ]]  0
-
-L69:                              [[  L67        ]]
-  69 False     66                 [[   335  386   77   79   76  341  326  331  344  385   68  ]]  Ctrl
- 326 mov       69  324            [[    68       ]]
- 331 mov       69  328            [[    68       ]]
- 385 mov       69  383            [[    68       ]]
- 344 mov       69  341            [[    68       ]]
-  68 call      69   70  326  385  344  331  [[    72  ]]  Ctrl
- 335 mov       69  332            [[    77       ]]
- 386 mov       69  383            [[    77       ]]
-  77 add       69  335  386       [[    76       ]]  int
-  79 #2        69                 [[    76       ]]  2
-  76 div       69   77   79       [[   341       ]]  int
- 341 mov       69   76            [[   344  342  343  ]]
-
-L6:                               [[  L7         ]]
-   6 $ctrl      7                 [[   363  399  402  404    5  ]]  Ctrl
- 363 mov        6  362            [[     5       ]]
- 399 mov        6  398            [[     5       ]]
- 404 #0         6                 [[     5       ]]  0
- 402 mov        6  401            [[     5       ]]
-   5 call       6   57  399  404  402  363  [[     4  ]]  Ctrl
-
-L186:                             [[  LOOP178    ]]
- 186 False    177                 [[   193  187  185  ]]  Ctrl
- 193 ld8      186  189  194  191  [[   187       ]]  int
- 187 cmp8     186  189  190  191  193  [[   185  ]]  bool
- 185 j!=      186  187            [[   184  198  ]]  [  Ctrl, Ctrl]
-
-L176:                             [[  LOOP178    ]]
- 176 True     177                 [[   149  175  ]]  Ctrl
- 149 #1       176                 [[   200       ]]  1
-
-L181:                             [[  L182       ]]
- 181 $ctrl    182                 [[   346  361  395  180  ]]  Ctrl
- 346 mov      181  345            [[   180       ]]
- 361 mov      181  360            [[   180       ]]
- 395 #10      181                 [[   180       ]]  10
- 180 call     181  183  346  361  395  [[   205  ]]  Ctrl
-
-L159:                             [[  LOOP157    ]]
- 159 False    156                 [[   171  169  164  411  ]]  Ctrl
- 164 inc      159  162            [[   162       ]]  int
- 171 ld8      159  168  172  162  [[   169       ]]  int
- 169 st8      159  168  170  162  171  [[   168  ]]  #3:Bot
-
-L115:                             [[  LOOP101    ]]
- 115 False    100                 [[   116  114  ]]  Ctrl
- 116 cmp      115  118  122       [[   114       ]]  bool
- 114 j<       115  116            [[   113  143  ]]  [  Ctrl, Ctrl]
-
-L99:                              [[  LOOP101    ]]
-  99 True     100                 [[   152  407  408   98  ]]  Ctrl
- 152 ALLMEM    99  132 ____  131  [[    98       ]]  #BOT
- 407 mov       99  406            [[    98       ]]
- 408 #0        99                 [[    98       ]]  0
-  98 Return    99  152  408  153  407  215  216  217  218  219  220  221  222  223  224  225  226  227  228  229  230  231  [[     1  ]]  [  Ctrl, #BOT, 0]
-
-L155:                             [[  LOOP157    ]]
- 155 True     156                 [[   166  212  154  ]]  Ctrl
- 166 ALLMEM   155  167 ____  168  [[   154       ]]  #BOT
- 212 #0       155                 [[   154       ]]  0
- 154 Return   155  166  212  173  232  233  234  235  236  237  238  239  240  241  242  243  244  245  246  247  248  249  [[     1  ]]  [  Ctrl, #BOT, 0]
-
-L4:                               [[  L6         ]]
-   4 CallEnd    5                 [[     3   58   59  ]]  [  Ctrl, #BOT, int]
-  58 $mem       4                 [[     2       ]]  #BOT
-  59 #2         4                 [[     2       ]]  int
-
-L198:                             [[  L186       ]]
- 198 True     185                 [[   391  418  ]]  Ctrl
- 391 #0       198                 [[   200       ]]  0
- 418 jmp      198                 [[   175       ]]
-
-L184:                             [[  L186       ]]
- 184 False    185                 [[   192  419  ]]  Ctrl
- 192 inc      184  191            [[   191       ]]  int
-
-L143:                             [[  L115       ]]
- 143 False    114                 [[   381  417  ]]  Ctrl
- 381 #0       143                 [[   148       ]]  0
- 417 jmp      143                 [[   108       ]]
-
-L205:                             [[  L181       ]]
- 205 CallEnd  180                 [[   204  206  207  ]]  [  Ctrl, #BOT, bool]
- 206 $mem     205                 [[   203       ]]  #BOT
- 207 #2       205                 [[   203       ]]  bool
-
-L113:                             [[  L115       ]]
- 113 True     114                 [[   123  112  ]]  Ctrl
- 123 cmp      113  125  126       [[   112       ]]  bool
- 112 j<=      113  123            [[   111  144  ]]  [  Ctrl, Ctrl]
-
-L72:                              [[  L69        ]]
-  72 CallEnd   68                 [[    71   81  ]]  [  Ctrl, #BOT, int]
-  71 $mem      72                 [[    80       ]]  #BOT
-
-L3:                               [[  L4         ]]
-   3 $ctrl      4                 [[     2       ]]  Ctrl
-   2 Return     3   58   59   60  304  305  306  307  308  309  310  311  312  313  314  315  316  317  318  319  320  321  [[     1  ]]  [  Ctrl, #BOT, int]
-
-L81:                              [[  L72        ]]
-  81 $ctrl     72                 [[   327  330  336  343   80  ]]  Ctrl
- 327 mov       81  324            [[    80       ]]
- 330 mov       81  328            [[    80       ]]
- 343 mov       81  341            [[    80       ]]
- 336 mov       81  332            [[    80       ]]
-  80 call      81   71  327  343  336  330  [[    89  ]]  Ctrl
-
-L204:                             [[  L205       ]]
- 204 $ctrl    205                 [[   410  203  ]]  Ctrl
- 410 mov      204  409            [[   203       ]]
- 203 Return   204  206  207  208  410  287  288  289  290  291  292  293  294  295  296  297  298  299  300  301  302  303  [[     1  ]]  [  Ctrl, #BOT, bool]
-
-L175:                             [[  L176      L198       ]]
- 175 Region  ____  176  418       [[   200  199  174  ]]  Ctrl
- 200 Phi_res  175  149  391       [[   174       ]]  bool
- 199 ALLMEM   175  189 ____  189  [[   174       ]]  #BOT
- 174 Return   175  199  200  201  268  269  270  271  272  273  274  275  276  277  278  279  280  281  282  283  284  285  [[     1  ]]  [  Ctrl, #BOT, bool]
-
-L111:                             [[  L113       ]]
- 111 False    112                 [[   323  142  129  110  ]]  Ctrl
- 323 mov      111  322            [[   142       ]]
- 142 ld8      111  131  323  126  [[   129       ]]  int
- 129 cmp8     111  131  322  118  142  [[   110  ]]  bool
- 110 j<=      111  129            [[   109  145  ]]  [  Ctrl, Ctrl]
-
-L144:                             [[  L113       ]]
- 144 True     112                 [[   380  108  ]]  Ctrl
- 380 #1       144                 [[   148       ]]  1
-
-L89:                              [[  L81        ]]
-  89 CallEnd   80                 [[    88   90  ]]  [  Ctrl, #BOT, int]
-  90 $mem      89                 [[    87       ]]  #BOT
-
-L88:                              [[  L89        ]]
-  88 $ctrl     89                 [[   325  329  333  342  384   87  ]]  Ctrl
- 325 mov       88  324            [[    87       ]]
- 329 mov       88  328            [[    87       ]]
- 384 mov       88  383            [[    87       ]]
- 342 mov       88  341            [[    87       ]]
- 333 mov       88  332            [[    87       ]]
-  87 call      88   90  329  384  342  333  325  [[    86  ]]  Ctrl
-
-L109:                             [[  L111       ]]
- 109 True     110                 [[   379  415  ]]  Ctrl
- 379 #1       109                 [[   148       ]]  1
- 415 jmp      109                 [[   108       ]]
-
-L145:                             [[  L111       ]]
- 145 False    110                 [[   382  416  ]]  Ctrl
- 382 #0       145                 [[   148       ]]  0
- 416 jmp      145                 [[   108       ]]
-
-L108:                             [[  L109      L143      L144      L145       ]]
- 108 Region  ____  415  417  144  416  [[   148  146  107  ]]  Ctrl
- 148 Phi_con  108  379  381  380  382  [[   146  ]]  bool
- 146 test     108  148            [[   107       ]]
- 107 j!=      108  146            [[   104  141  ]]  [  Ctrl, Ctrl]
-
-L86:                              [[  L88        ]]
-  86 CallEnd   87                 [[    85   92   94  ]]  [  Ctrl, #BOT, int]
-  92 $mem      86                 [[    91       ]]  #BOT
-  94 #2        86                 [[    93       ]]  int
-
-L141:                             [[  L108       ]]
- 141 False    107                 [[   140  128  414  ]]  Ctrl
- 128 inc      141  126            [[   127       ]]  int
- 140 ld8      141  131  322  126  [[   137       ]]  int
- 414 jmp      141                 [[   103       ]]
-
-L104:                             [[  L108       ]]
- 104 True     107                 [[   138  121  103  ]]  Ctrl
- 121 inc      104  118            [[   120       ]]  int
- 138 ld8      104  131  322  118  [[   137       ]]  int
-
-L85:                              [[  L86        ]]
-  85 $ctrl     86                 [[   412       ]]  Ctrl
- 412 jmp       85                 [[    64       ]]
-
-L103:                             [[  L104      L141       ]]
- 103 Region  ____  104  414       [[   120  127  137  133  136  413  ]]  Ctrl
- 120 Phi_i    103  121  118       [[   118       ]]  int
- 127 Phi_j    103  126  128       [[   126       ]]  int
- 137 Phi_$3   103  138  140       [[   133       ]]  int
- 136 inc      103  135            [[   135       ]]  int
- 133 st8      103  131  134  135  137  [[   131  ]]  #3:Bot
-
-L64:                              [[  L65       L85        ]]
-  64 Region  ____   65  412       [[    91   93   63  ]]  Ctrl
-  91 Phi_$me   64   70   92       [[    63       ]]  #BOT
-  93 Phi_arg   64   53   94       [[    63       ]]  int
-  63 Return    64   91   93   95  250  251  252  253  254  255  256  257  258  259  260  261  262  263  264  265  266  267  [[     1  ]]  [  Ctrl, #BOT, int]
-
-L1:                               [[  L155      L175      L204       ]]
-   1 Stop       2   63   98  154  174  203  [[   ]]  Bot
-
-
+---split_merge { *![int] int int *![int] -> int #0}---------------------------
+00F0 4883EC38    subi  rsp -= #56
+                 a:r9,b:rcx,begin:rdx,end:r8
+00F4 4889542428  mov   [rsp+40] = rdx                 // def/loop #1
+00F9 4C89442458  mov   [rsp+88] = r8                  // def/loop #0
+00FE 48894C2450  mov   [rsp+80] = rcx                 // def/loop #0
+0103 4C894C2448  mov   [rsp+72] = r9                  // def/loop #0
+0108 4C8B442458  mov   r8 = [rsp+88]                  // use/loop/use #0
+010D 4C8B542428  mov   r10 = [rsp+40]                 // use/loop/use #1
+0112 4D2BC2      sub   r8 -= r10
+0115 4983F801    cmp   r8, #1
+0119 7E71        j<=   L65                            // L69
+L69:
+011B 488B442458  mov   rax = [rsp+88]                 // use/loop/use #0
+0120 488B542428  mov   rdx = [rsp+40]                 // use/loop/use #1
+0125 4803C2      add   rax += rdx
+0128 B902000000  ldi   rcx = #2
+012D 489948F7F9  div   rax = rax / rcx                // kill rdx
+0132 4889442440  mov   [rsp+64] = rax                 // def/loop #0
+0137 488B4C2448  mov   rcx = [rsp+72]                 // use/loop/use #0
+013C 4C8B4C2450  mov   r9 = [rsp+80]                  // use/loop/use #0
+0141 4C8B442440  mov   r8 = [rsp+64]                  // use/loop/use #0
+0146 488B542428  mov   rdx = [rsp+40]                 // use/loop/use #1
+014B E8A0FFFFFF  call  split_merge  rcx  rdx  r8  r9
+0150 488B4C2448  mov   rcx = [rsp+72]                 // use/loop/use #0
+0155 4C8B4C2450  mov   r9 = [rsp+80]                  // use/loop/use #0
+015A 4C8B442458  mov   r8 = [rsp+88]                  // use/loop/use #0
+015F 488B542440  mov   rdx = [rsp+64]                 // use/loop/use #0
+0164 E887FFFFFF  call  split_merge  rcx  rdx  r8  r9
+0169 FF7424488F  mov   [rsp+32] = [rsp+72]            // use/loop/use #0
+016E 442420
+0171 488B4C2450  mov   rcx = [rsp+80]                 // use/loop/use #0
+0176 4C8B4C2458  mov   r9 = [rsp+88]                  // use/loop/use #0
+017B 4C8B442440  mov   r8 = [rsp+64]                  // use/loop/use #0
+0180 488B542428  mov   rdx = [rsp+40]                 // use/loop/use #1
+0185 E8A6FEFFFF  call  merge  rcx  rdx  r8  r9  [stk#42]
+018A EB02        jmp   L64
+L412:
+L65:
+018C 33C0        xor   rax,rax
+L64:
+                 arg:rax
+018E 4883C438C3  addi  rsp += #56
+0193             ret
+---{ *![int] int int *![int] -> int #0}---------------------------
 
----eq { *![int] *![int] int -> int #1}---------------------------
-               a:rcx,b:rdx,n:r8
-0000 ??        xor   rax,rax
-0001 ??        JMP   L178
-LOOP178:
-               i:rax
-0002 ??        cmp   rax, r8
-0003 ??        j>=   L176                           // L186
-L186:
-0004 ??        ld8   r9,[rdx+rax*8+8]
-0005 ??        cmp8  r9, [rcx+rax*8+8]
-0006 ??        j!=   L198                           // L184
-L184:
-0007 ??        inc   rax += #1
-0008 ??        jmp   LOOP178
-L419:
-0009 ??        JMP   L178
-L198:
-000A ??        xor   rax,rax
-000B ??        jmp   L175
-L418:
-000C ??        JMP   L175
-L176:
-000D ??        ldi   rax = #1
-000E ??        JMP   L175
-L175:
-               result:rax
-000F ??        ALLME
-0010 ??        ret
----{ *![int] *![int] int -> int #1}---------------------------
+---copy_array { *![int] int int *![int] -> int #1}---------------------------
+                 begin:rdx,end:r8,b:r9,a:rcx
+LOOP157:
+                 k:rdx
+02F0 493BD0      cmp   rdx, r8
+02F3 7D10        j>=   L155                           // L159
+L159:
+02F5 488B44D108  ld8   rax,[rcx+rdx*8+8]
+02FA 498944D108  st8   [r9+rdx*8+8],rax
+02FF 4883C201    inc   rdx += #1
+0303 EBEB        jmp   LOOP157
+L411:
+L155:
+0305 33C0        xor   rax,rax
+0307 C3          ret
+---{ *![int] int int *![int] -> int #1}---------------------------
 
 ---merge { *![int] int int int *![int] -> int #0}---------------------------
-               begin:rdx,middle:r8,end:r9,b:rcx,a:[stk#38]
-0011 ??        mov   xmm0 = rbx                     // def/loop #2
-0012 ??        mov   rbx = [rsp+0]                  // def/empty1 #0
-0013 ??        mov   r11 = rdx                      // use/self/phi #1
-0014 ??        mov   r10 = r8                       // use/self/use #1
-0015 ??        JMP   L101
+                 begin:rdx,middle:r8,end:r9,b:rcx,a:[rsp+40]
+0030 66480F6EC3  mov   xmm0 = rbx                     // def/loop #2
+0035 488B5C2428  mov   rbx = [rsp+40]                 // def/empty1 #0
+003A 4C8BDA      mov   r11 = rdx                      // use/self/phi #1
+003D 4D8BD0      mov   r10 = r8                       // use/self/use #1
 LOOP101:
-               i:r11,j:r10,k:rdx
-0016 ??        cmp   rdx, r9
-0017 ??        j>=   L99                            // L115
+                 i:r11,j:r10,k:rdx
+0040 493BD1      cmp   rdx, r9
+0043 7D52        j>=   L99                            // L115
 L115:
-0018 ??        cmp   r11, r8
-0019 ??        j<    L113                           // L143
+0045 4D3BD8      cmp   r11, r8
+0048 7C04        j<    L113                           // L143
 L143:
-001A ??        xor   rax,rax
-001B ??        jmp   L108
+004A 33C0        xor   rax,rax
+004C EB24        jmp   L108
 L417:
-001C ??        JMP   L108
 L113:
-001D ??        cmp   r9, r10
-001E ??        j<=   L144                           // L111
+004E 4D3BCA      cmp   r9, r10
+0051 7E1A        j<=   L144                           // L111
 L111:
-001F ??        mov   rax = rbx                      // use/empty1 #0
-0020 ??        ld8   rax,[rax+r10*8+8]
-0021 ??        cmp8  rax, [rbx+r11*8+8]
-0022 ??        j<=   L109                           // L145
+0053 488BC3      mov   rax = rbx                      // use/empty1 #0
+0056 4A8B44D008  ld8   rax,[rax+r10*8+8]
+005B 4A3944DB08  cmp8  rax, [rbx+r11*8+8]
+0060 7E04        j<=   L109                           // L145
 L145:
-0023 ??        xor   rax,rax
-0024 ??        jmp   L108
+0062 33C0        xor   rax,rax
+0064 EB0C        jmp   L108
 L416:
-0025 ??        JMP   L108
 L109:
-0026 ??        ldi   rax = #1
-0027 ??        jmp   L108
+0066 B801000000  ldi   rax = #1
+006B EB05        jmp   L108
 L415:
-0028 ??        JMP   L108
 L144:
-0029 ??        ldi   rax = #1
-002A ??        JMP   L108
+006D B801000000  ldi   rax = #1
 L108:
-               cond:rax
-002B ??        test  rax
-002C ??        j!=   L104                           // L141
+                 cond:rax
+0072 4883F800    test  rax
+0076 750B        j!=   L104                           // L141
 L141:
-002D ??        ld8   rax,[rbx+r10*8+8]
-002E ??        inc   r10 += #1
-002F ??        jmp   L103
+0078 4A8B44D308  ld8   rax,[rbx+r10*8+8]
+007D 4983C201    inc   r10 += #1
+0081 EB09        jmp   L103
 L414:
-0030 ??        JMP   L103
 L104:
-0031 ??        ld8   rax,[rbx+r11*8+8]
-0032 ??        inc   r11 += #1
-0033 ??        JMP   L103
+0083 4A8B44DB08  ld8   rax,[rbx+r11*8+8]
+0088 4983C301    inc   r11 += #1
 L103:
-               i:r11,j:r10,$3:rax
-0034 ??        st8   [rcx+rdx*8+8],rax
-0035 ??        inc   rdx += #1
-0036 ??        jmp   LOOP101
+                 i:r11,j:r10,$3:rax
+008C 488944D108  st8   [rcx+rdx*8+8],rax
+0091 4883C201    inc   rdx += #1
+0095 EBA9        jmp   LOOP101
 L413:
-0037 ??        JMP   L101
 L99:
-0038 ??        ALLME
-0039 ??        mov   rbx = xmm0                     // use/loop/use #2
-003A ??        xor   rax,rax
-003B ??        ret
+0097 66480F7EC3  mov   rbx = xmm0                     // use/loop/use #2
+009C 33C0        xor   rax,rax
+009E C3          ret
 ---{ *![int] int int int *![int] -> int #0}---------------------------
 
----merge_sort { *![int] *![int] int -> int #0}---------------------------
-               a:rcx,n:r8,b:rdx
-003C ??        mov   [rsp+24] = r8                  // def/loop #1
-003D ??        mov   [rsp+16] = rcx                 // def/loop #1
-003E ??        mov   [rsp+8] = rdx                  // def/loop #0
-003F ??        mov   r9 = [rsp+8]                   // use/loop/use #0
-0040 ??        mov   rcx = [rsp+16]                 // use/loop/use #1
-0041 ??        mov   r8 = [rsp+24]                  // use/loop/use #1
-0042 ??        xor   rdx,rdx
-0043 ??        call  copy_array  rcx  rdx  r8  r9
-0044 ??        mov   r9 = [rsp+8]                   // use/loop/use #0
-0045 ??        mov   rcx = [rsp+16]                 // use/loop/use #1
-0046 ??        mov   r8 = [rsp+24]                  // use/loop/use #1
-0047 ??        xor   rdx,rdx
-0048 ??        call  split_merge  rcx  rdx  r8  r9
-                 #2    rax
-0049 ??        addi  rsp += #40
-ret
----{ *![int] *![int] int -> int #0}---------------------------
 
----split_merge { *![int] int int *![int] -> int #0}---------------------------
-               a:r9,b:rcx,begin:rdx,end:r8
-004A ??        mov   [rsp+40] = rdx                 // def/loop #1
-004B ??        mov   [rsp+32] = r8                  // def/loop #0
-004C ??        mov   [rsp+24] = rcx                 // def/loop #0
-004D ??        mov   [rsp+16] = r9                  // def/loop #0
-004E ??        mov   r8 = [rsp+32]                  // use/loop/use #0
-004F ??        mov   r10 = [rsp+40]                 // use/loop/use #1
-0050 ??        sub   r8 -= r10
-0051 ??        cmp   r8, #1
-0052 ??        j<=   L65                            // L69
-L69:
-0053 ??        mov   rax = [rsp+32]                 // use/loop/use #0
-0054 ??        mov   rdx = [rsp+40]                 // use/loop/use #1
-0055 ??        add   rax += rdx
-0056 ??        ldi   rcx = #2
-0057 ??        div   rax = rax / rcx                // kill rdx
-0058 ??        mov   [rsp+8] = rax                  // def/loop #0
-0059 ??        mov   rcx = [rsp+16]                 // use/loop/use #0
-005A ??        mov   r9 = [rsp+24]                  // use/loop/use #0
-005B ??        mov   r8 = [rsp+8]                   // use/loop/use #0
-005C ??        mov   rdx = [rsp+40]                 // use/loop/use #1
-005D ??        call  split_merge  rcx  rdx  r8  r9
-005E ??        mov   rcx = [rsp+16]                 // use/loop/use #0
-005F ??        mov   r9 = [rsp+24]                  // use/loop/use #0
-0060 ??        mov   r8 = [rsp+32]                  // use/loop/use #0
-0061 ??        mov   rdx = [rsp+8]                  // use/loop/use #0
-0062 ??        call  split_merge  rcx  rdx  r8  r9
-0063 ??        mov   [rsp+32] = [rsp+16]            // use/loop/use #0
-0064 ??        mov   rcx = [rsp+24]                 // use/loop/use #0
-0065 ??        mov   r9 = [rsp+32]                  // use/loop/use #0
-0066 ??        mov   r8 = [rsp+8]                   // use/loop/use #0
-0067 ??        mov   rdx = [rsp+40]                 // use/loop/use #1
-0068 ??        call  merge  rcx  rdx  r8  r9  [stk#42]
-                 #2    rax
-0069 ??        jmp   L64
-L412:
-006A ??        JMP   L64
-L65:
-006B ??        xor   rax,rax
-006C ??        JMP   L64
-L64:
-               arg:rax
-006D ??        addi  rsp += #56
-ret
----{ *![int] int int *![int] -> int #0}---------------------------
+---eq { *![int] *![int] int -> int #1}---------------------------
+                 a:rcx,b:rdx,n:r8
+0000 33C0        xor   rax,rax
+LOOP178:
+                 i:rax
+0002 493BC0      cmp   rax, r8
+0005 7D16        j>=   L176                           // L186
+L186:
+0007 4C8B4CC208  ld8   r9,[rdx+rax*8+8]
+000C 4C394CC108  cmp8  r9, [rcx+rax*8+8]
+0011 7506        j!=   L198                           // L184
+L184:
+0013 4883C001    inc   rax += #1
+0017 EBE9        jmp   LOOP178
+L419:
+L198:
+0019 33C0        xor   rax,rax
+001B EB05        jmp   L175
+L418:
+L176:
+001D B801000000  ldi   rax = #1
+L175:
+                 result:rax
+0022 C3          ret
+---{ *![int] *![int] int -> int #1}---------------------------
 
 ---main { -> int #0}---------------------------
-
-006E ??        mov   [rsp+0] = rbx                  // def/loop #2
-006F ??        ldi   rdx = #88
-0070 ??        alloc ldi   rcx = #1
-call  #calloc
-                 [int] rax
-0071 ??        mov   [rsp+32] = rax                 // def/loop #0
-0072 ??        mov   rdx = [rsp+32]                 // use/loop/use #0
-0073 ??        st8   [rdx+8],#10
-0074 ??        mov   rcx = [rsp+32]                 // use/loop/use #0
-0075 ??        st8   [rcx+16],#9
-0076 ??        mov   rax = [rsp+32]                 // use/loop/use #0
-0077 ??        st8   [rax+24],#8
-0078 ??        mov   r11 = [rsp+32]                 // use/loop/use #0
-0079 ??        st8   [r11+32],#7
-007A ??        mov   r10 = [rsp+32]                 // use/loop/use #0
-007B ??        st8   [r10+40],#6
-007C ??        mov   r9 = [rsp+32]                  // use/loop/use #0
-007D ??        st8   [r9+48],#5
-007E ??        mov   r8 = [rsp+32]                  // use/loop/use #0
-007F ??        st8   [r8+56],#4
-0080 ??        mov   rdx = [rsp+32]                 // use/loop/use #0
-0081 ??        st8   [rdx+64],#3
-0082 ??        mov   rcx = [rsp+32]                 // use/loop/use #0
-0083 ??        st8   [rcx+72],#2
-0084 ??        mov   rax = [rsp+32]                 // use/loop/use #0
-0085 ??        st8   [rax+80],#1
-0086 ??        ldi   rdx = #88
-0087 ??        alloc ldi   rcx = #1
-call  #calloc
-                 [int] rax
-0088 ??        mov   [rsp+24] = rax                 // def/empty1 #1
-0089 ??        ldi   rdx = #88
-008A ??        alloc ldi   rcx = #1
-call  #calloc
-                 [int] rax
-008B ??        mov   rbx = rax                      // def/empty1 #0
-008C ??        st8   [rbx+8],#1
-008D ??        st8   [rbx+16],#2
-008E ??        st8   [rbx+24],#3
-008F ??        st8   [rbx+32],#4
-0090 ??        st8   [rbx+40],#5
-0091 ??        st8   [rbx+48],#6
-0092 ??        st8   [rbx+56],#7
-0093 ??        st8   [rbx+64],#8
-0094 ??        st8   [rbx+72],#9
-0095 ??        st8   [rbx+80],#10
-0096 ??        ALLME
-0097 ??        mov   rcx = [rsp+32]                 // use/loop/use #0
-0098 ??        mov   rdx = [rsp+24]                 // use/empty1 #0
-0099 ??        ldi   r8 = #10
-009A ??        call  merge_sort  rcx  rdx  r8
-009B ??        mov   rcx = [rsp+32]                 // use/loop/use #0
-009C ??        mov   rdx = rbx                      // use/empty1 #0
-009D ??        ldi   r8 = #10
-009E ??        call  eq  rcx  rdx  r8
-                 #2    rax
-009F ??        mov   rbx = [rsp+0]                  // use/loop/use #2
-00A0 ??        addi  rsp += #40
-ret
+01A0 4883EC28    subi  rsp -= #40
+01A4 48895C2420  mov   [rsp+32] = rbx                 // def/loop #2
+01A9 BA58000000  ldi   rdx = #88
+01AE B901000000  alloc ldi   rcx = #1
+01B3 E800000000  call  #calloc
+01B8 4889442418  mov   [rsp+24] = rax                 // def/loop #0
+01BD 488B542418  mov   rdx = [rsp+24]                 // use/loop/use #0
+01C2 48C742080A  st8   [rdx+8],#10
+01C7 000000
+01CA 488B4C2418  mov   rcx = [rsp+24]                 // use/loop/use #0
+01CF 48C7411009  st8   [rcx+16],#9
+01D4 000000
+01D7 488B442418  mov   rax = [rsp+24]                 // use/loop/use #0
+01DC 48C7401808  st8   [rax+24],#8
+01E1 000000
+01E4 4C8B5C2418  mov   r11 = [rsp+24]                 // use/loop/use #0
+01E9 49C7432007  st8   [r11+32],#7
+01EE 000000
+01F1 4C8B542418  mov   r10 = [rsp+24]                 // use/loop/use #0
+01F6 49C7422806  st8   [r10+40],#6
+01FB 000000
+01FE 4C8B4C2418  mov   r9 = [rsp+24]                  // use/loop/use #0
+0203 49C7413005  st8   [r9+48],#5
+0208 000000
+020B 4C8B442418  mov   r8 = [rsp+24]                  // use/loop/use #0
+0210 49C7403804  st8   [r8+56],#4
+0215 000000
+0218 488B542418  mov   rdx = [rsp+24]                 // use/loop/use #0
+021D 48C7424003  st8   [rdx+64],#3
+0222 000000
+0225 488B4C2418  mov   rcx = [rsp+24]                 // use/loop/use #0
+022A 48C7414802  st8   [rcx+72],#2
+022F 000000
+0232 488B442418  mov   rax = [rsp+24]                 // use/loop/use #0
+0237 48C7405001  st8   [rax+80],#1
+023C 000000
+023F BA58000000  ldi   rdx = #88
+0244 B901000000  alloc ldi   rcx = #1
+0249 E800000000  call  #calloc
+024E 4889442410  mov   [rsp+16] = rax                 // def/empty1 #1
+0253 BA58000000  ldi   rdx = #88
+0258 B901000000  alloc ldi   rcx = #1
+025D E800000000  call  #calloc
+0262 488BD8      mov   rbx = rax                      // def/empty1 #0
+0265 48C7430801  st8   [rbx+8],#1
+026A 000000
+026D 48C7431002  st8   [rbx+16],#2
+0272 000000
+0275 48C7431803  st8   [rbx+24],#3
+027A 000000
+027D 48C7432004  st8   [rbx+32],#4
+0282 000000
+0285 48C7432805  st8   [rbx+40],#5
+028A 000000
+028D 48C7433006  st8   [rbx+48],#6
+0292 000000
+0295 48C7433807  st8   [rbx+56],#7
+029A 000000
+029D 48C7434008  st8   [rbx+64],#8
+02A2 000000
+02A5 48C7434809  st8   [rbx+72],#9
+02AA 000000
+02AD 48C743500A  st8   [rbx+80],#10
+02B2 000000
+02B5 488B4C2418  mov   rcx = [rsp+24]                 // use/loop/use #0
+02BA 488B542410  mov   rdx = [rsp+16]                 // use/empty1 #0
+02BF 41B80A0000  ldi   r8 = #10
+02C4 00
+02C5 E8D6FDFFFF  call  merge_sort  rcx  rdx  r8
+02CA 488B4C2418  mov   rcx = [rsp+24]                 // use/loop/use #0
+02CF 488BD3      mov   rdx = rbx                      // use/empty1 #0
+02D2 41B80A0000  ldi   r8 = #10
+02D7 00
+02D8 E823FDFFFF  call  eq  rcx  rdx  r8
+02DD 488B5C2420  mov   rbx = [rsp+32]                 // use/loop/use #2
+02E2 4883C428C3  addi  rsp += #40
+02E7             ret
 ---{ -> int #0}---------------------------
 
----copy_array { *![int] int int *![int] -> int #1}---------------------------
-               begin:rdx,end:r8,b:r9,a:rcx
-00A1 ??        JMP   L157
-LOOP157:
-               k:rdx
-00A2 ??        cmp   rdx, r8
-00A3 ??        j>=   L155                           // L159
-L159:
-00A4 ??        ld8   rax,[rcx+rdx*8+8]
-00A5 ??        st8   [r9+rdx*8+8],rax
-00A6 ??        inc   rdx += #1
-00A7 ??        jmp   LOOP157
-L411:
-00A8 ??        JMP   L157
-L155:
-00A9 ??        ALLME
-00AA ??        xor   rax,rax
-00AB ??        ret
----{ *![int] int int *![int] -> int #1}---------------------------
+---merge_sort { *![int] *![int] int -> int #0}---------------------------
+00A0 4883EC28    subi  rsp -= #40
+                 a:rcx,n:r8,b:rdx
+00A4 4C89442440  mov   [rsp+64] = r8                  // def/loop #1
+00A9 48894C2438  mov   [rsp+56] = rcx                 // def/loop #1
+00AE 4889542430  mov   [rsp+48] = rdx                 // def/loop #0
+00B3 4C8B4C2430  mov   r9 = [rsp+48]                  // use/loop/use #0
+00B8 488B4C2438  mov   rcx = [rsp+56]                 // use/loop/use #1
+00BD 4C8B442440  mov   r8 = [rsp+64]                  // use/loop/use #1
+00C2 33D2        xor   rdx,rdx
+00C4 E827020000  call  copy_array  rcx  rdx  r8  r9
+00C9 4C8B4C2430  mov   r9 = [rsp+48]                  // use/loop/use #0
+00CE 488B4C2438  mov   rcx = [rsp+56]                 // use/loop/use #1
+00D3 4C8B442440  mov   r8 = [rsp+64]                  // use/loop/use #1
+00D8 33D2        xor   rdx,rdx
+00DA E811000000  call  split_merge  rcx  rdx  r8  r9
+00DF 4883C428C3  addi  rsp += #40
+00E4             ret
+---{ *![int] *![int] int -> int #0}---------------------------
+
+
diff --git a/seaofnodes/src/test/cases/mergsort/sort_smp.out b/seaofnodes/src/test/cases/mergsort/sort_smp.out
index e5edda1..c06e3ab 100644
--- a/seaofnodes/src/test/cases/mergsort/sort_smp.out
+++ b/seaofnodes/src/test/cases/mergsort/sort_smp.out
@@ -1,473 +1,3 @@
-START:                            [[   ]]
-  10 Start   ____ ____ ____       [[    13   61   64   86   58    9  190  121   70   93  ]]  [  Ctrl, #TOP, int]
-
-L9:                               [[  START      ]]
-   9 main    ____   10            [[    12  212  325  324  323  322  321  320  319  318  317  316  315  314  313  312  311  310  309  308  416  408   23  355  358   34  359   33  360   32  361   31  362   30  363   29  364   28  365   27  366   26  357   21  367   25  407   20  411   18  406   16  371   48   47   46   45   44   43   42   41   40   14   39   11  368  370  410    8  ]]  Ctrl
-  12 $mem       9   13            [[    11   23   23  ]]  #BOT
- 212 $rpc       9   86            [[     2       ]]  $[ALL]
- 358 mov        9  355            [[    34       ]]
-  34 st8        9   35  358 ____ ____  [[    33  ]]  #3:int
- 359 mov        9  355            [[    33       ]]
-  33 st8        9   34  359 ____ ____  [[    32  ]]  #3:int
- 360 mov        9  355            [[    32       ]]
-  32 st8        9   33  360 ____ ____  [[    31  ]]  #3:int
- 361 mov        9  355            [[    31       ]]
-  48 st8        9   49  371 ____ ____  [[    47  ]]  #3:int
-  31 st8        9   32  361 ____ ____  [[    30  ]]  #3:int
- 362 mov        9  355            [[    30       ]]
-  47 st8        9   48  371 ____ ____  [[    46  ]]  #3:int
-  30 st8        9   31  362 ____ ____  [[    29  ]]  #3:int
-  46 st8        9   47  371 ____ ____  [[    45  ]]  #3:int
- 363 mov        9  355            [[    29       ]]
-  45 st8        9   46  371 ____ ____  [[    44  ]]  #3:int
-  29 st8        9   30  363 ____ ____  [[    28  ]]  #3:int
- 364 mov        9  355            [[    28       ]]
-  44 st8        9   45  371 ____ ____  [[    43  ]]  #3:int
-  28 st8        9   29  364 ____ ____  [[    27  ]]  #3:int
- 365 mov        9  355            [[    27       ]]
-  43 st8        9   44  371 ____ ____  [[    42  ]]  #3:int
-  27 st8        9   28  365 ____ ____  [[    26  ]]  #3:int
- 366 mov        9  355            [[    26       ]]
-  42 st8        9   43  371 ____ ____  [[    41  ]]  #3:int
- 367 mov        9  355            [[    25       ]]
- 357 mov        9  355            [[    21       ]]
-  26 st8        9   27  366 ____ ____  [[    25  ]]  #3:int
-  41 st8        9   42  371 ____ ____  [[    40  ]]  #3:int
-  25 st8        9   26  367 ____ ____  [[    20  ]]  #3:int
- 407 #88        9                 [[    20       ]]  88
-  21 st4        9   22  357 ____ ____  [[    20  ]]  #2:u32
-  40 st8        9   41  371 ____ ____  [[    39  ]]  #3:int
-  20 new_ary    9  407   21   25  [[    19   36   37  ]]  [  Ctrl, *![int], #2:u32, #3:int]
-  19 $2        20                 [[    18       ]]  #2:u32
-  36 [int]     20                 [[   411   18  ]]  *[int]
-  37 $3        20                 [[    16       ]]  #3:int
- 408 #88        9                 [[    23       ]]  88
-  39 st8        9   40  371 ____ ____  [[    11  ]]  #3:int
-  18 st4        9   19   36 ____ ____  [[    16  ]]  #2:u32
- 411 mov        9   36            [[   370       ]]
- 406 #88        9                 [[    16       ]]  88
-  14 st4        9   15  371 ____ ____  [[    11  ]]  #2:u32
- 410 #10        9                 [[     8       ]]  10
-  16 new_ary    9  406   18   37  [[    15   38   49  ]]  [  Ctrl, *![int], #2:u32, #3:int]
-  15 $2        16                 [[    14       ]]  #2:u32
-  38 [int]     16                 [[   371       ]]  *[int]
-  49 $3        16                 [[    48       ]]  #3:int
-  11 ALLMEM     9   12   14   39  [[     8       ]]  #BOT
- 368 mov        9  355            [[     8       ]]
- 370 mov        9  411            [[     8       ]]
-  23 new_ary    9  408   12   12  [[    22   24   35  ]]  [  Ctrl, *![int], #2:u32, #3:int]
-  22 $2        23                 [[    21       ]]  #2:u32
-  24 [int]     23                 [[   355       ]]  *[int]
-  35 $3        23                 [[    34       ]]  #3:int
-   8 call       9   11  368  370  410  [[     7  ]]  Ctrl
- 371 mov        9   38            [[   372   39   40   41   42   43   44   45   46   47   48   14  ]]
- 355 mov        9   24            [[   356  368  367  366  365  364  363  362  361  360  359  358  357  ]]
- 308 rbx        9                 [[   416       ]]
- 416 mov        9  308            [[   417       ]]
- 310 rsi        9                 [[     2       ]]
- 311 rdi        9                 [[     2       ]]
- 312 r12        9                 [[     2       ]]
- 313 r13        9                 [[     2       ]]
- 314 r14        9                 [[     2       ]]
- 315 r15        9                 [[     2       ]]
- 316 xmm6       9                 [[     2       ]]
- 317 xmm7       9                 [[     2       ]]
- 318 xmm8       9                 [[     2       ]]
- 319 xmm9       9                 [[     2       ]]
- 320 xmm10      9                 [[     2       ]]
- 321 xmm11      9                 [[     2       ]]
- 322 xmm12      9                 [[     2       ]]
- 323 xmm13      9                 [[     2       ]]
- 324 xmm14      9                 [[     2       ]]
- 325 xmm15      9                 [[     2       ]]
- 309 rbp        9                 [[     2       ]]
-
-L70:                              [[  START      ]]
-  70 copy_ar ____   10            [[    75   77   79   82   84   85  289  288  287  286  285  284  283  282  281  280  279  278  277  276  275  274  273  272   69  ]]  Ctrl
-  75 begin     70   64            [[    74       ]]  int
-  77 end       70   64            [[    72       ]]  int
-  79 $mem      70   13            [[    78   80  ]]  #BOT
-  82 b         70   61            [[    81       ]]  *[int]
-  84 a         70   61            [[    83       ]]  *[int]
-  85 $rpc      70   86            [[    66       ]]  $[ALL]
- 272 rbx       70                 [[    66       ]]
- 278 r14       70                 [[    66       ]]
- 279 r15       70                 [[    66       ]]
- 280 xmm6      70                 [[    66       ]]
- 281 xmm7      70                 [[    66       ]]
- 282 xmm8      70                 [[    66       ]]
- 283 xmm9      70                 [[    66       ]]
- 284 xmm10     70                 [[    66       ]]
- 285 xmm11     70                 [[    66       ]]
- 286 xmm12     70                 [[    66       ]]
- 287 xmm13     70                 [[    66       ]]
- 288 xmm14     70                 [[    66       ]]
- 289 xmm15     70                 [[    66       ]]
- 277 r13       70                 [[    66       ]]
- 276 r12       70                 [[    66       ]]
- 275 rdi       70                 [[    66       ]]
- 274 rsi       70                 [[    66       ]]
- 273 rbp       70                 [[    66       ]]
-
-L121:                             [[  START      ]]
- 121 merge   ____   10            [[   138  141  144  151  154  158  173  235  234  233  232  231  230  229  228  227  226  225  224  223  222  221  220  219  218  413  326  376  381  120  ]]  Ctrl
- 138 begin    121   64            [[   376  155  ]]  int
- 141 middle   121   64            [[   135  381  ]]  int
- 144 end      121   64            [[   142  170  ]]  int
- 151 $mem     121   13            [[   150  172  ]]  #BOT
- 154 b        121   61            [[   153  159  ]]  *[int]
- 158 a        121   61            [[   326       ]]  *[int]
- 173 $rpc     121   86            [[   117       ]]  $[ALL]
- 381 mov      121  141            [[   145       ]]
- 376 mov      121  138            [[   137       ]]
- 326 mov      121  158            [[   327  148  161  157  ]]
- 218 rbx      121                 [[   413       ]]
- 413 mov      121  218            [[   414       ]]
- 221 rdi      121                 [[   117       ]]
- 226 xmm6     121                 [[   117       ]]
- 227 xmm7     121                 [[   117       ]]
- 228 xmm8     121                 [[   117       ]]
- 229 xmm9     121                 [[   117       ]]
- 230 xmm10    121                 [[   117       ]]
- 231 xmm11    121                 [[   117       ]]
- 232 xmm12    121                 [[   117       ]]
- 233 xmm13    121                 [[   117       ]]
- 234 xmm14    121                 [[   117       ]]
- 235 xmm15    121                 [[   117       ]]
- 220 rsi      121                 [[   117       ]]
- 219 rbp      121                 [[   117       ]]
- 224 r14      121                 [[   117       ]]
- 225 r15      121                 [[   117       ]]
- 223 r13      121                 [[   117       ]]
- 222 r12      121                 [[   117       ]]
-
-L190:                             [[  START      ]]
- 190 eq      ____   10            [[   196  197  201  204  208  271  270  269  268  267  266  265  264  263  262  261  260  259  258  257  256  255  254  398  189  ]]  Ctrl
- 196 $mem     190   13            [[   194  200  206  206  ]]  #BOT
- 197 a        190   61            [[   194       ]]  *[int]
- 201 b        190   61            [[   200       ]]  *[int]
- 204 n        190   64            [[   202       ]]  int
- 208 $rpc     190   86            [[   185       ]]  $[ALL]
- 398 #0       190                 [[   198       ]]  0
- 258 r12      190                 [[   185       ]]
- 259 r13      190                 [[   185       ]]
- 260 r14      190                 [[   185       ]]
- 261 r15      190                 [[   185       ]]
- 262 xmm6     190                 [[   185       ]]
- 263 xmm7     190                 [[   185       ]]
- 264 xmm8     190                 [[   185       ]]
- 265 xmm9     190                 [[   185       ]]
- 266 xmm10    190                 [[   185       ]]
- 267 xmm11    190                 [[   185       ]]
- 268 xmm12    190                 [[   185       ]]
- 269 xmm13    190                 [[   185       ]]
- 270 xmm14    190                 [[   185       ]]
- 271 xmm15    190                 [[   185       ]]
- 257 rdi      190                 [[   185       ]]
- 256 rsi      190                 [[   185       ]]
- 255 rbp      190                 [[   185       ]]
- 254 rbx      190                 [[   185       ]]
-
-L58:                              [[  START      ]]
-  58 merge_s ____   10            [[    59   60   63   65  182  307  306  305  304  303  302  301  300  299  298  297  296  295  294  293  292  291  290  403  400  349  351  353  402  405   57  ]]  Ctrl
-  59 $mem      58   13            [[    57       ]]  #BOT
-  60 a         58   61            [[   400       ]]  *[int]
-  63 n         58   64            [[   403       ]]  int
-  65 b         58   61            [[   349       ]]  *[int]
- 182 $rpc      58   86            [[    51       ]]  $[ALL]
- 405 mov       58  403            [[    57       ]]
- 402 mov       58  400            [[    57       ]]
- 353 #0        58                 [[    57       ]]  0
- 351 mov       58  349            [[    57       ]]
-  57 call      58   59  402  353  405  351  [[    56  ]]  Ctrl
- 349 mov       58   65            [[   350  351  ]]
- 400 mov       58   60            [[   401  402  ]]
- 403 mov       58   63            [[   404  405  ]]
- 291 rbp       58                 [[    51       ]]
- 295 r13       58                 [[    51       ]]
- 296 r14       58                 [[    51       ]]
- 297 r15       58                 [[    51       ]]
- 298 xmm6      58                 [[    51       ]]
- 299 xmm7      58                 [[    51       ]]
- 300 xmm8      58                 [[    51       ]]
- 301 xmm9      58                 [[    51       ]]
- 302 xmm10     58                 [[    51       ]]
- 303 xmm11     58                 [[    51       ]]
- 304 xmm12     58                 [[    51       ]]
- 305 xmm13     58                 [[    51       ]]
- 306 xmm14     58                 [[    51       ]]
- 307 xmm15     58                 [[    51       ]]
- 290 rbx       58                 [[    51       ]]
- 294 r12       58                 [[    51       ]]
- 293 rdi       58                 [[    51       ]]
- 292 rsi       58                 [[    51       ]]
-
-L93:                              [[  START      ]]
-  93 split_m ____   10            [[    99  101  102  103  106  177  253  252  251  250  249  248  247  246  245  244  243  242  241  240  239  238  237  236  391  336  332  328  342  395  110  108   92  ]]  Ctrl
-  99 $mem      93   13            [[    97  175  ]]  #BOT
- 101 a         93   61            [[   328       ]]  *[int]
- 102 b         93   61            [[   332       ]]  *[int]
- 103 begin     93   64            [[   391       ]]  int
- 106 end       93   64            [[   336       ]]  int
- 177 $rpc      93   86            [[    89       ]]  $[ALL]
- 395 mov       93  391            [[   110       ]]
- 332 mov       93  102            [[   335  333  334  ]]
- 336 mov       93  106            [[   342  340  337  339  ]]
- 391 mov       93  103            [[   395  392  394  393  ]]
- 342 mov       93  336            [[   110       ]]
- 328 mov       93  101            [[   329  331  330  ]]
- 110 sub       93  342  395       [[   108       ]]  int
- 108 cmp       93  110            [[    92       ]]  bool
-  92 j<=       93  108            [[    91   98  ]]  [  Ctrl, Ctrl]
- 241 r13       93                 [[    89       ]]
- 242 r14       93                 [[    89       ]]
- 243 r15       93                 [[    89       ]]
- 244 xmm6      93                 [[    89       ]]
- 245 xmm7      93                 [[    89       ]]
- 246 xmm8      93                 [[    89       ]]
- 247 xmm9      93                 [[    89       ]]
- 248 xmm10     93                 [[    89       ]]
- 249 xmm11     93                 [[    89       ]]
- 250 xmm12     93                 [[    89       ]]
- 251 xmm13     93                 [[    89       ]]
- 252 xmm14     93                 [[    89       ]]
- 253 xmm15     93                 [[    89       ]]
- 238 rsi       93                 [[    89       ]]
- 237 rbp       93                 [[    89       ]]
- 236 rbx       93                 [[    89       ]]
- 239 rdi       93                 [[    89       ]]
- 240 r12       93                 [[    89       ]]
-
-LOOP120:                          [[  L121      L122       ]]
- 120 Loop    ____  121  420       [[   137  145  150  155  170  119  ]]  Ctrl
- 137 Phi_i    120  376  139       [[   148  135  140  139  157  ]]  int
- 145 Phi_j    120  381  146       [[   162  142  146  147  161  ]]  int
- 150 Phi_$3   120  151  152       [[   148  153  157  159  161  162  172  ]]  #BOT
- 155 Phi_k    120  138  156       [[   170  153  156  159  ]]  int
- 170 cmp      120  155  144       [[   119       ]]  bool
- 119 j>=      120  170            [[   118  134  ]]  [  Ctrl, Ctrl]
-
-LOOP189:                          [[  L190      L191       ]]
- 189 Loop    ____  190  419       [[   198  202  188  ]]  Ctrl
- 198 Phi_i    189  398  199       [[   202  194  199  200  ]]  int
- 202 cmp      189  198  204       [[   188       ]]  bool
- 188 j>=      189  202            [[   187  193  ]]  [  Ctrl, Ctrl]
-
-LOOP69:                           [[  L70       L71        ]]
-  69 Loop    ____   70  425       [[    74   80   72   68  ]]  Ctrl
-  74 Phi_k     69   75   76       [[    72   76   81   83  ]]  int
-  80 Phi_$3    69   79   81       [[    78   81   83  ]]  #BOT
-  72 cmp       69   74   77       [[    68       ]]  bool
-  68 j>=       69   72            [[    67   71  ]]  [  Ctrl, Ctrl]
-
-L7:                               [[  L9         ]]
-   7 CallEnd    8                 [[     6  184  ]]  [  Ctrl, #BOT, 0]
- 184 $mem       7                 [[     5       ]]  #BOT
-
-L98:                              [[  L93        ]]
-  98 False     92                 [[   339  394  105  107  104  345  330  335  348  393   97  ]]  Ctrl
- 330 mov       98  328            [[    97       ]]
- 335 mov       98  332            [[    97       ]]
- 393 mov       98  391            [[    97       ]]
- 348 mov       98  345            [[    97       ]]
-  97 call      98   99  330  393  348  335  [[    96  ]]  Ctrl
- 339 mov       98  336            [[   105       ]]
- 394 mov       98  391            [[   105       ]]
- 105 add       98  339  394       [[   104       ]]  int
- 107 #2        98                 [[   104       ]]  2
- 104 div       98  105  107       [[   345       ]]  int
- 345 mov       98  104            [[   348  347  346  ]]
-
-L56:                              [[  L58        ]]
-  56 CallEnd   57                 [[    55   88  ]]  [  Ctrl, #BOT, 0]
-  88 $mem      56                 [[    54       ]]  #BOT
-
-L91:                              [[  L93        ]]
-  91 True      92                 [[    90       ]]  Ctrl
-
-L6:                               [[  L7         ]]
-   6 $ctrl      7                 [[   356  372  409    5  ]]  Ctrl
- 356 mov        6  355            [[     5       ]]
- 372 mov        6  371            [[     5       ]]
- 409 #10        6                 [[     5       ]]  10
-   5 call       6  184  356  372  409  [[     4  ]]  Ctrl
-
-L55:                              [[  L56        ]]
-  55 $ctrl     56                 [[   350  354  401  404   54  ]]  Ctrl
- 350 mov       55  349            [[    54       ]]
- 354 #0        55                 [[    54       ]]  0
- 404 mov       55  403            [[    54       ]]
- 401 mov       55  400            [[    54       ]]
-  54 call      55   88  401  354  404  350  [[    53  ]]  Ctrl
-
-L118:                             [[  LOOP120    ]]
- 118 True     119                 [[   172  414  415  117  ]]  Ctrl
- 172 ALLMEM   118  151 ____  150  [[   117       ]]  #BOT
- 414 mov      118  413            [[   117       ]]
- 415 #0       118                 [[   117       ]]  0
- 117 Return   118  172  415  173  414  219  220  221  222  223  224  225  226  227  228  229  230  231  232  233  234  235  [[     1  ]]  [  Ctrl, #BOT, 0]
-
-L134:                             [[  LOOP120    ]]
- 134 False    119                 [[   135  133  ]]  Ctrl
- 135 cmp      134  137  141       [[   133       ]]  bool
- 133 j<       134  135            [[   132  163  ]]  [  Ctrl, Ctrl]
-
-L187:                             [[  LOOP189    ]]
- 187 True     188                 [[   169  186  ]]  Ctrl
- 169 #1       187                 [[   207       ]]  1
-
-L193:                             [[  LOOP189    ]]
- 193 False    188                 [[   200  194  192  ]]  Ctrl
- 200 ld8      193  196  201  198  [[   194       ]]  int
- 194 cmp8     193  196  197  198  200  [[   192  ]]  bool
- 192 j!=      193  194            [[   191  205  ]]  [  Ctrl, Ctrl]
-
-L67:                              [[  LOOP69     ]]
-  67 True      68                 [[    78   62   66  ]]  Ctrl
-  78 ALLMEM    67   79 ____   80  [[    66       ]]  #BOT
-  62 #0        67                 [[    66       ]]  0
-  66 Return    67   78   62   85  272  273  274  275  276  277  278  279  280  281  282  283  284  285  286  287  288  289  [[     1  ]]  [  Ctrl, #BOT, 0]
-
-L71:                              [[  LOOP69     ]]
-  71 False     68                 [[    83   81   76  425  ]]  Ctrl
-  76 inc       71   74            [[    74       ]]  int
-  83 ld8       71   80   84   74  [[    81       ]]  int
-  81 st8       71   80   82   74   83  [[    80  ]]  #3:Bot
-
-L96:                              [[  L98        ]]
-  96 CallEnd   97                 [[    95  100  ]]  [  Ctrl, #BOT, 0]
- 100 $mem      96                 [[    94       ]]  #BOT
-
-L4:                               [[  L6         ]]
-   4 CallEnd    5                 [[     3  210  211  ]]  [  Ctrl, #BOT, bool]
- 210 $mem       4                 [[     2       ]]  #BOT
- 211 #2         4                 [[     2       ]]  bool
-
-L95:                              [[  L96        ]]
-  95 $ctrl     96                 [[   331  334  340  347   94  ]]  Ctrl
- 331 mov       95  328            [[    94       ]]
- 334 mov       95  332            [[    94       ]]
- 347 mov       95  345            [[    94       ]]
- 340 mov       95  336            [[    94       ]]
-  94 call      95  100  331  347  340  334  [[   115  ]]  Ctrl
-
-L132:                             [[  L134       ]]
- 132 True     133                 [[   142  131  ]]  Ctrl
- 142 cmp      132  144  145       [[   131       ]]  bool
- 131 j<=      132  142            [[   130  164  ]]  [  Ctrl, Ctrl]
-
-L53:                              [[  L55        ]]
-  53 CallEnd   54                 [[    52  181  ]]  [  Ctrl, #BOT, 0]
- 181 $mem      53                 [[    51       ]]  #BOT
-
-L163:                             [[  L134       ]]
- 163 False    133                 [[   389  424  ]]  Ctrl
- 389 #0       163                 [[   168       ]]  0
- 424 jmp      163                 [[   127       ]]
-
-L191:                             [[  L193       ]]
- 191 False    192                 [[   199  419  ]]  Ctrl
- 199 inc      191  198            [[   198       ]]  int
-
-L205:                             [[  L193       ]]
- 205 True     192                 [[   399  418  ]]  Ctrl
- 399 #0       205                 [[   207       ]]  0
- 418 jmp      205                 [[   186       ]]
-
-L3:                               [[  L4         ]]
-   3 $ctrl      4                 [[   417    2  ]]  Ctrl
- 417 mov        3  416            [[     2       ]]
-   2 Return     3  210  211  212  417  309  310  311  312  313  314  315  316  317  318  319  320  321  322  323  324  325  [[     1  ]]  [  Ctrl, #BOT, bool]
-
-L186:                             [[  L187      L205       ]]
- 186 Region  ____  187  418       [[   207  206  185  ]]  Ctrl
- 207 Phi_res  186  169  399       [[   185       ]]  bool
- 206 ALLMEM   186  196 ____  196  [[   185       ]]  #BOT
- 185 Return   186  206  207  208  254  255  256  257  258  259  260  261  262  263  264  265  266  267  268  269  270  271  [[     1  ]]  [  Ctrl, #BOT, bool]
-
-L52:                              [[  L53        ]]
-  52 $ctrl     53                 [[   352   51  ]]  Ctrl
- 352 #0        52                 [[    51       ]]  0
-  51 Return    52  181  352  182  290  291  292  293  294  295  296  297  298  299  300  301  302  303  304  305  306  307  [[     1  ]]  [  Ctrl, #BOT, 0]
-
-L115:                             [[  L95        ]]
- 115 CallEnd   94                 [[   114  116  ]]  [  Ctrl, #BOT, 0]
- 116 $mem     115                 [[   113       ]]  #BOT
-
-L130:                             [[  L132       ]]
- 130 False    131                 [[   327  162  148  129  ]]  Ctrl
- 327 mov      130  326            [[   162       ]]
- 162 ld8      130  150  327  145  [[   148       ]]  int
- 148 cmp8     130  150  326  137  162  [[   129  ]]  bool
- 129 j<=      130  148            [[   128  165  ]]  [  Ctrl, Ctrl]
-
-L164:                             [[  L132       ]]
- 164 True     131                 [[   388  127  ]]  Ctrl
- 388 #1       164                 [[   168       ]]  1
-
-L114:                             [[  L115       ]]
- 114 $ctrl    115                 [[   329  333  337  346  392  113  ]]  Ctrl
- 329 mov      114  328            [[   113       ]]
- 333 mov      114  332            [[   113       ]]
- 392 mov      114  391            [[   113       ]]
- 346 mov      114  345            [[   113       ]]
- 337 mov      114  336            [[   113       ]]
- 113 call     114  116  333  392  346  337  329  [[   112  ]]  Ctrl
-
-L128:                             [[  L130       ]]
- 128 True     129                 [[   387  422  ]]  Ctrl
- 387 #1       128                 [[   168       ]]  1
- 422 jmp      128                 [[   127       ]]
-
-L165:                             [[  L130       ]]
- 165 False    129                 [[   390  423  ]]  Ctrl
- 390 #0       165                 [[   168       ]]  0
- 423 jmp      165                 [[   127       ]]
-
-L112:                             [[  L114       ]]
- 112 CallEnd  113                 [[   111  176  ]]  [  Ctrl, #BOT, 0]
- 176 $mem     112                 [[   175       ]]  #BOT
-
-L127:                             [[  L128      L163      L164      L165       ]]
- 127 Region  ____  422  424  164  423  [[   168  166  126  ]]  Ctrl
- 168 Phi_con  127  387  389  388  390  [[   166  ]]  bool
- 166 test     127  168            [[   126       ]]
- 126 j!=      127  166            [[   123  160  ]]  [  Ctrl, Ctrl]
-
-L111:                             [[  L112       ]]
- 111 $ctrl    112                 [[    90       ]]  Ctrl
-
-L90:                              [[  L91       L111       ]]
-  90 Region  ____   91  111       [[   175  215   89  ]]  Ctrl
- 175 Phi_$me   90   99  176       [[    89       ]]  #BOT
- 215 #0        90                 [[    89       ]]  0
-  89 Return    90  175  215  177  236  237  238  239  240  241  242  243  244  245  246  247  248  249  250  251  252  253  [[     1  ]]  [  Ctrl, #BOT, 0]
-
-L160:                             [[  L127       ]]
- 160 False    126                 [[   161  159  147  421  ]]  Ctrl
- 147 inc      160  145            [[   146       ]]  int
- 161 ld8      160  150  326  145  [[   159       ]]  int
- 421 jmp      160                 [[   122       ]]
- 159 st8      160  150  154  155  161  [[   152  ]]  #3:Bot
-
-L123:                             [[  L127       ]]
- 123 True     126                 [[   157  153  140  122  ]]  Ctrl
- 140 inc      123  137            [[   139       ]]  int
- 157 ld8      123  150  326  137  [[   153       ]]  int
- 153 st8      123  150  154  155  157  [[   152  ]]  #3:Bot
-
-L122:                             [[  L123      L160       ]]
- 122 Region  ____  123  421       [[   139  146  152  156  420  ]]  Ctrl
- 139 Phi_i    122  140  137       [[   137       ]]  int
- 146 Phi_j    122  145  147       [[   145       ]]  int
- 152 Phi_$3   122  153  159       [[   150       ]]  #3:Bot
- 156 inc      122  155            [[   155       ]]  int
-
-L1:                               [[  L118      L67       L186       ]]
-   1 Stop       2   51   89  117   66  185  [[   ]]  Bot
-
-
-
 ---split_merge { *![int] int int *![int] -> 0 #0}---------------------------
 0000 4883EC38    subi  rsp -= #56
                  a:r9,b:rcx,begin:rdx,end:r8

From 7144c7d766b4f3ac344912a05ef45020ce938860 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Sat, 3 May 2025 11:51:26 +0100
Subject: [PATCH 070/119] For test cases where comparing Simple vs EeZee - best
 to have as much source code compatibility as possible

WIP add support for array len and init value in new array expression

WIP add support for array len and init value in new array expression

WIP fix for incremental SSA
---
 .../ezlang/compiler/CompiledFunction.java     | 34 ++++++-
 .../ezlang/compiler/Instruction.java          | 20 +++-
 .../ezlang/interpreter/Interpreter.java       | 14 ++-
 .../ezlang/interpreter/Value.java             |  5 +-
 .../ezlang/compiler/TestCompiler.java         |  4 +-
 .../ezlang/compiler/TestIncrementalSSA.java   | 50 ++++++++++
 .../ezlang/compiler/TestSSATransform.java     | 96 +++++++++++++++++--
 .../ezlang/parser/AST.java                    | 21 ++--
 .../ezlang/parser/Parser.java                 | 14 ++-
 .../ezlang/parser/TestParser.java             |  2 +-
 .../ezlang/compiler/CompiledFunction.java     | 39 +++++++-
 .../ezlang/compiler/Instruction.java          | 20 +++-
 .../ezlang/interpreter/Interpreter.java       | 14 ++-
 .../ezlang/interpreter/Value.java             |  5 +-
 .../ezlang/compiler/TestCompiler.java         | 87 ++++++++++++++++-
 .../ezlang/compiler/Compiler.java             |  2 +-
 seaofnodes/src/test/cases/mergsort/sort.ez    |  5 +-
 seaofnodes/src/test/cases/mergsort/sort.smp   | 23 +++--
 .../ezlang/semantic/SemaAssignTypes.java      | 42 +++++++-
 .../ezlang/types/Type.java                    |  2 +-
 20 files changed, 447 insertions(+), 52 deletions(-)

diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
index 48526e6..ae19644 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
@@ -426,7 +426,7 @@ private boolean compileArrayStoreExpr(AST.ArrayStoreExpr arrayStoreExpr) {
     }
 
     private boolean compileNewExpr(AST.NewExpr newExpr) {
-        codeNew(newExpr.type);
+        codeNew(newExpr.type,newExpr.len,newExpr.initValue);
         return false;
     }
 
@@ -644,20 +644,44 @@ else if (indexed instanceof Operand.LoadFieldOperand loadFieldOperand)
             codeMove(value, indexed);
     }
 
-    private void codeNew(Type type) {
+    private void codeNew(Type type, AST.Expr len, AST.Expr initVal) {
         if (type instanceof Type.TypeArray typeArray)
-            codeNewArray(typeArray);
+            codeNewArray(typeArray, len, initVal);
         else if (type instanceof Type.TypeStruct typeStruct)
             codeNewStruct(typeStruct);
         else
             throw new CompilerException("Unexpected type: " + type);
     }
 
-    private void codeNewArray(Type.TypeArray typeArray) {
+    private void codeNewArray(Type.TypeArray typeArray, AST.Expr len, AST.Expr initVal) {
         var temp = createTemp(typeArray);
+        Operand lenOperand = null;
+        Operand initValOperand = null;
+        if (len != null) {
+            boolean indexed = compileExpr(len);
+            if (indexed)
+                codeIndexedLoad();
+            if (initVal != null) {
+                indexed = compileExpr(initVal);
+                if (indexed)
+                    codeIndexedLoad();
+                initValOperand = pop();
+            }
+            lenOperand = pop();
+        }
+        Instruction insn;
         var target = (Operand.RegisterOperand) issa.write(temp);
-        var insn = new Instruction.NewArray(typeArray, target);
+        if (lenOperand != null) {
+            if (initValOperand != null)
+                insn = new Instruction.NewArray(typeArray, target, issa.read(lenOperand), issa.read(initValOperand));
+            else
+                insn = new Instruction.NewArray(typeArray, target, issa.read(lenOperand));
+        }
+        else
+            insn = new Instruction.NewArray(typeArray, target);
         issa.recordDef(target, insn);
+        if (lenOperand != null) issa.recordUse(lenOperand,insn);
+        if (initValOperand != null) issa.recordUse(initValOperand,insn);
         code(insn);
     }
 
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java
index 0ab1203..5baf003 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java
@@ -124,14 +124,28 @@ public NewArray(Type.TypeArray type, Operand.RegisterOperand destOperand) {
             super(I_NEW_ARRAY, destOperand);
             this.type = type;
         }
+        public NewArray(Type.TypeArray type, Operand.RegisterOperand destOperand, Operand len) {
+            super(I_NEW_ARRAY, destOperand, len);
+            this.type = type;
+        }
+        public NewArray(Type.TypeArray type, Operand.RegisterOperand destOperand, Operand len, Operand initValue) {
+            super(I_NEW_ARRAY, destOperand, len, initValue);
+            this.type = type;
+        }
+        public Operand len() { return uses.length > 0 ? uses[0] : null; }
+        public Operand initValue() { return uses.length > 1 ? uses[1] : null; }
         public Operand.RegisterOperand destOperand() { return def; }
         @Override
         public StringBuilder toStr(StringBuilder sb) {
-            return sb.append(def)
+            sb.append(def)
                     .append(" = ")
                     .append("New(")
-                    .append(type)
-                    .append(")");
+                    .append(type);
+            if (len() != null)
+                sb.append(", len=").append(len());
+            if (initValue() != null)
+                sb.append(", initValue=").append(initValue());
+            return sb.append(")");
         }
     }
 
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/interpreter/Interpreter.java b/optvm/src/main/java/com/compilerprogramming/ezlang/interpreter/Interpreter.java
index c85fa24..654d2df 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/interpreter/Interpreter.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/interpreter/Interpreter.java
@@ -200,7 +200,19 @@ else if (binaryInst.right() instanceof Operand.RegisterOperand registerOperand)
                     }
                 }
                 case Instruction.NewArray newArrayInst -> {
-                    execStack.stack[base + newArrayInst.destOperand().frameSlot()] = new Value.ArrayValue(newArrayInst.type);
+                    long size = 0;
+                    Value initValue = null;
+                    if (newArrayInst.len() instanceof Operand.ConstantOperand constantOperand)
+                        size = constantOperand.value;
+                    else if (newArrayInst.len() instanceof Operand.RegisterOperand registerOperand) {
+                        Value.IntegerValue indexValue = (Value.IntegerValue) execStack.stack[base + registerOperand.frameSlot()];
+                        size = (long) indexValue.value;
+                    }
+                    if (newArrayInst.initValue() instanceof Operand.ConstantOperand constantOperand)
+                        initValue = new Value.IntegerValue(constantOperand.value);
+                    else if (newArrayInst.initValue() instanceof Operand.RegisterOperand registerOperand)
+                        initValue = execStack.stack[base + registerOperand.frameSlot()];
+                    execStack.stack[base + newArrayInst.destOperand().frameSlot()] = new Value.ArrayValue(newArrayInst.type, size, initValue);
                 }
                 case Instruction.NewStruct newStructInst -> {
                     execStack.stack[base + newStructInst.destOperand().frameSlot()] = new Value.StructValue(newStructInst.type);
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/interpreter/Value.java b/optvm/src/main/java/com/compilerprogramming/ezlang/interpreter/Value.java
index 7f914a6..6717daa 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/interpreter/Value.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/interpreter/Value.java
@@ -17,9 +17,12 @@ public NullValue() {}
     static public class ArrayValue extends Value {
         public final Type.TypeArray arrayType;
         public final ArrayList<Value> values;
-        public ArrayValue(Type.TypeArray arrayType) {
+        public ArrayValue(Type.TypeArray arrayType, long len, Value initValue) {
             this.arrayType = arrayType;
             values = new ArrayList<>();
+            for (long i = 0; i < len; i++) {
+                values.add(initValue);
+            }
         }
     }
     static public class StructValue extends Value {
diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestCompiler.java b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestCompiler.java
index b6873ec..11b46eb 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestCompiler.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestCompiler.java
@@ -214,7 +214,7 @@ func foo()->[Int] {
         String result = compileSrc(src);
         Assert.assertEquals("""
 L0:
-    %t0 = New([Int])
+    %t0 = New([Int], len=3)
     %t0[0] = 1
     %t0[1] = 2
     %t0[2] = 3
@@ -235,7 +235,7 @@ func foo(n: Int) -> [Int] {
         Assert.assertEquals("""
 L0:
     arg n
-    %t1 = New([Int])
+    %t1 = New([Int], len=1)
     %t1[0] = n
     ret %t1
     goto  L1
diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestIncrementalSSA.java b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestIncrementalSSA.java
index e4f9b93..9dbca1a 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestIncrementalSSA.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestIncrementalSSA.java
@@ -667,6 +667,56 @@ func merge(begin: Int,middle: Int,end: Int)
 L3:
     goto  L1
 L1:
+""", result);
+    }
+
+    @Test
+    public void testSSA18()
+    {
+        String src = """
+                func foo(len: Int, val: Int, x: Int, y: Int)->[Int] {
+                    if (x > y) {
+                        len=len+x
+                        val=val+x
+                    }
+                    return new [Int]{len=len,value=val} 
+                }
+                """;
+        String result = compileSrc(src);
+        Assert.assertEquals("""
+func foo(len: Int,val: Int,x: Int,y: Int)->[Int]
+Reg #0 len 0
+Reg #1 val 1
+Reg #2 x 2
+Reg #3 y 3
+Reg #4 %t4 4
+Reg #5 %t5 5
+Reg #6 len_1 0
+Reg #7 %t7 7
+Reg #8 val_1 1
+Reg #9 %t9 9
+Reg #10 len_2 0
+Reg #11 val_2 1
+L0:
+    arg len
+    arg val
+    arg x
+    arg y
+    %t4 = x>y
+    if %t4 goto L2 else goto L3
+L2:
+    %t5 = len+x
+    len_1 = %t5
+    %t7 = val+x
+    val_1 = %t7
+    goto  L3
+L3:
+    val_2 = phi(val, val_1)
+    len_2 = phi(len, len_1)
+    %t9 = New([Int], len=len_2, initValue=val_2)
+    ret %t9
+    goto  L1
+L1:
 """, result);
     }
 
diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
index c8b5d6a..bc38276 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
@@ -1662,7 +1662,7 @@ func foo()->Int {
 Before SSA
 ==========
 L0:
-    %t2 = New([Int])
+    %t2 = New([Int], len=2)
     %t2[0] = 1
     %t2[1] = 2
     arr = %t2
@@ -1675,7 +1675,7 @@ func foo()->Int {
 After SSA
 =========
 L0:
-    %t2_0 = New([Int])
+    %t2_0 = New([Int], len=2)
     %t2_0[0] = 1
     %t2_0[1] = 2
     arr_0 = %t2_0
@@ -1688,7 +1688,7 @@ func foo()->Int {
 After exiting SSA
 =================
 L0:
-    %t2_0 = New([Int])
+    %t2_0 = New([Int], len=2)
     %t2_0[0] = 1
     %t2_0[1] = 2
     arr_0 = %t2_0
@@ -2781,7 +2781,7 @@ func foo()->Int
 Before SSA
 ==========
 L0:
-    %t1 = New([Foo?])
+    %t1 = New([Foo?], len=2)
     %t2 = New(Foo)
     %t2.i = 1
     %t1[0] = %t2
@@ -2805,7 +2805,7 @@ func foo()->Int
 After SSA
 =========
 L0:
-    %t1_0 = New([Foo?])
+    %t1_0 = New([Foo?], len=2)
     %t2_0 = New(Foo)
     %t2_0.i = 1
     %t1_0[0] = %t2_0
@@ -2830,7 +2830,7 @@ func foo()->Int
 After exiting SSA
 =================
 L0:
-    %t1_0 = New([Foo?])
+    %t1_0 = New([Foo?], len=2)
     %t2_0 = New(Foo)
     %t2_0.i = 1
     %t1_0[0] = %t2_0
@@ -2973,6 +2973,90 @@ func merge(begin: Int, middle: Int, end: Int)
 L3:
     goto  L1
 L1:
+""", result);
+    }
+
+    @Test
+    public void testSSA18()
+    {
+        String src = """
+                func foo(len: Int, val: Int, x: Int, y: Int)->[Int] {
+                    if (x > y) {
+                        len=len+x
+                        val=val+x
+                    }
+                    return new [Int]{len=len,value=val} 
+                }
+                """;
+        String result = compileSrc(src);
+        Assert.assertEquals("""
+func foo
+Before SSA
+==========
+L0:
+    arg len
+    arg val
+    arg x
+    arg y
+    %t4 = x>y
+    if %t4 goto L2 else goto L3
+L2:
+    %t5 = len+x
+    len = %t5
+    %t6 = val+x
+    val = %t6
+    goto  L3
+L3:
+    %t7 = New([Int], len=len, initValue=val)
+    ret %t7
+    goto  L1
+L1:
+After SSA
+=========
+L0:
+    arg len_0
+    arg val_0
+    arg x_0
+    arg y_0
+    %t4_0 = x_0>y_0
+    if %t4_0 goto L2 else goto L3
+L2:
+    %t5_0 = len_0+x_0
+    len_1 = %t5_0
+    %t6_0 = val_0+x_0
+    val_1 = %t6_0
+    goto  L3
+L3:
+    val_2 = phi(val_0, val_1)
+    len_2 = phi(len_0, len_1)
+    %t7_0 = New([Int], len=len_2, initValue=val_2)
+    ret %t7_0
+    goto  L1
+L1:
+After exiting SSA
+=================
+L0:
+    arg len_0
+    arg val_0
+    arg x_0
+    arg y_0
+    %t4_0 = x_0>y_0
+    val_2 = val_0
+    len_2 = len_0
+    if %t4_0 goto L2 else goto L3
+L2:
+    %t5_0 = len_0+x_0
+    len_1 = %t5_0
+    %t6_0 = val_0+x_0
+    val_1 = %t6_0
+    val_2 = val_1
+    len_2 = len_1
+    goto  L3
+L3:
+    %t7_0 = New([Int], len=len_2, initValue=val_2)
+    ret %t7_0
+    goto  L1
+L1:
 """, result);
     }
 }
\ No newline at end of file
diff --git a/parser/src/main/java/com/compilerprogramming/ezlang/parser/AST.java b/parser/src/main/java/com/compilerprogramming/ezlang/parser/AST.java
index 46bcf78..162dd29 100644
--- a/parser/src/main/java/com/compilerprogramming/ezlang/parser/AST.java
+++ b/parser/src/main/java/com/compilerprogramming/ezlang/parser/AST.java
@@ -545,14 +545,17 @@ public void accept(ASTVisitor visitor) {
      */
     public static class NewExpr extends Expr {
         public final TypeExpr typeExpr;
-        public final long len; // temp hack, as this needs to be an expression rather than constant
+        public final Expr len;
+        public final Expr initValue;
         public NewExpr(TypeExpr typeExpr) {
             this.typeExpr = typeExpr;
-            this.len = 0;
+            this.len = null;
+            this.initValue = null;
         }
-        public NewExpr(TypeExpr typeExpr, long len) {
+        public NewExpr(TypeExpr typeExpr, Expr len, Expr initValue) {
             this.typeExpr = typeExpr;
             this.len = len;
+            this.initValue = initValue;
         }
         @Override
         public StringBuilder toStr(StringBuilder sb) {
@@ -566,6 +569,11 @@ public void accept(ASTVisitor visitor) {
             if (visitor == null)
                 return;
             typeExpr.accept(visitor);
+            if (len != null) {
+                len.accept(visitor);
+                if (initValue != null)
+                    initValue.accept(visitor);
+            }
             visitor.visit(this, false);
         }
     }
@@ -579,12 +587,7 @@ public static class InitExpr extends Expr {
         public final List<Expr> initExprList;
         public InitExpr(NewExpr newExpr, List<Expr> initExprList) {
             this.initExprList = initExprList;
-            // For arrays we compute length based on number of elements
-            // This is not actually correct - see https://github.com/CompilerProgramming/ez-lang/issues/47
-            if (initExprList.size() != newExpr.len)
-                this.newExpr = new NewExpr(newExpr.typeExpr,initExprList.size());
-            else
-                this.newExpr = newExpr;
+            this.newExpr = newExpr;
         }
         @Override
         public StringBuilder toStr(StringBuilder sb) {
diff --git a/parser/src/main/java/com/compilerprogramming/ezlang/parser/Parser.java b/parser/src/main/java/com/compilerprogramming/ezlang/parser/Parser.java
index 821ce6c..423040b 100644
--- a/parser/src/main/java/com/compilerprogramming/ezlang/parser/Parser.java
+++ b/parser/src/main/java/com/compilerprogramming/ezlang/parser/Parser.java
@@ -333,6 +333,8 @@ private AST.Expr parseNew(Lexer lexer) {
         matchIdentifier(lexer, "new");
         AST.TypeExpr resultType = parseTypeExpr(lexer);
         var newExpr = new AST.NewExpr(resultType);
+        AST.Expr lenExpr = null;
+        AST.Expr initValueExpr = null;
         List<AST.Expr> initExpr = new ArrayList<>();
         int index = 0;
         if (testPunctuation(lexer, "{")) {
@@ -343,10 +345,14 @@ private AST.Expr parseNew(Lexer lexer) {
                     matchPunctuation(lexer, "=");
                     AST.Expr value = parseBool(lexer);
                     initExpr.add(new AST.InitFieldExpr(newExpr, fieldname, value));
+                    if (fieldname.equals("len"))
+                        lenExpr = value;
+                    else if (fieldname.equals("value"))
+                        initValueExpr = value;
                 }
                 else {
                     var indexLit = Integer.valueOf(index++);
-                    var indexExpr = new AST.LiteralExpr(Token.newNum(indexLit,indexLit.toString(),0));
+                    var indexExpr = new AST.LiteralExpr(Token.newNum(indexLit,indexLit.toString(),currentToken.lineNumber));
                     initExpr.add(new AST.ArrayInitExpr(newExpr, indexExpr, parseBool(lexer)));
                 }
                 if (isToken(currentToken, ","))
@@ -355,6 +361,12 @@ private AST.Expr parseNew(Lexer lexer) {
             }
         }
         matchPunctuation(lexer, "}");
+        if (initExpr.size() > 0 && lenExpr == null) {
+            var sizeLit = Integer.valueOf(initExpr.size());
+            lenExpr = new AST.LiteralExpr(Token.newNum(sizeLit,sizeLit.toString(),currentToken.lineNumber));
+        }
+        if (lenExpr != null)
+            return new AST.InitExpr(new AST.NewExpr(newExpr.typeExpr, lenExpr, initValueExpr), initExpr);
         return new AST.InitExpr(newExpr, initExpr);
     }
 
diff --git a/parser/src/test/java/com/compilerprogramming/ezlang/parser/TestParser.java b/parser/src/test/java/com/compilerprogramming/ezlang/parser/TestParser.java
index 1c60dc0..b4966c3 100644
--- a/parser/src/test/java/com/compilerprogramming/ezlang/parser/TestParser.java
+++ b/parser/src/test/java/com/compilerprogramming/ezlang/parser/TestParser.java
@@ -36,7 +36,7 @@ func bar() -> Test {
 func main() {
     var m = 42
     var t: Tree
-    var array = new [Int] {size=10,1,2,3}
+    var array = new [Int] {len=10,1,2,3}
     array[1] = 42
     t.left = null
     if (m < 1)
diff --git a/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java b/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
index 9433d5c..3071909 100644
--- a/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
+++ b/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
@@ -383,7 +383,7 @@ private boolean compileArrayStoreExpr(AST.ArrayStoreExpr arrayStoreExpr) {
     }
 
     private boolean compileNewExpr(AST.NewExpr newExpr) {
-        codeNew(newExpr.type);
+        codeNew(newExpr.type,newExpr.len,newExpr.initValue);
         return false;
     }
 
@@ -603,13 +603,42 @@ else if (indexed instanceof Operand.LoadFieldOperand loadFieldOperand)
             code(new Instruction.Move(value, indexed));
     }
 
-    private void codeNew(Type type) {
-        var temp = createTemp(type);
+    private void codeNew(Type type, AST.Expr len, AST.Expr initVal) {
         if (type instanceof Type.TypeArray typeArray)
-            code(new Instruction.NewArray(typeArray, temp));
-        else if (type instanceof Type.TypeStruct typeStruct)
+            codeNewArray(typeArray, len, initVal);
+        else if (type instanceof Type.TypeStruct typeStruct) {
+            var temp = createTemp(type);
             code(new Instruction.NewStruct(typeStruct, temp));
+        }
         else
             throw new CompilerException("Unexpected type: " + type);
     }
+
+    private void codeNewArray(Type.TypeArray typeArray, AST.Expr len, AST.Expr initVal) {
+        var temp = createTemp(typeArray);
+        Operand lenOperand = null;
+        Operand initValOperand = null;
+        if (len != null) {
+            boolean indexed = compileExpr(len);
+            if (indexed)
+                codeIndexedLoad();
+            if (initVal != null) {
+                indexed = compileExpr(initVal);
+                if (indexed)
+                    codeIndexedLoad();
+                initValOperand = pop();
+            }
+            lenOperand = pop();
+        }
+        Instruction insn;
+        if (lenOperand != null) {
+            if (initValOperand != null)
+                insn = new Instruction.NewArray(typeArray, temp, lenOperand, initValOperand);
+            else
+                insn = new Instruction.NewArray(typeArray, temp, lenOperand);
+        }
+        else
+            insn = new Instruction.NewArray(typeArray, temp);
+        code(insn);
+    }
 }
diff --git a/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java b/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java
index a80169a..73b5673 100644
--- a/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java
+++ b/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java
@@ -62,14 +62,28 @@ public NewArray(Type.TypeArray type, Operand.RegisterOperand destOperand) {
             super(I_NEW_ARRAY, destOperand);
             this.type = type;
         }
+        public NewArray(Type.TypeArray type, Operand.RegisterOperand destOperand, Operand len) {
+            super(I_NEW_ARRAY, destOperand, len);
+            this.type = type;
+        }
+        public NewArray(Type.TypeArray type, Operand.RegisterOperand destOperand, Operand len, Operand initValue) {
+            super(I_NEW_ARRAY, destOperand, len, initValue);
+            this.type = type;
+        }
+        public Operand len() { return uses.length > 0 ? uses[0] : null; }
+        public Operand initValue() { return uses.length > 1 ? uses[1] : null; }
         public Operand.RegisterOperand destOperand() { return def; }
         @Override
         public StringBuilder toStr(StringBuilder sb) {
-            return sb.append(def)
+            sb.append(def)
                     .append(" = ")
                     .append("New(")
-                    .append(type)
-                    .append(")");
+                    .append(type);
+            if (len() != null)
+                sb.append(", len=").append(len());
+            if (initValue() != null)
+                sb.append(", initValue=").append(initValue());
+            return sb.append(")");
         }
     }
 
diff --git a/registervm/src/main/java/com/compilerprogramming/ezlang/interpreter/Interpreter.java b/registervm/src/main/java/com/compilerprogramming/ezlang/interpreter/Interpreter.java
index 919a82e..f441455 100644
--- a/registervm/src/main/java/com/compilerprogramming/ezlang/interpreter/Interpreter.java
+++ b/registervm/src/main/java/com/compilerprogramming/ezlang/interpreter/Interpreter.java
@@ -200,7 +200,19 @@ else if (binaryInst.right() instanceof Operand.RegisterOperand registerOperand)
                     }
                 }
                 case Instruction.NewArray newArrayInst -> {
-                    execStack.stack[base + newArrayInst.destOperand().frameSlot()] = new Value.ArrayValue(newArrayInst.type);
+                    long size = 0;
+                    Value initValue = null;
+                    if (newArrayInst.len() instanceof Operand.ConstantOperand constantOperand)
+                        size = constantOperand.value;
+                    else if (newArrayInst.len() instanceof Operand.RegisterOperand registerOperand) {
+                        Value.IntegerValue indexValue = (Value.IntegerValue) execStack.stack[base + registerOperand.frameSlot()];
+                        size = (long) indexValue.value;
+                    }
+                    if (newArrayInst.initValue() instanceof Operand.ConstantOperand constantOperand)
+                        initValue = new Value.IntegerValue(constantOperand.value);
+                    else if (newArrayInst.initValue() instanceof Operand.RegisterOperand registerOperand)
+                        initValue = execStack.stack[base + registerOperand.frameSlot()];
+                    execStack.stack[base + newArrayInst.destOperand().frameSlot()] = new Value.ArrayValue(newArrayInst.type, size, initValue);
                 }
                 case Instruction.NewStruct newStructInst -> {
                     execStack.stack[base + newStructInst.destOperand().frameSlot()] = new Value.StructValue(newStructInst.type);
diff --git a/registervm/src/main/java/com/compilerprogramming/ezlang/interpreter/Value.java b/registervm/src/main/java/com/compilerprogramming/ezlang/interpreter/Value.java
index 7f914a6..6717daa 100644
--- a/registervm/src/main/java/com/compilerprogramming/ezlang/interpreter/Value.java
+++ b/registervm/src/main/java/com/compilerprogramming/ezlang/interpreter/Value.java
@@ -17,9 +17,12 @@ public NullValue() {}
     static public class ArrayValue extends Value {
         public final Type.TypeArray arrayType;
         public final ArrayList<Value> values;
-        public ArrayValue(Type.TypeArray arrayType) {
+        public ArrayValue(Type.TypeArray arrayType, long len, Value initValue) {
             this.arrayType = arrayType;
             values = new ArrayList<>();
+            for (long i = 0; i < len; i++) {
+                values.add(initValue);
+            }
         }
     }
     static public class StructValue extends Value {
diff --git a/registervm/src/test/java/com/compilerprogramming/ezlang/compiler/TestCompiler.java b/registervm/src/test/java/com/compilerprogramming/ezlang/compiler/TestCompiler.java
index 142b8b6..1461658 100644
--- a/registervm/src/test/java/com/compilerprogramming/ezlang/compiler/TestCompiler.java
+++ b/registervm/src/test/java/com/compilerprogramming/ezlang/compiler/TestCompiler.java
@@ -203,7 +203,7 @@ func foo()->[Int] {
         String result = compileSrc(src);
         Assert.assertEquals("""
                 L0:
-                    %t0 = New([Int])
+                    %t0 = New([Int], len=3)
                     %t0[0] = 1
                     %t0[1] = 2
                     %t0[2] = 3
@@ -223,7 +223,7 @@ func foo(n: Int) -> [Int] {
         String result = compileSrc(src);
         Assert.assertEquals("""
                 L0:
-                    %t1 = New([Int])
+                    %t1 = New([Int], len=1)
                     %t1[0] = n
                     ret %t1
                     goto  L1
@@ -577,6 +577,89 @@ public void testFunction29() {
     ret null
     goto  L1
 L1:
+""", result);
+    }
+
+    @Test
+    public void testFunction30() {
+        String src = """
+                func foo()->[Int] { 
+                    return new [Int]{len=10,value=0} 
+                }
+                """;
+        String result = compileSrc(src);
+        Assert.assertEquals("""
+L0:
+    %t0 = New([Int], len=10, initValue=0)
+    ret %t0
+    goto  L1
+L1:
+""", result);
+    }
+
+    @Test
+    public void testFunction31() {
+        String src = """
+                func foo(len: Int, val: Int)->[Int] { 
+                    return new [Int]{len=len,value=val} 
+                }
+                """;
+        String result = compileSrc(src);
+        Assert.assertEquals("""
+L0:
+    %t2 = New([Int], len=len, initValue=val)
+    ret %t2
+    goto  L1
+L1:
+""", result);
+    }
+
+    @Test
+    public void testFunction32() {
+        String src = """
+                func foo(len: Int, val: Int, x: Int)->[Int] { 
+                    return new [Int]{len=len+x,value=val+x} 
+                }
+                """;
+        String result = compileSrc(src);
+        Assert.assertEquals("""
+L0:
+    %t4 = len+x
+    %t5 = val+x
+    %t3 = New([Int], len=%t4, initValue=%t5)
+    ret %t3
+    goto  L1
+L1:
+""", result);
+    }
+
+    @Test
+    public void testFunction33() {
+        String src = """
+                func foo(len: Int, val: Int, x: Int, y: Int)->[Int] {
+                    if (x > y) {
+                        len=len+x
+                        val=val+x
+                    }
+                    return new [Int]{len=len,value=val} 
+                }
+                """;
+        String result = compileSrc(src);
+        Assert.assertEquals("""
+L0:
+    %t4 = x>y
+    if %t4 goto L2 else goto L3
+L2:
+    %t4 = len+x
+    len = %t4
+    %t4 = val+x
+    val = %t4
+    goto  L3
+L3:
+    %t4 = New([Int], len=len, initValue=val)
+    ret %t4
+    goto  L1
+L1:
 """, result);
     }
 
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java
index 46805d5..3d7e546 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java
@@ -554,7 +554,7 @@ private Node compileNewExpr(AST.NewExpr newExpr) {
         Type type = newExpr.type;
         if (type instanceof Type.TypeArray typeArray) {
             SONTypeMemPtr tarray = (SONTypeMemPtr) TYPES.get(typeArray.name());
-            return newArray(tarray._obj,newExpr.len==0?ZERO:con(newExpr.len));
+            return newArray(tarray._obj,newExpr.len==null?ZERO:compileExpr(newExpr.len));
         }
         else if (type instanceof Type.TypeStruct typeStruct) {
             SONTypeMemPtr tptr = (SONTypeMemPtr) TYPES.get(typeStruct.name());
diff --git a/seaofnodes/src/test/cases/mergsort/sort.ez b/seaofnodes/src/test/cases/mergsort/sort.ez
index 8ad506d..b5a5ae4 100644
--- a/seaofnodes/src/test/cases/mergsort/sort.ez
+++ b/seaofnodes/src/test/cases/mergsort/sort.ez
@@ -6,14 +6,15 @@ func merge_sort(a: [Int], b: [Int], n: Int)
     split_merge(a, 0, n, b)
 }
 
-func split_merge(b: [Int], begin: Int, end: Int, a: [Int])
+func split_merge(b: [Int], begin: Int, end: Int, a: [Int])->Int
 {
     if (end - begin <= 1)
-        return;
+        return 0
     var middle = (end + begin) / 2
     split_merge(a, begin, middle, b)
     split_merge(a, middle, end, b)
     merge(b, begin, middle, end, a)
+    return 0
 }
 
 func merge(b: [Int], begin: Int, middle: Int, end: Int, a: [Int])
diff --git a/seaofnodes/src/test/cases/mergsort/sort.smp b/seaofnodes/src/test/cases/mergsort/sort.smp
index b82b3f4..d0aeaa4 100644
--- a/seaofnodes/src/test/cases/mergsort/sort.smp
+++ b/seaofnodes/src/test/cases/mergsort/sort.smp
@@ -17,23 +17,34 @@ val split_merge = { int[] b, int begin, int end, int[] a ->
 };
 
 val merge = { int[] b, int begin, int middle, int end, int[] a ->
-    int i = begin, j = middle;
-
-    for (int k = begin; k < end; k++) {
+    int i = begin;
+    int j = middle;
+    int k = begin;
+    while (k < end) {
         // && and ||
         bool cond = false;
         if (i < middle) {
             if (j >= end)          cond = true;
             else if (a[i] <= a[j]) cond = true;
         }
-        if (cond) b[k] = a[i++];
-        else      b[k] = a[j++];
+        if (cond) {
+            b[k] = a[i];
+            i = i + 1;
+        }
+        else {
+            b[k] = a[j];
+            j = j + 1;
+        }
+        k = k + 1;
     }
 };
 
 val copy_array = { int[] a, int begin, int end, int[] b ->
-    for (int k = begin; k < end; k++)
+    int k = begin;
+    while (k < end) {
         b[k] = a[k];
+        k = k + 1;
+    }
 };
 
 val eq = { int[] a, int[] b, int n ->
diff --git a/semantic/src/main/java/com/compilerprogramming/ezlang/semantic/SemaAssignTypes.java b/semantic/src/main/java/com/compilerprogramming/ezlang/semantic/SemaAssignTypes.java
index 0fa930d..743ac68 100644
--- a/semantic/src/main/java/com/compilerprogramming/ezlang/semantic/SemaAssignTypes.java
+++ b/semantic/src/main/java/com/compilerprogramming/ezlang/semantic/SemaAssignTypes.java
@@ -67,6 +67,8 @@ public ASTVisitor visit(AST.VarDecl varDecl, boolean enter) {
     @Override
     public ASTVisitor visit(AST.BinaryExpr binaryExpr, boolean enter) {
         if (!enter) {
+            if (binaryExpr.type != null)
+                return this;
             validType(binaryExpr.expr1.type, true);
             validType(binaryExpr.expr2.type, true);
             if (binaryExpr.expr1.type instanceof Type.TypeInteger &&
@@ -92,7 +94,7 @@ else if (((binaryExpr.expr1.type instanceof Type.TypeNull &&
 
     @Override
     public ASTVisitor visit(AST.UnaryExpr unaryExpr, boolean enter) {
-        if (enter) {
+        if (enter || unaryExpr.type != null) {
             return this;
         }
         validType(unaryExpr.expr.type, false);
@@ -109,6 +111,8 @@ public ASTVisitor visit(AST.UnaryExpr unaryExpr, boolean enter) {
     public ASTVisitor visit(AST.GetFieldExpr fieldExpr, boolean enter) {
         if (enter)
             return this;
+        if (fieldExpr.type != null)
+            return this;
         validType(fieldExpr.object.type, false);
         Type.TypeStruct structType = null;
         if (fieldExpr.object.type instanceof Type.TypeStruct ts) {
@@ -131,6 +135,8 @@ else if (fieldExpr.object.type instanceof Type.TypeNullable ptr &&
     public ASTVisitor visit(AST.SetFieldExpr fieldExpr, boolean enter) {
         if (enter)
             return this;
+        if (fieldExpr.type != null)
+            return this;
         validType(fieldExpr.object.type, true);
         Type.TypeStruct structType = null;
         if (fieldExpr.object.type instanceof Type.TypeStruct ts) {
@@ -140,6 +146,16 @@ else if (fieldExpr.object.type instanceof Type.TypeNullable ptr &&
                 ptr.baseType instanceof Type.TypeStruct ts) {
             structType = ts;
         }
+        else if (fieldExpr.object.type instanceof Type.TypeArray typeArray) {
+            if (fieldExpr.fieldName.equals("len"))
+                checkAssignmentCompatible(typeDictionary.INT,fieldExpr.value.type);
+            else if (fieldExpr.fieldName.equals("value"))
+                checkAssignmentCompatible(typeArray.getElementType(),fieldExpr.value.type);
+            else
+                throw new CompilerException("Unexpected array initializer " + fieldExpr.fieldName);
+            fieldExpr.type = fieldExpr.value.type;
+            return this;
+        }
         else
             throw new CompilerException("Unexpected struct type " + fieldExpr.object.type);
         var fieldType = structType.getField(fieldExpr.fieldName);
@@ -155,6 +171,8 @@ else if (fieldExpr.object.type instanceof Type.TypeNullable ptr &&
     @Override
     public ASTVisitor visit(AST.CallExpr callExpr, boolean enter) {
         if (!enter) {
+            if (callExpr.type != null)
+                return this;
             validType(callExpr.callee.type, false);
             if (callExpr.callee.type instanceof Type.TypeFunction f) {
                 callExpr.type = f.returnType;
@@ -192,6 +210,8 @@ public ASTVisitor visit(AST.ReturnTypeExpr returnTypeExpr, boolean enter) {
     @Override
     public ASTVisitor visit(AST.LiteralExpr literalExpr, boolean enter) {
         if (enter) {
+            if (literalExpr.type != null)
+                return this;
             if (literalExpr.value.kind == Token.Kind.NUM) {
                 literalExpr.type = typeDictionary.INT;
             }
@@ -209,6 +229,8 @@ else if (literalExpr.value.kind == Token.Kind.IDENT
     @Override
     public ASTVisitor visit(AST.ArrayLoadExpr arrayIndexExpr, boolean enter) {
         if (!enter) {
+            if (arrayIndexExpr.type != null)
+                return this;
             validType(arrayIndexExpr.array.type, false);
             Type.TypeArray arrayType = null;
             if (arrayIndexExpr.array.type instanceof Type.TypeArray ta) {
@@ -231,6 +253,8 @@ else if (arrayIndexExpr.array.type instanceof Type.TypeNullable ptr &&
     @Override
     public ASTVisitor visit(AST.ArrayStoreExpr arrayIndexExpr, boolean enter) {
         if (!enter) {
+            if (arrayIndexExpr.type != null)
+                return this;
             validType(arrayIndexExpr.array.type, false);
             Type.TypeArray arrayType = null;
             if (arrayIndexExpr.array.type instanceof Type.TypeArray ta) {
@@ -256,6 +280,8 @@ else if (arrayIndexExpr.array.type instanceof Type.TypeNullable ptr &&
     public ASTVisitor visit(AST.NewExpr newExpr, boolean enter) {
         if (enter)
             return this;
+        if (newExpr.type != null)
+            return this;
         if (newExpr.typeExpr.type == null)
             throw new CompilerException("Unresolved type in new expression");
         validType(newExpr.typeExpr.type, false);
@@ -266,6 +292,14 @@ public ASTVisitor visit(AST.NewExpr newExpr, boolean enter) {
         }
         else if (newExpr.typeExpr.type instanceof Type.TypeArray arrayType) {
             newExpr.type = newExpr.typeExpr.type;
+            if (newExpr.len != null) {
+                if (!(newExpr.len.type instanceof Type.TypeInteger))
+                    throw new CompilerException("Array len must be integer type");
+                if (newExpr.initValue != null) {
+                    if (!arrayType.getElementType().isAssignable(newExpr.initValue.type))
+                        throw new CompilerException("Array init value must be assignable to array element type");
+                }
+            }
         }
         else
             throw new CompilerException("Unsupported type in new expression");
@@ -278,6 +312,8 @@ public ASTVisitor visit(AST.InitExpr initExpr, boolean enter) {
             return this;
         if (initExpr.newExpr.type == null)
             throw new CompilerException("Unresolved type in new expression");
+        if (initExpr.type != null)
+            return this;
         validType(initExpr.newExpr.type, false);
         if (initExpr.newExpr.type instanceof Type.TypeNullable)
             throw new CompilerException("new cannot be used to create a Nullable type");
@@ -290,6 +326,8 @@ public ASTVisitor visit(AST.InitExpr initExpr, boolean enter) {
             }
         }
         else if (initExpr.newExpr.type instanceof Type.TypeArray arrayType) {
+            if (initExpr.initExprList.size() > 0)
+                initExpr.initExprList.removeIf(e->e instanceof AST.InitFieldExpr);
             for (AST.Expr expr: initExpr.initExprList) {
                 checkAssignmentCompatible(arrayType.getElementType(), expr.type);
             }
@@ -304,6 +342,8 @@ else if (initExpr.newExpr.type instanceof Type.TypeArray arrayType) {
     public ASTVisitor visit(AST.NameExpr nameExpr, boolean enter) {
         if (!enter)
             return this;
+        if (nameExpr.type != null)
+            return this;
         var symbol = currentScope.lookup(nameExpr.name);
         if (symbol == null) {
             throw new CompilerException("Unknown symbol " + nameExpr.name);
diff --git a/types/src/main/java/com/compilerprogramming/ezlang/types/Type.java b/types/src/main/java/com/compilerprogramming/ezlang/types/Type.java
index 05cbca6..1c8514c 100644
--- a/types/src/main/java/com/compilerprogramming/ezlang/types/Type.java
+++ b/types/src/main/java/com/compilerprogramming/ezlang/types/Type.java
@@ -53,7 +53,7 @@ public String toString() {
     public String name() { return name; }
 
     public boolean isAssignable(Type other) {
-        if (other instanceof TypeVoid || other instanceof TypeUnknown)
+        if (other == null || other instanceof TypeVoid || other instanceof TypeUnknown)
             return false;
         if (this == other || equals(other)) return true;
         if (this instanceof TypeNullable nullable) {

From dd4fab37e4177deca9da926abf6f57cd6e415062 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Mon, 5 May 2025 20:25:53 +0100
Subject: [PATCH 071/119] WIP simple test case

---
 .../ezlang/interpreter/TestInterpreter.java   | 25 +++++++++++++++++++
 .../ezlang/interpreter/TestInterpreter.java   | 25 +++++++++++++++++++
 2 files changed, 50 insertions(+)

diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java b/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java
index 78af875..b108b38 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java
@@ -693,4 +693,29 @@ func main()->Int
                 integerValue.value == 1);
     }
 
+    @Test
+    public void testFunction108() {
+        String src = """
+                func make(len: Int, val: Int)->[Int]
+                {
+                    return new [Int]{len=len, value=val}
+                }
+                func main()->Int
+                {
+                    var arr = make(3,3);
+                    var i = 0
+                    while (i < 3) {
+                        if (arr[i] != 3)
+                            return 1
+                        i = i + 1
+                    }
+                    return 0
+                }
+                """;
+        var value = compileAndRun(src, "main", Options.OPT);
+        Assert.assertNotNull(value);
+        Assert.assertTrue(value instanceof Value.IntegerValue integerValue &&
+                integerValue.value == 0);
+    }
+
 }
diff --git a/registervm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java b/registervm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java
index 49f8450..ee70826 100644
--- a/registervm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java
+++ b/registervm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java
@@ -332,4 +332,29 @@ func main()->Int
         Assert.assertTrue(value instanceof Value.IntegerValue integerValue &&
                 integerValue.value == 1);
     }
+
+    @Test
+    public void testFunction108() {
+        String src = """
+                func make(len: Int, val: Int)->[Int]
+                {
+                    return new [Int]{len=len, value=val}
+                }
+                func main()->Int
+                {
+                    var arr = make(3,3);
+                    var i = 0
+                    while (i < 3) {
+                        if (arr[i] != 3)
+                            return 1
+                        i = i + 1
+                    }
+                    return 0
+                }
+                """;
+        var value = compileAndRun(src, "main");
+        Assert.assertNotNull(value);
+        Assert.assertTrue(value instanceof Value.IntegerValue integerValue &&
+                integerValue.value == 0);
+    }
 }

From c7afd554fdaf1b08a3211adab0da825c82d1ccf0 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Mon, 5 May 2025 20:39:30 +0100
Subject: [PATCH 072/119] WIP original

---
 seaofnodes/src/test/cases/sieve/sieve.smp | 31 +++++++++++++++++++++++
 1 file changed, 31 insertions(+)
 create mode 100644 seaofnodes/src/test/cases/sieve/sieve.smp

diff --git a/seaofnodes/src/test/cases/sieve/sieve.smp b/seaofnodes/src/test/cases/sieve/sieve.smp
new file mode 100644
index 0000000..f0509a8
--- /dev/null
+++ b/seaofnodes/src/test/cases/sieve/sieve.smp
@@ -0,0 +1,31 @@
+// -*- mode: java;  -*-
+val sieve = { int N ->
+    // The main Sieve array
+    bool[] !ary = new bool[N];
+    // The primes less than N
+    u32[] !primes = new u32[N>>1];
+    // Number of primes so far, searching at index p
+    int nprimes = 0, p=2;
+    // Find primes while p^2 < N
+    while( p*p < N ) {
+        // 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;
+        p++;
+    }
+
+    // Now just collect the remaining primes, no more marking
+    for( ; p < N; p++ )
+        if( !ary[p] )
+            primes[nprimes++] = p;
+
+    // Copy/shrink the result array
+    u32[] !rez = new u32[nprimes];
+    for( int j=0; j < nprimes; j++ )
+        rez[j] = primes[j];
+    return rez;
+};
\ No newline at end of file

From 2982f5cbc20cbcffc0f7904927923f5176d3dba5 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Mon, 5 May 2025 22:05:28 +0100
Subject: [PATCH 073/119] WIP sieve test case - unfortunately broken in optvm

---
 .../ezlang/interpreter/TestInterpreter.java   | 76 ++++++++++++++++++
 .../ezlang/interpreter/TestInterpreter.java   | 77 +++++++++++++++++++
 seaofnodes/src/test/cases/sieve/sieve.ez      | 67 ++++++++++++++++
 3 files changed, 220 insertions(+)
 create mode 100644 seaofnodes/src/test/cases/sieve/sieve.ez

diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java b/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java
index b108b38..456d232 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java
@@ -718,4 +718,80 @@ func main()->Int
                 integerValue.value == 0);
     }
 
+    @Test
+    public void testFunction109() {
+        String src = """
+func sieve(N: Int)->[Int]
+{
+    // The main Sieve array
+    var ary = new [Int]{len=N,value=0}
+    // The primes less than N
+    var primes = new [Int]{len=N/2,value=0}
+    // Number of primes so far, searching at index p
+    var nprimes = 0
+    var p=2
+    // Find primes while p^2 < N
+    while( p*p < N ) {
+        // skip marked non-primes
+        while( ary[p] ) {
+            p = p + 1
+        }
+        // p is now a prime
+        primes[nprimes] = p
+        nprimes = nprimes+1
+        // Mark out the rest non-primes
+        var i = p + p
+        while( i < N ) {
+            ary[i] = 1
+            i = i + p
+        }
+        p = p + 1
+    }
+
+    // Now just collect the remaining primes, no more marking
+    while ( p < N ) {
+        if( !ary[p] ) {
+            primes[nprimes] = p
+            nprimes = nprimes + 1
+        }
+        p = p + 1
+    }
+
+    // Copy/shrink the result array
+    var rez = new [Int]{len=nprimes,value=0}
+    var j = 0
+    while( j < nprimes ) {
+        rez[j] = primes[j]
+        j = j + 1
+    }
+    return rez
+}
+func eq(a: [Int], b: [Int], n: Int)->Int
+{
+    var result = 1
+    var i = 0
+    while (i < n)
+    {
+        if (a[i] != b[i])
+        {
+            result = 0
+            break
+        }
+        i = i + 1
+    }
+    return result
+}
+
+func main()->Int
+{
+    var rez = sieve(20)
+    var expected = new [Int]{2,3,5,7,11,13,17,19}
+    return eq(rez,expected,8)
+}
+""";
+        var value = compileAndRun(src, "main");
+        Assert.assertNotNull(value);
+        Assert.assertTrue(value instanceof Value.IntegerValue integerValue &&
+                integerValue.value == 1);
+    }
 }
diff --git a/registervm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java b/registervm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java
index ee70826..3667caa 100644
--- a/registervm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java
+++ b/registervm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java
@@ -357,4 +357,81 @@ func main()->Int
         Assert.assertTrue(value instanceof Value.IntegerValue integerValue &&
                 integerValue.value == 0);
     }
+
+    @Test
+    public void testFunction109() {
+        String src = """
+func sieve(N: Int)->[Int]
+{
+    // The main Sieve array
+    var ary = new [Int]{len=N,value=0}
+    // The primes less than N
+    var primes = new [Int]{len=N/2,value=0}
+    // Number of primes so far, searching at index p
+    var nprimes = 0
+    var p=2
+    // Find primes while p^2 < N
+    while( p*p < N ) {
+        // skip marked non-primes
+        while( ary[p] ) {
+            p = p + 1
+        }
+        // p is now a prime
+        primes[nprimes] = p
+        nprimes = nprimes+1
+        // Mark out the rest non-primes
+        var i = p + p
+        while( i < N ) {
+            ary[i] = 1
+            i = i + p
+        }
+        p = p + 1
+    }
+
+    // Now just collect the remaining primes, no more marking
+    while ( p < N ) {
+        if( !ary[p] ) {
+            primes[nprimes] = p
+            nprimes = nprimes + 1
+        }
+        p = p + 1
+    }
+
+    // Copy/shrink the result array
+    var rez = new [Int]{len=nprimes,value=0}
+    var j = 0
+    while( j < nprimes ) {
+        rez[j] = primes[j]
+        j = j + 1
+    }
+    return rez
+}
+func eq(a: [Int], b: [Int], n: Int)->Int
+{
+    var result = 1
+    var i = 0
+    while (i < n)
+    {
+        if (a[i] != b[i])
+        {
+            result = 0
+            break
+        }
+        i = i + 1
+    }
+    return result
+}
+
+func main()->Int
+{
+    var rez = sieve(20)
+    var expected = new [Int]{2,3,5,7,11,13,17,19}
+    return eq(rez,expected,8)
+}
+""";
+        var value = compileAndRun(src, "main");
+        Assert.assertNotNull(value);
+        Assert.assertTrue(value instanceof Value.IntegerValue integerValue &&
+                integerValue.value == 1);
+    }
 }
diff --git a/seaofnodes/src/test/cases/sieve/sieve.ez b/seaofnodes/src/test/cases/sieve/sieve.ez
new file mode 100644
index 0000000..8f4aa4a
--- /dev/null
+++ b/seaofnodes/src/test/cases/sieve/sieve.ez
@@ -0,0 +1,67 @@
+func sieve(N: Int)->[Int]
+{
+    // The main Sieve array
+    var ary = new [Int]{len=N,value=0}
+    // The primes less than N
+    var primes = new [Int]{len=N/2,value=0}
+    // Number of primes so far, searching at index p
+    var nprimes = 0
+    var p=2
+    // Find primes while p^2 < N
+    while( p*p < N ) {
+        // skip marked non-primes
+        while( ary[p] ) {
+            p = p + 1
+        }
+        // p is now a prime
+        primes[nprimes] = p
+        nprimes = nprimes+1
+        // Mark out the rest non-primes
+        var i = p + p
+        while( i < N ) {
+            ary[i] = 1
+            i = i + p
+        }
+        p = p + 1
+    }
+
+    // Now just collect the remaining primes, no more marking
+    while ( p < N ) {
+        if( !ary[p] ) {
+            primes[nprimes] = p
+            nprimes = nprimes + 1
+        }
+        p = p + 1
+    }
+
+    // Copy/shrink the result array
+    var rez = new [Int]{len=nprimes,value=0}
+    var j = 0
+    while( j < nprimes ) {
+        rez[j] = primes[j]
+        j = j + 1
+    }
+    return rez
+}
+func eq(a: [Int], b: [Int], n: Int)->Int
+{
+    var result = 1
+    var i = 0
+    while (i < n)
+    {
+        if (a[i] != b[i])
+        {
+            result = 0
+            break
+        }
+        i = i + 1
+    }
+    return result
+}
+
+func main()->Int
+{
+    var rez = sieve(20)
+    var expected = new [Int]{2,3,5,7,11,13,17,19}
+    return eq(rez,expected,8)
+}
\ No newline at end of file

From 1ea7975cd7d193f1cf2f5dc8e95aa91c02d7230e Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Mon, 5 May 2025 22:39:53 +0100
Subject: [PATCH 074/119] WIP trying to repro bug

---
 .../ezlang/compiler/TestIncrementalSSA.java   | 27 +++++++++++++++++
 .../ezlang/compiler/TestSSATransform.java     | 29 +++++++++++++++++++
 2 files changed, 56 insertions(+)

diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestIncrementalSSA.java b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestIncrementalSSA.java
index 9dbca1a..49ba3bf 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestIncrementalSSA.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestIncrementalSSA.java
@@ -719,5 +719,32 @@ func foo(len: Int,val: Int,x: Int,y: Int)->[Int]
 L1:
 """, result);
     }
+    @Test
+    public void testSSA19()
+    {
+        String src = """
+func bug(N: Int)
+{
+    // The main Sieve array
+    //var ary = new [Int]{len=N,value=0}
+    var p=2
+    // Find primes while p^2 < N
+    while( p*p < N ) {
+        // skip marked non-primes
+        //while( ary[p] ) {
+        while (p) {
+            p = p + 1
+        }
+        p = p + 1
+    }
 
+    // Now just collect the remaining primes, no more marking
+    while ( p < N ) {
+        p = p + 1
+    }
+}
+                """;
+        String result = compileSrc(src);
+        System.out.println(result);
+    }
 }
\ No newline at end of file
diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
index bc38276..0649ab2 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
@@ -3059,4 +3059,33 @@ func foo(len: Int, val: Int, x: Int, y: Int)->[Int] {
 L1:
 """, result);
     }
+
+    @Test
+    public void testSSA19()
+    {
+        String src = """
+func bug(N: Int)
+{
+    // The main Sieve array
+    //var ary = new [Int]{len=N,value=0}
+    var p=2
+    // Find primes while p^2 < N
+    while( p*p < N ) {
+        // skip marked non-primes
+        //while( ary[p] ) {
+        while (p) {
+            p = p + 1
+        }
+        p = p + 1
+    }
+
+    // Now just collect the remaining primes, no more marking
+    while ( p < N ) {
+        p = p + 1
+    }
+}
+                """;
+        String result = compileSrc(src);
+        System.out.println(result);
+    }
 }
\ No newline at end of file

From c6e293705a3f290effbbc39c44f42db1b558a84a Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Mon, 5 May 2025 22:59:54 +0100
Subject: [PATCH 075/119] WIP trying to repro bug

---
 .../ezlang/compiler/TestIncrementalSSA.java        | 12 ++----------
 .../ezlang/compiler/TestSSATransform.java          | 14 ++++----------
 2 files changed, 6 insertions(+), 20 deletions(-)

diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestIncrementalSSA.java b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestIncrementalSSA.java
index 49ba3bf..62b8900 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestIncrementalSSA.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestIncrementalSSA.java
@@ -725,20 +725,12 @@ public void testSSA19()
         String src = """
 func bug(N: Int)
 {
-    // The main Sieve array
-    //var ary = new [Int]{len=N,value=0}
     var p=2
-    // Find primes while p^2 < N
-    while( p*p < N ) {
-        // skip marked non-primes
-        //while( ary[p] ) {
-        while (p) {
+    while( p < N ) {
+        if (p) {
             p = p + 1
         }
-        p = p + 1
     }
-
-    // Now just collect the remaining primes, no more marking
     while ( p < N ) {
         p = p + 1
     }
diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
index 0649ab2..a58df67 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
@@ -24,10 +24,12 @@ String compileSrc(String src) {
                 sb.append("Before SSA\n");
                 sb.append("==========\n");
                 BasicBlock.toStr(sb, functionBuilder.entry, new BitSet(), false);
+                //functionBuilder.toDot(sb,false);
                 new EnterSSA(functionBuilder, Options.NONE);
                 sb.append("After SSA\n");
                 sb.append("=========\n");
                 BasicBlock.toStr(sb, functionBuilder.entry, new BitSet(), false);
+                //functionBuilder.toDot(sb,false);
                 new ExitSSA(functionBuilder, Options.NONE);
                 sb.append("After exiting SSA\n");
                 sb.append("=================\n");
@@ -3066,20 +3068,12 @@ public void testSSA19()
         String src = """
 func bug(N: Int)
 {
-    // The main Sieve array
-    //var ary = new [Int]{len=N,value=0}
     var p=2
-    // Find primes while p^2 < N
-    while( p*p < N ) {
-        // skip marked non-primes
-        //while( ary[p] ) {
-        while (p) {
+    while( p < N ) {
+        if (p) {
             p = p + 1
         }
-        p = p + 1
     }
-
-    // Now just collect the remaining primes, no more marking
     while ( p < N ) {
         p = p + 1
     }

From 18db2b73c174551013ac1d0115f5384ec1bcf5dd Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Sat, 24 May 2025 14:29:43 +0100
Subject: [PATCH 076/119] Implement second method of building DOM Frontiers
 based on Appel's Modern Compiler Implementation in C. Set the IDOM of root
 node to null rather than itself, post Dom Tree calculation. Add option to
 dump out the DOM Tree Pre-SSA

---
 .../ezlang/compiler/BasicBlock.java           |  8 +++
 .../ezlang/compiler/DominatorTree.java        | 64 ++++++++++++++++---
 .../ezlang/compiler/EnterSSA.java             |  4 ++
 .../ezlang/compiler/Options.java              |  1 +
 .../ezlang/compiler/TestDominators.java       | 25 +++++++-
 .../ezlang/compiler/TestSSATransform.java     |  1 +
 6 files changed, 92 insertions(+), 11 deletions(-)

diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/BasicBlock.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/BasicBlock.java
index ccb8b09..51f7d51 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/BasicBlock.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/BasicBlock.java
@@ -213,6 +213,14 @@ public StringBuilder toDot(StringBuilder sb, boolean verbose) {
         sb.append("</TABLE>");
         return sb;
     }
+    public StringBuilder listDomFrontiers(StringBuilder sb) {
+        sb.append("L").append(this.bid).append(":");
+        for (BasicBlock bb: dominationFrontier) {
+            sb.append(" L").append(bb.bid);
+        }
+        sb.append("\n");
+        return sb;
+    }
     @Override
     public boolean equals(Object o) {
         if (this == o) return true;
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/DominatorTree.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/DominatorTree.java
index ea3dc2f..2a6b120 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/DominatorTree.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/DominatorTree.java
@@ -1,10 +1,8 @@
 package com.compilerprogramming.ezlang.compiler;
 
-import java.util.ArrayList;
 import java.util.Comparator;
 import java.util.HashSet;
 import java.util.List;
-import java.util.function.Consumer;
 
 /**
  * The dominator tree construction algorithm is based on figure 9.24,
@@ -34,6 +32,7 @@ public DominatorTree(BasicBlock entry) {
         populateTree();
         setDepth();
         calculateDominanceFrontiers();
+        //calculateDominanceFrontiersMethod2();
     }
 
     private void calculateDominatorTree() {
@@ -41,7 +40,7 @@ private void calculateDominatorTree() {
         annotateBlocksWithRPO();
         sortBlocksByRPO();
 
-        // Set IDom entry for root to itself
+        // Set IDom entry for root to itself (see note below)
         entry.idom = entry;
         boolean changed = true;
         while (changed) {
@@ -68,6 +67,11 @@ private void calculateDominatorTree() {
                 }
             }
         }
+        // There is a contradiction between what is described in the book
+        // and the algo - as the book says the IDOM for root is undefined
+        // But we set this to itself when calculating. So after calculations
+        // are done, set this to null. This is technically more correct IMO.
+        entry.idom = null;
     }
 
     private void resetDomInfo() {
@@ -146,7 +150,7 @@ private BasicBlock findFirstPredecessorWithIdom(BasicBlock n) {
     private void populateTree() {
         for (BasicBlock block : blocks) {
             BasicBlock idom = block.idom;
-            if (idom == block) // root
+            if (idom == null) // root
                 continue;
             // add edge from idom to n
             idom.dominatedChildren.add(block);
@@ -166,12 +170,13 @@ private void setDepth() {
      */
     private void setDepth_(BasicBlock block) {
         BasicBlock idom = block.idom;
-        if (idom != block) {
+        if (idom != null) {
             assert idom.domDepth > 0;
             block.domDepth = idom.domDepth + 1;
-        } else {
-            assert idom.domDepth == 1;
-            assert idom.domDepth == block.domDepth;
+        }
+        else {
+            // root (entry) block's idom is null
+            assert block.domDepth == 1;
         }
         for (BasicBlock child : block.dominatedChildren)
             setDepth_(child);
@@ -189,6 +194,8 @@ private void calculateDominanceFrontiers() {
         //          while runner != doms[b]
         //              add b to runner’s dominance frontier set
         //              runner = doms[runner]
+        for (BasicBlock b: blocks)
+            b.dominationFrontier.clear();   // empty set
         for (BasicBlock b : blocks) {
             if (b.predecessors.size() >= 2) {
                 for (BasicBlock p : b.predecessors) {
@@ -204,6 +211,37 @@ private void calculateDominanceFrontiers() {
         }
     }
 
+    // We have an alternative approach to calculating DOM Frontiers to
+    // allow us to validate above
+    private void calculateDominanceFrontiersMethod2()
+    {
+        for (BasicBlock b: blocks)
+            b.dominationFrontier.clear();   // empty set
+        computeDF(entry);
+    }
+
+    // Implementation based on description in pg 440 of
+    // Modern Compiler implementation in C
+    // Appel
+    private void computeDF(BasicBlock n) {
+        var S = new HashSet<BasicBlock>();
+        for (BasicBlock y: n.successors) {
+            if (y.idom != n)
+                S.add(y);
+        }
+        for (BasicBlock c: n.dominatedChildren) {
+            computeDF(c);
+            for (BasicBlock w: c.dominationFrontier) {
+                // Note that the printed book has an error below
+                // and errata gives the correct version
+                if (!n.dominates(w) || n==w) {
+                    S.add(w);
+                }
+            }
+        }
+        n.dominationFrontier.addAll(S);
+    }
+
     public String generateDotOutput() {
         StringBuilder sb = new StringBuilder();
         sb.append("digraph DomTree {\n");
@@ -212,10 +250,18 @@ public String generateDotOutput() {
         }
         for (BasicBlock n : blocks) {
             BasicBlock idom = n.idom;
-            if (idom == n) continue;
+            if (idom == null) continue;
             sb.append(idom.uniqueName()).append("->").append(n.uniqueName()).append(";\n");
         }
         sb.append("}\n");
         return sb.toString();
     }
+
+    public String listDomFrontiers() {
+        StringBuilder sb = new StringBuilder();
+        for (BasicBlock n : blocks) {
+            n.listDomFrontiers(sb);
+        }
+        return sb.toString();
+    }
 }
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/EnterSSA.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/EnterSSA.java
index 83e7c24..2cd5171 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/EnterSSA.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/EnterSSA.java
@@ -41,6 +41,10 @@ public EnterSSA(CompiledFunction bytecodeFunction, EnumSet<Options> options) {
             System.out.println("Pre SSA Dominator Tree");
             System.out.println(domTree.generateDotOutput());
         }
+        if (options.contains(Options.DUMP_PRE_SSA_DOMFRONTIERS)) {
+            System.out.println("Pre SSA Dominance Frontiers");
+            System.out.println(domTree.listDomFrontiers());
+        }
         this.blocks = domTree.blocks;   // the blocks are ordered reverse post order
         findNonLocalNames();
         new Liveness(bytecodeFunction); // EWe require liveness info to construct pruned ssa
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Options.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Options.java
index e71988c..2cdc45a 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Options.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Options.java
@@ -10,6 +10,7 @@ public enum Options {
     REGALLOC,
     DUMP_INITIAL_IR,
     DUMP_PRE_SSA_DOMTREE,
+    DUMP_PRE_SSA_DOMFRONTIERS,
     DUMP_SSA_IR,
     DUMP_SCCP_PREAPPLY,
     DUMP_SCCP_POSTAPPLY,
diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestDominators.java b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestDominators.java
index 4a74ded..a58843b 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestDominators.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestDominators.java
@@ -12,6 +12,8 @@ BasicBlock add(List<BasicBlock> nodes, BasicBlock node) {
         return node;
     }
 
+    // This CFG example is taken from page 473 of
+    // Engineering a Compiler 3rd ed
     BasicBlock makeGraph(List<BasicBlock> nodes) {
         BasicBlock r0 = add(nodes, new BasicBlock(1));
         BasicBlock r1 = add(nodes, new BasicBlock(2, r0));
@@ -28,15 +30,34 @@ BasicBlock makeGraph(List<BasicBlock> nodes) {
         return r0;
     }
 
+    // This DOM Tree example is taken from page 473 of
+    // Engineering a Compiler 3rd ed
     @Test
     public void testDominatorTree() {
         List<BasicBlock> nodes = new ArrayList<>();
         BasicBlock root = makeGraph(nodes);
         DominatorTree tree = new DominatorTree(root);
         System.out.println(tree.generateDotOutput());
-        long[] expectedIdoms = {0,1,1,2,2,4,2,6,6,6};
+        // expected            {_,_,0,1,1,3,1,5,5,5}
+        // Note first entry is not used
+        // Note we set idom of root to itself so the second entry
+        // does not match example
+        long[] expectedIdoms = {-1,0,1,2,2,4,2,6,6,6};
         for (BasicBlock n: nodes) {
-            Assert.assertEquals(expectedIdoms[(int)n.bid], n.idom.bid);
+            if (expectedIdoms[(int)n.bid] == 0)
+                Assert.assertNull(n.idom);
+            else
+                Assert.assertEquals(expectedIdoms[(int)n.bid], n.idom.bid);
+        }
+        // -1 means empty set
+        long[] expectedDF = {0,-1,2,4,2,-1,4,8,4,8};
+        for (BasicBlock n: nodes) {
+            if (expectedDF[(int)n.bid] == -1) {
+                Assert.assertTrue(n.dominationFrontier.isEmpty());
+            }
+            else {
+                Assert.assertEquals(1,n.dominationFrontier.size());
+            }
         }
     }
 
diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
index a58df67..dba13ff 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
@@ -26,6 +26,7 @@ String compileSrc(String src) {
                 BasicBlock.toStr(sb, functionBuilder.entry, new BitSet(), false);
                 //functionBuilder.toDot(sb,false);
                 new EnterSSA(functionBuilder, Options.NONE);
+                //new EnterSSA(functionBuilder, EnumSet.of(DUMP_PRE_SSA_DOMFRONTIERS));
                 sb.append("After SSA\n");
                 sb.append("=========\n");
                 BasicBlock.toStr(sb, functionBuilder.entry, new BitSet(), false);

From 5ab969be28da8141d12cf16214c8c6e8ceda58d6 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Sat, 24 May 2025 17:22:03 +0100
Subject: [PATCH 077/119] Add option to dump Pre-SSA Liveness calculation

---
 .../com/compilerprogramming/ezlang/compiler/EnterSSA.java     | 4 ++++
 .../java/com/compilerprogramming/ezlang/compiler/Options.java | 1 +
 2 files changed, 5 insertions(+)

diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/EnterSSA.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/EnterSSA.java
index 2cd5171..d58f736 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/EnterSSA.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/EnterSSA.java
@@ -48,6 +48,10 @@ public EnterSSA(CompiledFunction bytecodeFunction, EnumSet<Options> options) {
         this.blocks = domTree.blocks;   // the blocks are ordered reverse post order
         findNonLocalNames();
         new Liveness(bytecodeFunction); // EWe require liveness info to construct pruned ssa
+        if (options.contains(Options.DUMP_PRE_SSA_LIVENESS)) {
+            System.out.println("Pre SSA Liveness");
+            System.out.println(bytecodeFunction.toStr(new StringBuilder(), true));
+        }
         insertPhis();
         renameVars();
         bytecodeFunction.isSSA = true;
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Options.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Options.java
index 2cdc45a..b53c8cd 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Options.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Options.java
@@ -11,6 +11,7 @@ public enum Options {
     DUMP_INITIAL_IR,
     DUMP_PRE_SSA_DOMTREE,
     DUMP_PRE_SSA_DOMFRONTIERS,
+    DUMP_PRE_SSA_LIVENESS,
     DUMP_SSA_IR,
     DUMP_SCCP_PREAPPLY,
     DUMP_SCCP_POSTAPPLY,

From 87312a4c661a81ef24278ab7256bb9a4be7eb58a Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Sat, 24 May 2025 23:22:24 +0100
Subject: [PATCH 078/119] Fix bug popping SSA names from stack during SSA
 rename - Phi instructions were missed

---
 .../ezlang/compiler/EnterSSA.java             |   5 +-
 .../ezlang/compiler/TestIncrementalSSA.java   | 154 +++++++++++++++++-
 .../ezlang/compiler/TestSSATransform.java     | 104 +++++++++++-
 3 files changed, 256 insertions(+), 7 deletions(-)

diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/EnterSSA.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/EnterSSA.java
index d58f736..143b123 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/EnterSSA.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/EnterSSA.java
@@ -185,8 +185,9 @@ void search(BasicBlock block) {
         }
         // Pop stacks for defs
         for (Instruction i: block.instructions) {
-            if (i.definesVar()) {
-                var reg = i.def();
+            // Phis don't answer to definesVar() or def()
+            if (i.definesVar() || i instanceof Instruction.Phi) {
+                var reg = i instanceof Instruction.Phi phi ? phi.value() : i.def();
                 stacks[reg.nonSSAId()].pop();
             }
         }
diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestIncrementalSSA.java b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestIncrementalSSA.java
index 62b8900..5058125 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestIncrementalSSA.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestIncrementalSSA.java
@@ -606,6 +606,111 @@ func main()->Int
 """, result);
     }
 
+    @Test
+    public void testSSA11() {
+        String src = """
+        func main()->Int {
+            var a = 0
+            var i = 0
+            var j = 0
+            while (i < 3) {
+                j = 0
+                while (j < 3) {
+                    if (i == j) 
+                        a = a + i + j
+                    else if (i > j)
+                        a = a - 1
+                    j = j + 1        
+                }
+                i = i + 1    
+            }
+            return a
+        }
+""";
+        String result = compileSrc(src);
+        Assert.assertEquals("""
+func main()->Int
+Reg #0 a 0
+Reg #1 i 1
+Reg #2 j 2
+Reg #3 %t3 3
+Reg #4 i_1 1
+Reg #5 j_1 2
+Reg #6 %t6 6
+Reg #7 j_2 2
+Reg #8 %t8 8
+Reg #9 i_2 1
+Reg #10 %t10 10
+Reg #11 a_1 0
+Reg #12 %t12 12
+Reg #13 a_2 0
+Reg #14 %t14 14
+Reg #15 %t15 15
+Reg #16 a_3 0
+Reg #17 %t17 17
+Reg #18 j_3 2
+Reg #19 j_4 2
+Reg #20 j_5 2
+Reg #21 a_4 0
+Reg #22 a_5 0
+Reg #23 a_6 0
+Reg #24 i_3 1
+Reg #25 i_4 1
+Reg #26 %t26 26
+Reg #27 i_5 1
+L0:
+    a = 0
+    i = 0
+    j = 0
+    goto  L2
+L2:
+    a_4 = phi(a, a_1)
+    i_1 = phi(i, i_5)
+    %t3 = i_1<3
+    if %t3 goto L3 else goto L4
+L3:
+    j_1 = 0
+    goto  L5
+L5:
+    a_1 = phi(a_4, a_5)
+    j_2 = phi(j_1, j_5)
+    %t6 = j_2<3
+    if %t6 goto L6 else goto L7
+L6:
+    %t8 = i_1==j_2
+    if %t8 goto L8 else goto L9
+L8:
+    %t10 = a_1+i_1
+    %t12 = %t10+j_2
+    a_2 = %t12
+    goto  L10
+L10:
+    a_5 = phi(a_2, a_6)
+    %t17 = j_2+1
+    j_5 = %t17
+    goto  L5
+L9:
+    %t14 = i_1>j_2
+    if %t14 goto L11 else goto L12
+L11:
+    %t15 = a_1-1
+    a_3 = %t15
+    goto  L12
+L12:
+    a_6 = phi(a_1, a_3)
+    goto  L10
+L7:
+    %t26 = i_2+1
+    i_5 = %t26
+    goto  L2
+L4:
+    ret a_4
+    goto  L1
+L1:
+""", result);
+    }
+
+
     @Test
     public void testSSA17() {
         String src = """
@@ -737,6 +842,51 @@ func bug(N: Int)
 }
                 """;
         String result = compileSrc(src);
-        System.out.println(result);
-    }
+        Assert.assertEquals("""
+func bug(N: Int)
+Reg #0 N 0
+Reg #1 p 1
+Reg #2 %t2 2
+Reg #3 p_1 1
+Reg #4 N_1 0
+Reg #5 %t5 5
+Reg #6 p_2 1
+Reg #7 N_2 0
+Reg #8 p_3 1
+Reg #9 %t9 9
+Reg #10 p_4 1
+Reg #11 N_3 0
+Reg #12 %t12 12
+Reg #13 p_5 1
+L0:
+    arg N
+    p = 2
+    goto  L2
+L2:
+    p_1 = phi(p, p_3)
+    %t2 = p_1<N
+    if %t2 goto L3 else goto L4
+L3:
+    if p_1 goto L5 else goto L6
+L5:
+    %t5 = p_1+1
+    p_2 = %t5
+    goto  L6
+L6:
+    p_3 = phi(p_1, p_2)
+    goto  L2
+L4:
+    goto  L7
+L7:
+    p_4 = phi(p_1, p_5)
+    %t9 = p_4<N_1
+    if %t9 goto L8 else goto L9
+L8:
+    %t12 = p_4+1
+    p_5 = %t12
+    goto  L7
+L9:
+    goto  L1
+L1:
+""", result);    }
 }
\ No newline at end of file
diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
index dba13ff..1b06690 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
@@ -2279,7 +2279,7 @@ else if (i > j)
     %t5_0 = i_1==j_2
     if %t5_0 goto L8 else goto L9
 L8:
-    %t6_0 = a_4+i_1
+    %t6_0 = a_2+i_1
     %t7_0 = %t6_0+j_2
     a_5 = %t7_0
     goto  L10
@@ -2330,7 +2330,7 @@ else if (i > j)
     %t5_0 = i_1==j_2
     if %t5_0 goto L8 else goto L9
 L8:
-    %t6_0 = a_4+i_1
+    %t6_0 = a_2+i_1
     %t7_0 = %t6_0+j_2
     a_5 = %t7_0
     a_6 = a_5
@@ -3081,6 +3081,104 @@ func bug(N: Int)
 }
                 """;
         String result = compileSrc(src);
-        System.out.println(result);
+        Assert.assertEquals("""
+func bug
+Before SSA
+==========
+L0:
+    arg N
+    p = 2
+    goto  L2
+L2:
+    %t2 = p<N
+    if %t2 goto L3 else goto L4
+L3:
+    if p goto L5 else goto L6
+L5:
+    %t3 = p+1
+    p = %t3
+    goto  L6
+L6:
+    goto  L2
+L4:
+    goto  L7
+L7:
+    %t4 = p<N
+    if %t4 goto L8 else goto L9
+L8:
+    %t5 = p+1
+    p = %t5
+    goto  L7
+L9:
+    goto  L1
+L1:
+After SSA
+=========
+L0:
+    arg N_0
+    p_0 = 2
+    goto  L2
+L2:
+    p_1 = phi(p_0, p_5)
+    %t2_0 = p_1<N_0
+    if %t2_0 goto L3 else goto L4
+L3:
+    if p_1 goto L5 else goto L6
+L5:
+    %t3_0 = p_1+1
+    p_4 = %t3_0
+    goto  L6
+L6:
+    p_5 = phi(p_1, p_4)
+    goto  L2
+L4:
+    goto  L7
+L7:
+    p_2 = phi(p_1, p_3)
+    %t4_0 = p_2<N_0
+    if %t4_0 goto L8 else goto L9
+L8:
+    %t5_0 = p_2+1
+    p_3 = %t5_0
+    goto  L7
+L9:
+    goto  L1
+L1:
+After exiting SSA
+=================
+L0:
+    arg N_0
+    p_0 = 2
+    p_1 = p_0
+    goto  L2
+L2:
+    %t2_0 = p_1<N_0
+    if %t2_0 goto L3 else goto L4
+L3:
+    p_5 = p_1
+    if p_1 goto L5 else goto L6
+L5:
+    %t3_0 = p_1+1
+    p_4 = %t3_0
+    p_5 = p_4
+    goto  L6
+L6:
+    p_1 = p_5
+    goto  L2
+L4:
+    p_2 = p_1
+    goto  L7
+L7:
+    %t4_0 = p_2<N_0
+    if %t4_0 goto L8 else goto L9
+L8:
+    %t5_0 = p_2+1
+    p_3 = %t5_0
+    p_2 = p_3
+    goto  L7
+L9:
+    goto  L1
+L1:
+""", result);
     }
 }
\ No newline at end of file

From 4508ad8bd6ec256760b0162fc6b30cde5dbac40a Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Sun, 25 May 2025 14:38:18 +0100
Subject: [PATCH 079/119] #51 Fix bug popping SSA names from stack during SSA
 rename - Phi instructions were missed

---
 .../ezlang/compiler/TestSSATransform.java     | 503 ++++++++++++++++++
 1 file changed, 503 insertions(+)

diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
index 1b06690..02aeb01 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
@@ -3179,6 +3179,509 @@ func bug(N: Int)
 L9:
     goto  L1
 L1:
+""", result);
+    }
+
+    @Test
+    public void testSSA20()
+    {
+        String src = """
+func sieve(N: Int)->[Int]
+{
+    // The main Sieve array
+    var ary = new [Int]{len=N,value=0}
+    // The primes less than N
+    var primes = new [Int]{len=N/2,value=0}
+    // Number of primes so far, searching at index p
+    var nprimes = 0
+    var p=2
+    // Find primes while p^2 < N
+    while( p*p < N ) {
+        // skip marked non-primes
+        while( ary[p] ) {
+            p = p + 1
+        }
+        // p is now a prime
+        primes[nprimes] = p
+        nprimes = nprimes+1
+        // Mark out the rest non-primes
+        var i = p + p
+        while( i < N ) {
+            ary[i] = 1
+            i = i + p
+        }
+        p = p + 1
+    }
+
+    // Now just collect the remaining primes, no more marking
+    while ( p < N ) {
+        if( !ary[p] ) {
+            primes[nprimes] = p
+            nprimes = nprimes + 1
+        }
+        p = p + 1
+    }
+
+    // Copy/shrink the result array
+    var rez = new [Int]{len=nprimes,value=0}
+    var j = 0
+    while( j < nprimes ) {
+        rez[j] = primes[j]
+        j = j + 1
+    }
+    return rez
+}
+func eq(a: [Int], b: [Int], n: Int)->Int
+{
+    var result = 1
+    var i = 0
+    while (i < n)
+    {
+        if (a[i] != b[i])
+        {
+            result = 0
+            break
+        }
+        i = i + 1
+    }
+    return result
+}
+
+func main()->Int
+{
+    var rez = sieve(20)
+    var expected = new [Int]{2,3,5,7,11,13,17,19}
+    return eq(rez,expected,8)
+}
+""";
+        String result = compileSrc(src);
+        Assert.assertEquals("""
+func sieve
+Before SSA
+==========
+L0:
+    arg N
+    %t8 = New([Int], len=N, initValue=0)
+    ary = %t8
+    %t10 = N/2
+    %t9 = New([Int], len=%t10, initValue=0)
+    primes = %t9
+    nprimes = 0
+    p = 2
+    goto  L2
+L2:
+    %t11 = p*p
+    %t12 = %t11<N
+    if %t12 goto L3 else goto L4
+L3:
+    goto  L5
+L5:
+    %t13 = ary[p]
+    if %t13 goto L6 else goto L7
+L6:
+    %t14 = p+1
+    p = %t14
+    goto  L5
+L7:
+    primes[nprimes] = p
+    %t15 = nprimes+1
+    nprimes = %t15
+    %t16 = p+p
+    i = %t16
+    goto  L8
+L8:
+    %t17 = i<N
+    if %t17 goto L9 else goto L10
+L9:
+    ary[i] = 1
+    %t18 = i+p
+    i = %t18
+    goto  L8
+L10:
+    %t19 = p+1
+    p = %t19
+    goto  L2
+L4:
+    goto  L11
+L11:
+    %t20 = p<N
+    if %t20 goto L12 else goto L13
+L12:
+    %t21 = ary[p]
+    %t22 = !%t21
+    if %t22 goto L14 else goto L15
+L14:
+    primes[nprimes] = p
+    %t23 = nprimes+1
+    nprimes = %t23
+    goto  L15
+L15:
+    %t24 = p+1
+    p = %t24
+    goto  L11
+L13:
+    %t25 = New([Int], len=nprimes, initValue=0)
+    rez = %t25
+    j = 0
+    goto  L16
+L16:
+    %t26 = j<nprimes
+    if %t26 goto L17 else goto L18
+L17:
+    %t27 = primes[j]
+    rez[j] = %t27
+    %t28 = j+1
+    j = %t28
+    goto  L16
+L18:
+    ret rez
+    goto  L1
+L1:
+After SSA
+=========
+L0:
+    arg N_0
+    %t8_0 = New([Int], len=N_0, initValue=0)
+    ary_0 = %t8_0
+    %t10_0 = N_0/2
+    %t9_0 = New([Int], len=%t10_0, initValue=0)
+    primes_0 = %t9_0
+    nprimes_0 = 0
+    p_0 = 2
+    goto  L2
+L2:
+    p_1 = phi(p_0, p_5)
+    nprimes_1 = phi(nprimes_0, nprimes_5)
+    %t11_0 = p_1*p_1
+    %t12_0 = %t11_0<N_0
+    if %t12_0 goto L3 else goto L4
+L3:
+    goto  L5
+L5:
+    p_4 = phi(p_1, p_6)
+    %t13_0 = ary_0[p_4]
+    if %t13_0 goto L6 else goto L7
+L6:
+    %t14_0 = p_4+1
+    p_6 = %t14_0
+    goto  L5
+L7:
+    primes_0[nprimes_1] = p_4
+    %t15_0 = nprimes_1+1
+    nprimes_5 = %t15_0
+    %t16_0 = p_4+p_4
+    i_0 = %t16_0
+    goto  L8
+L8:
+    i_1 = phi(i_0, i_2)
+    %t17_0 = i_1<N_0
+    if %t17_0 goto L9 else goto L10
+L9:
+    ary_0[i_1] = 1
+    %t18_0 = i_1+p_4
+    i_2 = %t18_0
+    goto  L8
+L10:
+    %t19_0 = p_4+1
+    p_5 = %t19_0
+    goto  L2
+L4:
+    goto  L11
+L11:
+    p_2 = phi(p_1, p_3)
+    nprimes_2 = phi(nprimes_1, nprimes_4)
+    %t20_0 = p_2<N_0
+    if %t20_0 goto L12 else goto L13
+L12:
+    %t21_0 = ary_0[p_2]
+    %t22_0 = !%t21_0
+    if %t22_0 goto L14 else goto L15
+L14:
+    primes_0[nprimes_2] = p_2
+    %t23_0 = nprimes_2+1
+    nprimes_3 = %t23_0
+    goto  L15
+L15:
+    nprimes_4 = phi(nprimes_2, nprimes_3)
+    %t24_0 = p_2+1
+    p_3 = %t24_0
+    goto  L11
+L13:
+    %t25_0 = New([Int], len=nprimes_2, initValue=0)
+    rez_0 = %t25_0
+    j_0 = 0
+    goto  L16
+L16:
+    j_1 = phi(j_0, j_2)
+    %t26_0 = j_1<nprimes_2
+    if %t26_0 goto L17 else goto L18
+L17:
+    %t27_0 = primes_0[j_1]
+    rez_0[j_1] = %t27_0
+    %t28_0 = j_1+1
+    j_2 = %t28_0
+    goto  L16
+L18:
+    ret rez_0
+    goto  L1
+L1:
+After exiting SSA
+=================
+L0:
+    arg N_0
+    %t8_0 = New([Int], len=N_0, initValue=0)
+    ary_0 = %t8_0
+    %t10_0 = N_0/2
+    %t9_0 = New([Int], len=%t10_0, initValue=0)
+    primes_0 = %t9_0
+    nprimes_0 = 0
+    p_0 = 2
+    p_1 = p_0
+    nprimes_1 = nprimes_0
+    goto  L2
+L2:
+    %t11_0 = p_1*p_1
+    %t12_0 = %t11_0<N_0
+    if %t12_0 goto L3 else goto L4
+L3:
+    p_4 = p_1
+    goto  L5
+L5:
+    %t13_0 = ary_0[p_4]
+    if %t13_0 goto L6 else goto L7
+L6:
+    %t14_0 = p_4+1
+    p_6 = %t14_0
+    p_4 = p_6
+    goto  L5
+L7:
+    primes_0[nprimes_1] = p_4
+    %t15_0 = nprimes_1+1
+    nprimes_5 = %t15_0
+    %t16_0 = p_4+p_4
+    i_0 = %t16_0
+    i_1 = i_0
+    goto  L8
+L8:
+    %t17_0 = i_1<N_0
+    if %t17_0 goto L9 else goto L10
+L9:
+    ary_0[i_1] = 1
+    %t18_0 = i_1+p_4
+    i_2 = %t18_0
+    i_1 = i_2
+    goto  L8
+L10:
+    %t19_0 = p_4+1
+    p_5 = %t19_0
+    p_1 = p_5
+    nprimes_1 = nprimes_5
+    goto  L2
+L4:
+    p_2 = p_1
+    nprimes_2 = nprimes_1
+    goto  L11
+L11:
+    %t20_0 = p_2<N_0
+    if %t20_0 goto L12 else goto L13
+L12:
+    %t21_0 = ary_0[p_2]
+    %t22_0 = !%t21_0
+    nprimes_4 = nprimes_2
+    if %t22_0 goto L14 else goto L15
+L14:
+    primes_0[nprimes_2] = p_2
+    %t23_0 = nprimes_2+1
+    nprimes_3 = %t23_0
+    nprimes_4 = nprimes_3
+    goto  L15
+L15:
+    %t24_0 = p_2+1
+    p_3 = %t24_0
+    p_2 = p_3
+    nprimes_2 = nprimes_4
+    goto  L11
+L13:
+    %t25_0 = New([Int], len=nprimes_2, initValue=0)
+    rez_0 = %t25_0
+    j_0 = 0
+    j_1 = j_0
+    goto  L16
+L16:
+    %t26_0 = j_1<nprimes_2
+    if %t26_0 goto L17 else goto L18
+L17:
+    %t27_0 = primes_0[j_1]
+    rez_0[j_1] = %t27_0
+    %t28_0 = j_1+1
+    j_2 = %t28_0
+    j_1 = j_2
+    goto  L16
+L18:
+    ret rez_0
+    goto  L1
+L1:
+func eq
+Before SSA
+==========
+L0:
+    arg a
+    arg b
+    arg n
+    result = 1
+    i = 0
+    goto  L2
+L2:
+    %t5 = i<n
+    if %t5 goto L3 else goto L4
+L3:
+    %t6 = a[i]
+    %t7 = b[i]
+    %t8 = %t6!=%t7
+    if %t8 goto L5 else goto L6
+L5:
+    result = 0
+    goto  L4
+L4:
+    ret result
+    goto  L1
+L1:
+L6:
+    %t9 = i+1
+    i = %t9
+    goto  L2
+After SSA
+=========
+L0:
+    arg a_0
+    arg b_0
+    arg n_0
+    result_0 = 1
+    i_0 = 0
+    goto  L2
+L2:
+    i_1 = phi(i_0, i_2)
+    %t5_0 = i_1<n_0
+    if %t5_0 goto L3 else goto L4
+L3:
+    %t6_0 = a_0[i_1]
+    %t7_0 = b_0[i_1]
+    %t8_0 = %t6_0!=%t7_0
+    if %t8_0 goto L5 else goto L6
+L5:
+    result_1 = 0
+    goto  L4
+L4:
+    result_2 = phi(result_0, result_1)
+    ret result_2
+    goto  L1
+L1:
+L6:
+    %t9_0 = i_1+1
+    i_2 = %t9_0
+    goto  L2
+After exiting SSA
+=================
+L0:
+    arg a_0
+    arg b_0
+    arg n_0
+    result_0 = 1
+    i_0 = 0
+    i_1 = i_0
+    goto  L2
+L2:
+    %t5_0 = i_1<n_0
+    result_2 = result_0
+    if %t5_0 goto L3 else goto L4
+L3:
+    %t6_0 = a_0[i_1]
+    %t7_0 = b_0[i_1]
+    %t8_0 = %t6_0!=%t7_0
+    if %t8_0 goto L5 else goto L6
+L5:
+    result_1 = 0
+    result_2 = result_1
+    goto  L4
+L4:
+    ret result_2
+    goto  L1
+L1:
+L6:
+    %t9_0 = i_1+1
+    i_2 = %t9_0
+    i_1 = i_2
+    goto  L2
+func main
+Before SSA
+==========
+L0:
+    %t2 = 20
+    %t3 = call sieve params %t2
+    rez = %t3
+    %t4 = New([Int], len=8)
+    %t4[0] = 2
+    %t4[1] = 3
+    %t4[2] = 5
+    %t4[3] = 7
+    %t4[4] = 11
+    %t4[5] = 13
+    %t4[6] = 17
+    %t4[7] = 19
+    expected = %t4
+    %t5 = rez
+    %t6 = expected
+    %t7 = 8
+    %t8 = call eq params %t5, %t6, %t7
+    ret %t8
+    goto  L1
+L1:
+After SSA
+=========
+L0:
+    %t2_0 = 20
+    %t3_0 = call sieve params %t2_0
+    rez_0 = %t3_0
+    %t4_0 = New([Int], len=8)
+    %t4_0[0] = 2
+    %t4_0[1] = 3
+    %t4_0[2] = 5
+    %t4_0[3] = 7
+    %t4_0[4] = 11
+    %t4_0[5] = 13
+    %t4_0[6] = 17
+    %t4_0[7] = 19
+    expected_0 = %t4_0
+    %t5_0 = rez_0
+    %t6_0 = expected_0
+    %t7_0 = 8
+    %t8_0 = call eq params %t5_0, %t6_0, %t7_0
+    ret %t8_0
+    goto  L1
+L1:
+After exiting SSA
+=================
+L0:
+    %t2_0 = 20
+    %t3_0 = call sieve params %t2_0
+    rez_0 = %t3_0
+    %t4_0 = New([Int], len=8)
+    %t4_0[0] = 2
+    %t4_0[1] = 3
+    %t4_0[2] = 5
+    %t4_0[3] = 7
+    %t4_0[4] = 11
+    %t4_0[5] = 13
+    %t4_0[6] = 17
+    %t4_0[7] = 19
+    expected_0 = %t4_0
+    %t5_0 = rez_0
+    %t6_0 = expected_0
+    %t7_0 = 8
+    %t8_0 = call eq params %t5_0, %t6_0, %t7_0
+    ret %t8_0
+    goto  L1
+L1:
 """, result);
     }
 }
\ No newline at end of file

From 4a956bf7f3e44c66237c3606dc5b8e6ebf8f0d36 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Sun, 25 May 2025 14:38:42 +0100
Subject: [PATCH 080/119] #53 Test case for bug

---
 .../ezlang/compiler/TestIncrementalSSA.java   | 314 +++++++++++++++++-
 1 file changed, 313 insertions(+), 1 deletion(-)

diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestIncrementalSSA.java b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestIncrementalSSA.java
index 5058125..1263b13 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestIncrementalSSA.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestIncrementalSSA.java
@@ -824,6 +824,7 @@ func foo(len: Int,val: Int,x: Int,y: Int)->[Int]
 L1:
 """, result);
     }
+
     @Test
     public void testSSA19()
     {
@@ -888,5 +889,316 @@ func bug(N: Int)
 L9:
     goto  L1
 L1:
-""", result);    }
+""", result);
+    }
+
+    @Test
+    public void testSSA20()
+    {
+        String src = """
+func sieve(N: Int)->[Int]
+{
+    // The main Sieve array
+    var ary = new [Int]{len=N,value=0}
+    // The primes less than N
+    var primes = new [Int]{len=N/2,value=0}
+    // Number of primes so far, searching at index p
+    var nprimes = 0
+    var p=2
+    // Find primes while p^2 < N
+    while( p*p < N ) {
+        // skip marked non-primes
+        while( ary[p] ) {
+            p = p + 1
+        }
+        // p is now a prime
+        primes[nprimes] = p
+        nprimes = nprimes+1
+        // Mark out the rest non-primes
+        var i = p + p
+        while( i < N ) {
+            ary[i] = 1
+            i = i + p
+        }
+        p = p + 1
+    }
+
+    // Now just collect the remaining primes, no more marking
+    while ( p < N ) {
+        if( !ary[p] ) {
+            primes[nprimes] = p
+            nprimes = nprimes + 1
+        }
+        p = p + 1
+    }
+
+    // Copy/shrink the result array
+    var rez = new [Int]{len=nprimes,value=0}
+    var j = 0
+    while( j < nprimes ) {
+        rez[j] = primes[j]
+        j = j + 1
+    }
+    return rez
+}
+func eq(a: [Int], b: [Int], n: Int)->Int
+{
+    var result = 1
+    var i = 0
+    while (i < n)
+    {
+        if (a[i] != b[i])
+        {
+            result = 0
+            break
+        }
+        i = i + 1
+    }
+    return result
+}
+
+func main()->Int
+{
+    var rez = sieve(20)
+    var expected = new [Int]{2,3,5,7,11,13,17,19}
+    return eq(rez,expected,8)
+}
+""";
+        String result = compileSrc(src);
+        Assert.assertEquals("""
+func sieve(N: Int)->[Int]
+Reg #0 N 0
+Reg #1 ary 1
+Reg #2 primes 2
+Reg #3 nprimes 3
+Reg #4 p 4
+Reg #5 rez 5
+Reg #6 j 6
+Reg #7 i 7
+Reg #8 %t8 8
+Reg #9 %t9 9
+Reg #10 %t10 10
+Reg #11 %t11 11
+Reg #12 p_1 4
+Reg #13 %t13 13
+Reg #14 N_1 0
+Reg #15 %t15 15
+Reg #16 ary_1 1
+Reg #17 p_2 4
+Reg #18 %t18 18
+Reg #19 p_3 4
+Reg #20 ary_2 1
+Reg #21 primes_1 2
+Reg #22 primes_2 2
+Reg #23 nprimes_1 3
+Reg #24 nprimes_2 3
+Reg #25 %t25 25
+Reg #26 nprimes_3 3
+Reg #27 %t27 27
+Reg #28 %t28 28
+Reg #29 i_1 7
+Reg #30 N_2 0
+Reg #31 ary_3 1
+Reg #32 %t32 32
+Reg #33 p_4 4
+Reg #34 i_2 7
+Reg #35 N_3 0
+Reg #36 %t36 36
+Reg #37 p_5 4
+Reg #38 primes_3 2
+Reg #39 nprimes_4 3
+Reg #40 %t40 40
+Reg #41 p_6 4
+Reg #42 N_4 0
+Reg #43 %t43 43
+Reg #44 ary_4 1
+Reg #45 %t45 45
+Reg #46 primes_4 2
+Reg #47 nprimes_5 3
+Reg #48 %t48 48
+Reg #49 nprimes_6 3
+Reg #50 %t50 50
+Reg #51 p_7 4
+Reg #52 p_8 4
+Reg #53 N_5 0
+Reg #54 ary_5 1
+Reg #55 primes_5 2
+Reg #56 nprimes_7 3
+Reg #57 %t57 57
+Reg #58 %t58 58
+Reg #59 j_1 6
+Reg #60 nprimes_8 3
+Reg #61 %t61 61
+Reg #62 primes_6 2
+Reg #63 rez_1 5
+Reg #64 %t64 64
+Reg #65 j_2 6
+L0:
+    arg N
+    %t8 = New([Int], len=N, initValue=0)
+    ary = %t8
+    %t10 = N/2
+    %t9 = New([Int], len=%t10, initValue=0)
+    primes = %t9
+    nprimes = 0
+    p = 2
+    goto  L2
+L2:
+    nprimes_2 = phi(nprimes, nprimes_3)
+    ary_2 = phi(ary, ary_3)
+    N_1 = phi(N, N_2)
+    p_1 = phi(p, p_5)
+    %t11 = p_1*p_1
+    %t13 = %t11<N_1
+    if %t13 goto L3 else goto L4
+L3:
+    goto  L5
+L5:
+    p_2 = phi(p_1, p_3)
+    %t15 = ary_2[p_2]
+    if %t15 goto L6 else goto L7
+L6:
+    %t18 = p_2+1
+    p_3 = %t18
+    goto  L5
+L7:
+    primes[nprimes_2] = p_2
+    %t25 = nprimes_2+1
+    nprimes_3 = %t25
+    %t27 = p_2+p_2
+    i = %t27
+    goto  L8
+L8:
+    i_1 = phi(i, i_2)
+    %t28 = i_1<N_1
+    if %t28 goto L9 else goto L10
+L9:
+    ary_1[i_1] = 1
+    %t32 = i_1+p_2
+    i_2 = %t32
+    goto  L8
+L10:
+    %t36 = p_4+1
+    p_5 = %t36
+    goto  L2
+L4:
+    goto  L11
+L11:
+    nprimes_5 = phi(nprimes_2, nprimes_7)
+    p_6 = phi(p_1, p_8)
+    %t40 = p_6<N_1
+    if %t40 goto L12 else goto L13
+L12:
+    %t43 = ary_2[p_6]
+    %t45 = !%t43
+    if %t45 goto L14 else goto L15
+L14:
+    primes_2[nprimes_5] = p_6
+    %t48 = nprimes_5+1
+    nprimes_6 = %t48
+    goto  L15
+L15:
+    nprimes_7 = phi(nprimes_5, nprimes_6)
+    %t50 = p_6+1
+    p_8 = %t50
+    goto  L11
+L13:
+    %t57 = New([Int], len=nprimes_5, initValue=0)
+    rez = %t57
+    j = 0
+    goto  L16
+L16:
+    j_1 = phi(j, j_2)
+    %t58 = j_1<nprimes_5
+    if %t58 goto L17 else goto L18
+L17:
+    %t61 = primes_4[j_1]
+    rez[j_1] = %t61
+    %t64 = j_1+1
+    j_2 = %t64
+    goto  L16
+L18:
+    ret rez_1
+    goto  L1
+L1:
+func eq(a: [Int],b: [Int],n: Int)->Int
+Reg #0 a 0
+Reg #1 b 1
+Reg #2 n 2
+Reg #3 result 3
+Reg #4 i 4
+Reg #5 %t5 5
+Reg #6 i_1 4
+Reg #7 n_1 2
+Reg #8 %t8 8
+Reg #9 a_1 0
+Reg #10 %t10 10
+Reg #11 b_1 1
+Reg #12 %t12 12
+Reg #13 result_1 3
+Reg #14 %t14 14
+Reg #15 i_2 4
+Reg #16 result_2 3
+Reg #17 result_3 3
+L0:
+    arg a
+    arg b
+    arg n
+    result = 1
+    i = 0
+    goto  L2
+L2:
+    i_1 = phi(i, i_2)
+    %t5 = i_1<n
+    if %t5 goto L3 else goto L4
+L3:
+    %t8 = a[i_1]
+    %t10 = b[i_1]
+    %t12 = %t8!=%t10
+    if %t12 goto L5 else goto L6
+L5:
+    result_1 = 0
+    goto  L4
+L4:
+    result_2 = phi(result, result_1)
+    ret result_2
+    goto  L1
+L1:
+L6:
+    %t14 = i_1+1
+    i_2 = %t14
+    goto  L2
+func main()->Int
+Reg #0 rez 0
+Reg #1 expected 1
+Reg #2 %t2 2
+Reg #3 %t3 3
+Reg #4 %t4 4
+Reg #5 %t5 5
+Reg #6 %t6 6
+Reg #7 %t7 7
+Reg #8 %t8 8
+L0:
+    %t2 = 20
+    %t3 = call sieve params %t2
+    rez = %t3
+    %t4 = New([Int], len=8)
+    %t4[0] = 2
+    %t4[1] = 3
+    %t4[2] = 5
+    %t4[3] = 7
+    %t4[4] = 11
+    %t4[5] = 13
+    %t4[6] = 17
+    %t4[7] = 19
+    expected = %t4
+    %t5 = rez
+    %t6 = expected
+    %t7 = 8
+    %t8 = call eq params %t5, %t6, %t7
+    ret %t8
+    goto  L1
+L1:
+""", result);
+    }
 }
\ No newline at end of file

From 2b02afb8a7029856473e778ce8868365ed43ade8 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Sun, 25 May 2025 14:46:16 +0100
Subject: [PATCH 081/119] #53 When a phi goes dead all references to its def in
 the memoized set of defs must be removed

---
 .../ezlang/compiler/CompiledFunction.java     |  44 +-
 .../ezlang/compiler/Instruction.java          |   5 +-
 .../ezlang/compiler/Register.java             |  14 +-
 .../ezlang/compiler/SSAEdges.java             |   2 +
 .../ezlang/compiler/TestIncrementalSSA.java   | 510 +++++++++---------
 5 files changed, 320 insertions(+), 255 deletions(-)

diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
index ae19644..501e7d5 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
@@ -965,6 +965,32 @@ private Register addPhiOperands(Register variable, Instruction.Phi phi) {
             return tryRemovingPhi(phi);
         }
 
+        // The Phi's def is dead so we need to remove
+        // all occurrences of this def from the memoized defs
+        // per Basic Block
+        private void clearDefs(Instruction.Phi phi) {
+            // TODO rethink the data structure for currentDef
+            var def = phi.value();
+            var defs = currentDef.get(def.nonSSAId());
+            // Make a list of block/reg that we need to delete
+            var bbList = new ArrayList<BasicBlock>();
+            var regList = new ArrayList<Register>();
+            for (var entries : defs.entrySet()) {
+                var bb = entries.getKey();
+                var reg = entries.getValue();
+                if (reg.equals(def)) {
+                    bbList.add(bb);
+                    regList.add(reg);
+                }
+            }
+            // Now delete them
+            for (int i = 0; i < bbList.size(); i++) {
+                var bb = bbList.get(i);
+                var reg = regList.get(i);
+                defs.remove(bb, reg);
+            }
+        }
+
         private Register tryRemovingPhi(Instruction.Phi phi) {
             Register same = null;
             // Check if phi has distinct inputs
@@ -991,6 +1017,9 @@ private Register tryRemovingPhi(Instruction.Phi phi) {
             // remove all uses of phi to same and remove phi
             replacePhiValueAndUsers(phi, same);
             phi.block.deleteInstruction(phi);
+            // Since the phi is dead any references to its def
+            // must be removed; this is not mentioned in the paper
+            clearDefs(phi);
             // try to recursively remove all phi users, which might have become trivial
             for (var use: users) {
                 if (use instanceof Instruction.Phi phiuser)
@@ -1003,25 +1032,28 @@ private Register tryRemovingPhi(Instruction.Phi phi) {
          * Reroute all uses of phi to new value
          */
         private void replacePhiValueAndUsers(Instruction.Phi phi, Register newValue) {
-            var oldDefUseChain = ssaDefUses.get(phi.value());
+            var oldValue = phi.value();
+            var oldDefUseChain = ssaDefUses.get(oldValue);
             var newDefUseChain = ssaDefUses.get(newValue);
             if (newDefUseChain == null) {
-                // Can be null because this may be existing def
-                newDefUseChain = SSAEdges.addDef(ssaDefUses, newValue, phi);
+                throw new CompilerException("Expected error: undefined var " + newValue);
             }
             if (oldDefUseChain != null) {
                 for (Instruction instruction: oldDefUseChain.useList) {
+                    boolean replaced;
                     if (instruction instanceof Instruction.Phi somePhi) {
-                        somePhi.replaceInput(phi.value(), newValue);
+                        replaced = somePhi.replaceInput(oldValue, newValue);
                     }
                     else {
-                        instruction.replaceUse(phi.value(), newValue);
+                        replaced = instruction.replaceUse(oldValue, newValue);
+                    }
+                    if (!replaced) {
+                        throw new CompilerException("Discrepancy between var use list and var definition");
                     }
                 }
                 // Users of phi old value become users of the new value
                 newDefUseChain.useList.addAll(oldDefUseChain.useList);
                 oldDefUseChain.useList.clear();
-                // FIXME remove old def from def-use chains
             }
         }
 
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java
index 5baf003..0aa50ff 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java
@@ -431,15 +431,18 @@ public void addInput(Register register) {
             newUses[newUses.length-1] = new Operand.RegisterOperand(register);
             this.uses = newUses;
         }
-        public void replaceInput(Register oldReg, Register newReg) {
+        public boolean replaceInput(Register oldReg, Register newReg) {
+            boolean replaced = false;
             for (int i = 0; i < numInputs(); i++) {
                 if (isRegisterInput(i)) {
                     Register in = inputAsRegister(i);
                     if (in.equals(oldReg)) {
                         replaceInput(i, newReg);
+                        replaced = true;
                     }
                 }
             }
+            return replaced;
         }
     }
 
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Register.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Register.java
index 3376e61..42895b9 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Register.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Register.java
@@ -40,7 +40,7 @@ public class Register {
      *
      * Not unique.
      */
-    private final String name;
+    protected final String name;
     /**
      * The type of the register
      */
@@ -50,7 +50,7 @@ public class Register {
      * of the executing function. Multiple registers may share the same
      * frame slot because of different non-overlapping life times.
      */
-    private int frameSlot;
+    protected int frameSlot;
 
     public Register(int id, String name, Type type) {
         this(id,name,type,id);  // Initially frame slot is set to the unique ID
@@ -107,7 +107,15 @@ public SSARegister(Register original, int id, int version) {
         public int nonSSAId() {
             return originalRegNumber;
         }
-    }
 
+        @Override
+        public String toString() {
+            return "SSARegister{name=" + name + ", id=" + id + ", frameSlot=" + frameSlot + ", ssaVersion=" + ssaVersion + ", originalRegNumber=" + originalRegNumber + '}';
+        }
+    }
 
+    @Override
+    public String toString() {
+        return "Register{name=" + name + ", id=" + id + ", frameSlot=" + frameSlot + "}";
+    }
 }
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SSAEdges.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SSAEdges.java
index 4e81e91..41ebb8b 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SSAEdges.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SSAEdges.java
@@ -89,6 +89,8 @@ private static void recordUses(Map<Register, SSADef> defUseChains, Register[] in
 
     public static void recordUse(Map<Register, SSADef> defUseChains, Instruction instruction, Register register) {
         SSADef def = defUseChains.get(register);
+        if (def == null)
+            throw new CompilerException("No def found for " + register);
         def.useList.add(instruction);
     }
 
diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestIncrementalSSA.java b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestIncrementalSSA.java
index 1263b13..e3a5272 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestIncrementalSSA.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestIncrementalSSA.java
@@ -183,188 +183,189 @@ func example14_66(p: Int, q: Int, r: Int, s: Int, t: Int) {
                 """;
         String result = compileSrc(src);
         Assert.assertEquals("""
-                func print(a: Int,b: Int,c: Int,d: Int)
-                Reg #0 a 0
-                Reg #1 b 1
-                Reg #2 c 2
-                Reg #3 d 3
-                L0:
-                    arg a
-                    arg b
-                    arg c
-                    arg d
-                    goto  L1
-                L1:
-                func example14_66(p: Int,q: Int,r: Int,s: Int,t: Int)
-                Reg #0 p 0
-                Reg #1 q 1
-                Reg #2 r 2
-                Reg #3 s 3
-                Reg #4 t 4
-                Reg #5 i 5
-                Reg #6 j 6
-                Reg #7 k 7
-                Reg #8 l 8
-                Reg #9 p_1 0
-                Reg #10 i_1 5
-                Reg #11 j_1 6
-                Reg #12 q_1 1
-                Reg #13 l_1 8
-                Reg #14 l_2 8
-                Reg #15 %t15 15
-                Reg #16 k_1 7
-                Reg #17 k_2 7
-                Reg #18 k_3 7
-                Reg #19 %t19 19
-                Reg #20 k_4 7
-                Reg #21 %t21 21
-                Reg #22 i_2 5
-                Reg #23 i_3 5
-                Reg #24 %t24 24
-                Reg #25 j_2 6
-                Reg #26 j_3 6
-                Reg #27 j_4 6
-                Reg #28 %t28 28
-                Reg #29 k_5 7
-                Reg #30 %t30 30
-                Reg #31 l_3 8
-                Reg #32 l_4 8
-                Reg #33 l_5 8
-                Reg #34 r_1 2
-                Reg #35 %t35 35
-                Reg #36 l_6 8
-                Reg #37 l_7 8
-                Reg #38 %t38 38
-                Reg #39 s_1 3
-                Reg #40 s_2 3
-                Reg #41 r_2 2
-                Reg #42 r_3 2
-                Reg #43 r_4 2
-                Reg #44 r_5 2
-                Reg #45 s_3 3
-                Reg #46 s_4 3
-                Reg #47 s_5 3
-                Reg #48 l_8 8
-                Reg #49 %t49 49
-                Reg #50 i_4 5
-                Reg #51 i_5 5
-                Reg #52 i_6 5
-                Reg #53 i_7 5
-                Reg #54 %t54 54
-                Reg #55 t_1 4
-                Reg #56 t_2 4
-                Reg #57 t_3 4
-                Reg #58 t_4 4
-                Reg #59 t_5 4
-                Reg #60 t_6 4
-                Reg #61 p_2 0
-                Reg #62 p_3 0
-                Reg #63 p_4 0
-                Reg #64 p_5 0
-                Reg #65 p_6 0
-                Reg #66 q_2 1
-                Reg #67 q_3 1
-                Reg #68 q_4 1
-                Reg #69 q_5 1
-                Reg #70 q_6 1
-                Reg #71 r_6 2
-                Reg #72 s_6 3
-                Reg #73 j_5 6
-                Reg #74 j_6 6
-                Reg #75 j_7 6
-                Reg #76 k_6 7
-                Reg #77 k_7 7
-                Reg #78 k_8 7
-                Reg #79 l_9 8
-                L0:
-                    arg p
-                    arg q
-                    arg r
-                    arg s
-                    arg t
-                    i = 1
-                    j = 1
-                    k = 1
-                    l = 1
-                    goto  L2
-                L2:
-                    t_5 = phi(t, t_1)
-                    s_5 = phi(s, s_2)
-                    r_4 = phi(r, r_1)
-                    l_5 = phi(l, l_9)
-                    j_4 = phi(j, j_5)
-                    k_2 = phi(k, k_6)
-                    q_1 = phi(q, q_2)
-                    i_1 = phi(i, i_7)
-                    p_1 = phi(p, p_2)
-                    if 1 goto L3 else goto L4
-                L3:
-                    if p_1 goto L5 else goto L6
-                L5:
-                    j_1 = i_1
-                    if q_1 goto L8 else goto L9
-                L8:
-                    l_1 = 2
-                    goto  L10
-                L10:
-                    l_4 = phi(l_1, l_2)
-                    %t15 = k_2+1
-                    k_3 = %t15
-                    goto  L7
-                L7:
-                    l_3 = phi(l_4, l_5)
-                    k_5 = phi(k_3, k_4)
-                    j_2 = phi(j_1, j_4)
-                    %t21 = i_1
-                    %t24 = j_2
-                    %t28 = k_5
-                    %t30 = l_3
-                    call print params %t21, %t24, %t28, %t30
-                    goto  L11
-                L11:
-                    l_6 = phi(l_3, l_8)
-                    if 1 goto L12 else goto L13
-                L12:
-                    if r_4 goto L14 else goto L15
-                L14:
-                    %t35 = l_6+4
-                    l_7 = %t35
-                    goto  L15
-                L15:
-                    l_8 = phi(l_6, l_7)
-                    %t38 = !s_5
-                    if %t38 goto L16 else goto L17
-                L16:
-                    goto  L13
-                L13:
-                    l_9 = phi(l_6, l_8)
-                    k_6 = phi(k_5, k_7)
-                    j_5 = phi(j_2, j_6)
-                    q_2 = phi(q_1, q_3)
-                    p_2 = phi(p_1, p_3)
-                    t_1 = phi(t_5, t_2)
-                    i_4 = phi(i_1, i_5)
-                    %t49 = i_4+6
-                    i_7 = %t49
-                    %t54 = !t_1
-                    if %t54 goto L18 else goto L19
-                L18:
-                    goto  L4
-                L4:
-                    goto  L1
-                L1:
-                L19:
-                    goto  L2
-                L17:
-                    goto  L11
-                L9:
-                    l_2 = 3
-                    goto  L10
-                L6:
-                    %t19 = k_2+2
-                    k_4 = %t19
-                    goto  L7
-                """, result);
+func print(a: Int,b: Int,c: Int,d: Int)
+Reg #0 a 0
+Reg #1 b 1
+Reg #2 c 2
+Reg #3 d 3
+L0:
+    arg a
+    arg b
+    arg c
+    arg d
+    goto  L1
+L1:
+func example14_66(p: Int,q: Int,r: Int,s: Int,t: Int)
+Reg #0 p 0
+Reg #1 q 1
+Reg #2 r 2
+Reg #3 s 3
+Reg #4 t 4
+Reg #5 i 5
+Reg #6 j 6
+Reg #7 k 7
+Reg #8 l 8
+Reg #9 p_1 0
+Reg #10 i_1 5
+Reg #11 j_1 6
+Reg #12 q_1 1
+Reg #13 l_1 8
+Reg #14 l_2 8
+Reg #15 %t15 15
+Reg #16 k_1 7
+Reg #17 k_2 7
+Reg #18 k_3 7
+Reg #19 %t19 19
+Reg #20 k_4 7
+Reg #21 %t21 21
+Reg #22 i_2 5
+Reg #23 i_3 5
+Reg #24 %t24 24
+Reg #25 j_2 6
+Reg #26 j_3 6
+Reg #27 j_4 6
+Reg #28 %t28 28
+Reg #29 k_5 7
+Reg #30 %t30 30
+Reg #31 l_3 8
+Reg #32 l_4 8
+Reg #33 l_5 8
+Reg #34 r_1 2
+Reg #35 %t35 35
+Reg #36 l_6 8
+Reg #37 l_7 8
+Reg #38 %t38 38
+Reg #39 s_1 3
+Reg #40 s_2 3
+Reg #41 r_2 2
+Reg #42 r_3 2
+Reg #43 r_4 2
+Reg #44 r_5 2
+Reg #45 s_3 3
+Reg #46 s_4 3
+Reg #47 s_5 3
+Reg #48 l_8 8
+Reg #49 %t49 49
+Reg #50 i_4 5
+Reg #51 i_5 5
+Reg #52 i_6 5
+Reg #53 i_7 5
+Reg #54 i_8 5
+Reg #55 %t55 55
+Reg #56 t_1 4
+Reg #57 t_2 4
+Reg #58 t_3 4
+Reg #59 t_4 4
+Reg #60 t_5 4
+Reg #61 t_6 4
+Reg #62 t_7 4
+Reg #63 p_2 0
+Reg #64 p_3 0
+Reg #65 p_4 0
+Reg #66 p_5 0
+Reg #67 p_6 0
+Reg #68 p_7 0
+Reg #69 q_2 1
+Reg #70 q_3 1
+Reg #71 q_4 1
+Reg #72 q_5 1
+Reg #73 q_6 1
+Reg #74 q_7 1
+Reg #75 r_6 2
+Reg #76 r_7 2
+Reg #77 r_8 2
+Reg #78 r_9 2
+Reg #79 s_6 3
+Reg #80 s_7 3
+Reg #81 s_8 3
+Reg #82 s_9 3
+Reg #83 j_5 6
+Reg #84 j_6 6
+Reg #85 j_7 6
+Reg #86 j_8 6
+Reg #87 k_6 7
+Reg #88 k_7 7
+Reg #89 k_8 7
+Reg #90 k_9 7
+Reg #91 l_9 8
+L0:
+    arg p
+    arg q
+    arg r
+    arg s
+    arg t
+    i = 1
+    j = 1
+    k = 1
+    l = 1
+    goto  L2
+L2:
+    l_5 = phi(l, l_9)
+    j_4 = phi(j, j_2)
+    k_2 = phi(k, k_5)
+    i_1 = phi(i, i_8)
+    if 1 goto L3 else goto L4
+L3:
+    if p goto L5 else goto L6
+L5:
+    j_1 = i_1
+    if q goto L8 else goto L9
+L8:
+    l_1 = 2
+    goto  L10
+L10:
+    l_4 = phi(l_1, l_2)
+    %t15 = k_2+1
+    k_3 = %t15
+    goto  L7
+L7:
+    l_3 = phi(l_4, l_5)
+    k_5 = phi(k_3, k_4)
+    j_2 = phi(j_1, j_4)
+    %t21 = i_1
+    %t24 = j_2
+    %t28 = k_5
+    %t30 = l_3
+    call print params %t21, %t24, %t28, %t30
+    goto  L11
+L11:
+    l_6 = phi(l_3, l_8)
+    if 1 goto L12 else goto L13
+L12:
+    if r goto L14 else goto L15
+L14:
+    %t35 = l_6+4
+    l_7 = %t35
+    goto  L15
+L15:
+    l_8 = phi(l_6, l_7)
+    %t38 = !s
+    if %t38 goto L16 else goto L17
+L16:
+    goto  L13
+L13:
+    l_9 = phi(l_6, l_8)
+    %t49 = i_1+6
+    i_8 = %t49
+    %t55 = !t
+    if %t55 goto L18 else goto L19
+L18:
+    goto  L4
+L4:
+    goto  L1
+L1:
+L19:
+    goto  L2
+L17:
+    goto  L11
+L9:
+    l_2 = 3
+    goto  L10
+L6:
+    %t19 = k_2+2
+    k_4 = %t19
+    goto  L7
+""", result);
     }
 
     @Test
@@ -658,6 +659,9 @@ func main()->Int
 Reg #25 i_4 1
 Reg #26 %t26 26
 Reg #27 i_5 1
+Reg #28 i_6 1
+Reg #29 i_7 1
+Reg #30 i_8 1
 L0:
     a = 0
     i = 0
@@ -665,7 +669,7 @@ func main()->Int
     goto  L2
 L2:
     a_4 = phi(a, a_1)
-    i_1 = phi(i, i_5)
+    i_1 = phi(i, i_8)
     %t3 = i_1<3
     if %t3 goto L3 else goto L4
 L3:
@@ -700,8 +704,8 @@ func main()->Int
     a_6 = phi(a_1, a_3)
     goto  L10
 L7:
-    %t26 = i_2+1
-    i_5 = %t26
+    %t26 = i_1+1
+    i_8 = %t26
     goto  L2
 L4:
     ret a_4
@@ -859,6 +863,8 @@ func bug(N: Int)
 Reg #11 N_3 0
 Reg #12 %t12 12
 Reg #13 p_5 1
+Reg #14 N_4 0
+Reg #15 N_5 0
 L0:
     arg N
     p = 2
@@ -880,7 +886,7 @@ func bug(N: Int)
     goto  L7
 L7:
     p_4 = phi(p_1, p_5)
-    %t9 = p_4<N_1
+    %t9 = p_4<N
     if %t9 goto L8 else goto L9
 L8:
     %t12 = p_4+1
@@ -1003,36 +1009,52 @@ func sieve(N: Int)->[Int]
 Reg #33 p_4 4
 Reg #34 i_2 7
 Reg #35 N_3 0
-Reg #36 %t36 36
-Reg #37 p_5 4
-Reg #38 primes_3 2
-Reg #39 nprimes_4 3
-Reg #40 %t40 40
-Reg #41 p_6 4
-Reg #42 N_4 0
-Reg #43 %t43 43
-Reg #44 ary_4 1
-Reg #45 %t45 45
-Reg #46 primes_4 2
-Reg #47 nprimes_5 3
-Reg #48 %t48 48
-Reg #49 nprimes_6 3
-Reg #50 %t50 50
-Reg #51 p_7 4
-Reg #52 p_8 4
-Reg #53 N_5 0
-Reg #54 ary_5 1
-Reg #55 primes_5 2
-Reg #56 nprimes_7 3
-Reg #57 %t57 57
-Reg #58 %t58 58
-Reg #59 j_1 6
-Reg #60 nprimes_8 3
-Reg #61 %t61 61
-Reg #62 primes_6 2
-Reg #63 rez_1 5
-Reg #64 %t64 64
-Reg #65 j_2 6
+Reg #36 ary_4 1
+Reg #37 %t37 37
+Reg #38 p_5 4
+Reg #39 p_6 4
+Reg #40 N_4 0
+Reg #41 ary_5 1
+Reg #42 primes_3 2
+Reg #43 nprimes_4 3
+Reg #44 %t44 44
+Reg #45 p_7 4
+Reg #46 N_5 0
+Reg #47 %t47 47
+Reg #48 ary_6 1
+Reg #49 %t49 49
+Reg #50 primes_4 2
+Reg #51 nprimes_5 3
+Reg #52 %t52 52
+Reg #53 nprimes_6 3
+Reg #54 %t54 54
+Reg #55 p_8 4
+Reg #56 p_9 4
+Reg #57 N_6 0
+Reg #58 N_7 0
+Reg #59 N_8 0
+Reg #60 N_9 0
+Reg #61 ary_7 1
+Reg #62 ary_8 1
+Reg #63 ary_9 1
+Reg #64 ary_10 1
+Reg #65 primes_5 2
+Reg #66 primes_6 2
+Reg #67 primes_7 2
+Reg #68 primes_8 2
+Reg #69 nprimes_7 3
+Reg #70 %t70 70
+Reg #71 %t71 71
+Reg #72 j_1 6
+Reg #73 nprimes_8 3
+Reg #74 %t74 74
+Reg #75 primes_9 2
+Reg #76 rez_1 5
+Reg #77 %t77 77
+Reg #78 j_2 6
+Reg #79 primes_10 2
+Reg #80 primes_11 2
+Reg #81 rez_2 5
 L0:
     arg N
     %t8 = New([Int], len=N, initValue=0)
@@ -1045,17 +1067,15 @@ func sieve(N: Int)->[Int]
     goto  L2
 L2:
     nprimes_2 = phi(nprimes, nprimes_3)
-    ary_2 = phi(ary, ary_3)
-    N_1 = phi(N, N_2)
-    p_1 = phi(p, p_5)
+    p_1 = phi(p, p_6)
     %t11 = p_1*p_1
-    %t13 = %t11<N_1
+    %t13 = %t11<N
     if %t13 goto L3 else goto L4
 L3:
     goto  L5
 L5:
     p_2 = phi(p_1, p_3)
-    %t15 = ary_2[p_2]
+    %t15 = ary[p_2]
     if %t15 goto L6 else goto L7
 L6:
     %t18 = p_2+1
@@ -1070,55 +1090,55 @@ func sieve(N: Int)->[Int]
     goto  L8
 L8:
     i_1 = phi(i, i_2)
-    %t28 = i_1<N_1
+    %t28 = i_1<N
     if %t28 goto L9 else goto L10
 L9:
-    ary_1[i_1] = 1
+    ary[i_1] = 1
     %t32 = i_1+p_2
     i_2 = %t32
     goto  L8
 L10:
-    %t36 = p_4+1
-    p_5 = %t36
+    %t37 = p_2+1
+    p_6 = %t37
     goto  L2
 L4:
     goto  L11
 L11:
     nprimes_5 = phi(nprimes_2, nprimes_7)
-    p_6 = phi(p_1, p_8)
-    %t40 = p_6<N_1
-    if %t40 goto L12 else goto L13
+    p_7 = phi(p_1, p_9)
+    %t44 = p_7<N
+    if %t44 goto L12 else goto L13
 L12:
-    %t43 = ary_2[p_6]
-    %t45 = !%t43
-    if %t45 goto L14 else goto L15
+    %t47 = ary[p_7]
+    %t49 = !%t47
+    if %t49 goto L14 else goto L15
 L14:
-    primes_2[nprimes_5] = p_6
-    %t48 = nprimes_5+1
-    nprimes_6 = %t48
+    primes[nprimes_5] = p_7
+    %t52 = nprimes_5+1
+    nprimes_6 = %t52
     goto  L15
 L15:
     nprimes_7 = phi(nprimes_5, nprimes_6)
-    %t50 = p_6+1
-    p_8 = %t50
+    %t54 = p_7+1
+    p_9 = %t54
     goto  L11
 L13:
-    %t57 = New([Int], len=nprimes_5, initValue=0)
-    rez = %t57
+    %t70 = New([Int], len=nprimes_5, initValue=0)
+    rez = %t70
     j = 0
     goto  L16
 L16:
     j_1 = phi(j, j_2)
-    %t58 = j_1<nprimes_5
-    if %t58 goto L17 else goto L18
+    %t71 = j_1<nprimes_5
+    if %t71 goto L17 else goto L18
 L17:
-    %t61 = primes_4[j_1]
-    rez[j_1] = %t61
-    %t64 = j_1+1
-    j_2 = %t64
+    %t74 = primes[j_1]
+    rez[j_1] = %t74
+    %t77 = j_1+1
+    j_2 = %t77
     goto  L16
 L18:
-    ret rez_1
+    ret rez
     goto  L1
 L1:
 func eq(a: [Int],b: [Int],n: Int)->Int

From 100f69466d0a9f32b873375b2434c8435917b097 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Sun, 25 May 2025 18:43:16 +0100
Subject: [PATCH 082/119] #53 When a phi goes dead all references to its def in
 the memoized set of defs must be replaced rather than removed thus avoiding
 unnecessary future phi creation?

---
 .../ezlang/compiler/CompiledFunction.java     |  19 +-
 .../ezlang/compiler/TestIncrementalSSA.java   | 201 ++++++++----------
 2 files changed, 94 insertions(+), 126 deletions(-)

diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
index 501e7d5..9ed46cf 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
@@ -965,14 +965,14 @@ private Register addPhiOperands(Register variable, Instruction.Phi phi) {
             return tryRemovingPhi(phi);
         }
 
-        // The Phi's def is dead so we need to remove
-        // all occurrences of this def from the memoized defs
+        // The Phi's def was replaced
+        // We must also replace all occurrences of the phi def from the memoized defs
         // per Basic Block
-        private void clearDefs(Instruction.Phi phi) {
+        private void replaceDefs(Instruction.Phi phi, Register newValue) {
             // TODO rethink the data structure for currentDef
             var def = phi.value();
             var defs = currentDef.get(def.nonSSAId());
-            // Make a list of block/reg that we need to delete
+            // Make a list of block/reg that we need to update
             var bbList = new ArrayList<BasicBlock>();
             var regList = new ArrayList<Register>();
             for (var entries : defs.entrySet()) {
@@ -983,14 +983,15 @@ private void clearDefs(Instruction.Phi phi) {
                     regList.add(reg);
                 }
             }
-            // Now delete them
+            // Now replace the phi def
             for (int i = 0; i < bbList.size(); i++) {
                 var bb = bbList.get(i);
                 var reg = regList.get(i);
-                defs.remove(bb, reg);
+                defs.replace(bb, reg, newValue);
             }
         }
 
+        // reference implementation https://github.com/dibyendumajumdar/libfirm/blob/master/ir/ir/ircons.c#L97
         private Register tryRemovingPhi(Instruction.Phi phi) {
             Register same = null;
             // Check if phi has distinct inputs
@@ -1017,9 +1018,6 @@ private Register tryRemovingPhi(Instruction.Phi phi) {
             // remove all uses of phi to same and remove phi
             replacePhiValueAndUsers(phi, same);
             phi.block.deleteInstruction(phi);
-            // Since the phi is dead any references to its def
-            // must be removed; this is not mentioned in the paper
-            clearDefs(phi);
             // try to recursively remove all phi users, which might have become trivial
             for (var use: users) {
                 if (use instanceof Instruction.Phi phiuser)
@@ -1055,6 +1053,9 @@ private void replacePhiValueAndUsers(Instruction.Phi phi, Register newValue) {
                 newDefUseChain.useList.addAll(oldDefUseChain.useList);
                 oldDefUseChain.useList.clear();
             }
+            // Since the phi is replaced by newvalue
+            // we must also update the memoized defs
+            replaceDefs(phi, newValue);
         }
 
         private List<Instruction> getUsesExcept(Instruction.Phi phi) {
diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestIncrementalSSA.java b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestIncrementalSSA.java
index e3a5272..453b674 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestIncrementalSSA.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestIncrementalSSA.java
@@ -250,44 +250,32 @@ func example14_66(p: Int,q: Int,r: Int,s: Int,t: Int)
 Reg #51 i_5 5
 Reg #52 i_6 5
 Reg #53 i_7 5
-Reg #54 i_8 5
-Reg #55 %t55 55
-Reg #56 t_1 4
-Reg #57 t_2 4
-Reg #58 t_3 4
-Reg #59 t_4 4
-Reg #60 t_5 4
-Reg #61 t_6 4
-Reg #62 t_7 4
-Reg #63 p_2 0
-Reg #64 p_3 0
-Reg #65 p_4 0
-Reg #66 p_5 0
-Reg #67 p_6 0
-Reg #68 p_7 0
-Reg #69 q_2 1
-Reg #70 q_3 1
-Reg #71 q_4 1
-Reg #72 q_5 1
-Reg #73 q_6 1
-Reg #74 q_7 1
-Reg #75 r_6 2
-Reg #76 r_7 2
-Reg #77 r_8 2
-Reg #78 r_9 2
-Reg #79 s_6 3
-Reg #80 s_7 3
-Reg #81 s_8 3
-Reg #82 s_9 3
-Reg #83 j_5 6
-Reg #84 j_6 6
-Reg #85 j_7 6
-Reg #86 j_8 6
-Reg #87 k_6 7
-Reg #88 k_7 7
-Reg #89 k_8 7
-Reg #90 k_9 7
-Reg #91 l_9 8
+Reg #54 %t54 54
+Reg #55 t_1 4
+Reg #56 t_2 4
+Reg #57 t_3 4
+Reg #58 t_4 4
+Reg #59 t_5 4
+Reg #60 t_6 4
+Reg #61 p_2 0
+Reg #62 p_3 0
+Reg #63 p_4 0
+Reg #64 p_5 0
+Reg #65 p_6 0
+Reg #66 q_2 1
+Reg #67 q_3 1
+Reg #68 q_4 1
+Reg #69 q_5 1
+Reg #70 q_6 1
+Reg #71 r_6 2
+Reg #72 s_6 3
+Reg #73 j_5 6
+Reg #74 j_6 6
+Reg #75 j_7 6
+Reg #76 k_6 7
+Reg #77 k_7 7
+Reg #78 k_8 7
+Reg #79 l_9 8
 L0:
     arg p
     arg q
@@ -303,7 +291,7 @@ func example14_66(p: Int,q: Int,r: Int,s: Int,t: Int)
     l_5 = phi(l, l_9)
     j_4 = phi(j, j_2)
     k_2 = phi(k, k_5)
-    i_1 = phi(i, i_8)
+    i_1 = phi(i, i_7)
     if 1 goto L3 else goto L4
 L3:
     if p goto L5 else goto L6
@@ -346,9 +334,9 @@ func example14_66(p: Int,q: Int,r: Int,s: Int,t: Int)
 L13:
     l_9 = phi(l_6, l_8)
     %t49 = i_1+6
-    i_8 = %t49
-    %t55 = !t
-    if %t55 goto L18 else goto L19
+    i_7 = %t49
+    %t54 = !t
+    if %t54 goto L18 else goto L19
 L18:
     goto  L4
 L4:
@@ -659,9 +647,6 @@ func main()->Int
 Reg #25 i_4 1
 Reg #26 %t26 26
 Reg #27 i_5 1
-Reg #28 i_6 1
-Reg #29 i_7 1
-Reg #30 i_8 1
 L0:
     a = 0
     i = 0
@@ -669,7 +654,7 @@ func main()->Int
     goto  L2
 L2:
     a_4 = phi(a, a_1)
-    i_1 = phi(i, i_8)
+    i_1 = phi(i, i_5)
     %t3 = i_1<3
     if %t3 goto L3 else goto L4
 L3:
@@ -705,7 +690,7 @@ func main()->Int
     goto  L10
 L7:
     %t26 = i_1+1
-    i_8 = %t26
+    i_5 = %t26
     goto  L2
 L4:
     ret a_4
@@ -863,8 +848,6 @@ func bug(N: Int)
 Reg #11 N_3 0
 Reg #12 %t12 12
 Reg #13 p_5 1
-Reg #14 N_4 0
-Reg #15 N_5 0
 L0:
     arg N
     p = 2
@@ -1009,52 +992,36 @@ func sieve(N: Int)->[Int]
 Reg #33 p_4 4
 Reg #34 i_2 7
 Reg #35 N_3 0
-Reg #36 ary_4 1
-Reg #37 %t37 37
-Reg #38 p_5 4
-Reg #39 p_6 4
-Reg #40 N_4 0
-Reg #41 ary_5 1
-Reg #42 primes_3 2
-Reg #43 nprimes_4 3
-Reg #44 %t44 44
-Reg #45 p_7 4
-Reg #46 N_5 0
-Reg #47 %t47 47
-Reg #48 ary_6 1
-Reg #49 %t49 49
-Reg #50 primes_4 2
-Reg #51 nprimes_5 3
-Reg #52 %t52 52
-Reg #53 nprimes_6 3
-Reg #54 %t54 54
-Reg #55 p_8 4
-Reg #56 p_9 4
-Reg #57 N_6 0
-Reg #58 N_7 0
-Reg #59 N_8 0
-Reg #60 N_9 0
-Reg #61 ary_7 1
-Reg #62 ary_8 1
-Reg #63 ary_9 1
-Reg #64 ary_10 1
-Reg #65 primes_5 2
-Reg #66 primes_6 2
-Reg #67 primes_7 2
-Reg #68 primes_8 2
-Reg #69 nprimes_7 3
-Reg #70 %t70 70
-Reg #71 %t71 71
-Reg #72 j_1 6
-Reg #73 nprimes_8 3
-Reg #74 %t74 74
-Reg #75 primes_9 2
-Reg #76 rez_1 5
-Reg #77 %t77 77
-Reg #78 j_2 6
-Reg #79 primes_10 2
-Reg #80 primes_11 2
-Reg #81 rez_2 5
+Reg #36 %t36 36
+Reg #37 p_5 4
+Reg #38 primes_3 2
+Reg #39 nprimes_4 3
+Reg #40 %t40 40
+Reg #41 p_6 4
+Reg #42 N_4 0
+Reg #43 %t43 43
+Reg #44 ary_4 1
+Reg #45 %t45 45
+Reg #46 primes_4 2
+Reg #47 nprimes_5 3
+Reg #48 %t48 48
+Reg #49 nprimes_6 3
+Reg #50 %t50 50
+Reg #51 p_7 4
+Reg #52 p_8 4
+Reg #53 N_5 0
+Reg #54 ary_5 1
+Reg #55 primes_5 2
+Reg #56 nprimes_7 3
+Reg #57 %t57 57
+Reg #58 %t58 58
+Reg #59 j_1 6
+Reg #60 nprimes_8 3
+Reg #61 %t61 61
+Reg #62 primes_6 2
+Reg #63 rez_1 5
+Reg #64 %t64 64
+Reg #65 j_2 6
 L0:
     arg N
     %t8 = New([Int], len=N, initValue=0)
@@ -1067,7 +1034,7 @@ func sieve(N: Int)->[Int]
     goto  L2
 L2:
     nprimes_2 = phi(nprimes, nprimes_3)
-    p_1 = phi(p, p_6)
+    p_1 = phi(p, p_5)
     %t11 = p_1*p_1
     %t13 = %t11<N
     if %t13 goto L3 else goto L4
@@ -1098,44 +1065,44 @@ func sieve(N: Int)->[Int]
     i_2 = %t32
     goto  L8
 L10:
-    %t37 = p_2+1
-    p_6 = %t37
+    %t36 = p_2+1
+    p_5 = %t36
     goto  L2
 L4:
     goto  L11
 L11:
     nprimes_5 = phi(nprimes_2, nprimes_7)
-    p_7 = phi(p_1, p_9)
-    %t44 = p_7<N
-    if %t44 goto L12 else goto L13
+    p_6 = phi(p_1, p_8)
+    %t40 = p_6<N
+    if %t40 goto L12 else goto L13
 L12:
-    %t47 = ary[p_7]
-    %t49 = !%t47
-    if %t49 goto L14 else goto L15
+    %t43 = ary[p_6]
+    %t45 = !%t43
+    if %t45 goto L14 else goto L15
 L14:
-    primes[nprimes_5] = p_7
-    %t52 = nprimes_5+1
-    nprimes_6 = %t52
+    primes[nprimes_5] = p_6
+    %t48 = nprimes_5+1
+    nprimes_6 = %t48
     goto  L15
 L15:
     nprimes_7 = phi(nprimes_5, nprimes_6)
-    %t54 = p_7+1
-    p_9 = %t54
+    %t50 = p_6+1
+    p_8 = %t50
     goto  L11
 L13:
-    %t70 = New([Int], len=nprimes_5, initValue=0)
-    rez = %t70
+    %t57 = New([Int], len=nprimes_5, initValue=0)
+    rez = %t57
     j = 0
     goto  L16
 L16:
     j_1 = phi(j, j_2)
-    %t71 = j_1<nprimes_5
-    if %t71 goto L17 else goto L18
+    %t58 = j_1<nprimes_5
+    if %t58 goto L17 else goto L18
 L17:
-    %t74 = primes[j_1]
-    rez[j_1] = %t74
-    %t77 = j_1+1
-    j_2 = %t77
+    %t61 = primes[j_1]
+    rez[j_1] = %t61
+    %t64 = j_1+1
+    j_2 = %t64
     goto  L16
 L18:
     ret rez

From 266d017169d840387cdd304b7514b2226257aba7 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Sun, 25 May 2025 18:52:46 +0100
Subject: [PATCH 083/119] #53 Simplify impl

---
 .../ezlang/compiler/CompiledFunction.java     | 27 ++++---------------
 1 file changed, 5 insertions(+), 22 deletions(-)

diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
index 9ed46cf..9731d25 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
@@ -968,27 +968,10 @@ private Register addPhiOperands(Register variable, Instruction.Phi phi) {
         // The Phi's def was replaced
         // We must also replace all occurrences of the phi def from the memoized defs
         // per Basic Block
-        private void replaceDefs(Instruction.Phi phi, Register newValue) {
-            // TODO rethink the data structure for currentDef
-            var def = phi.value();
-            var defs = currentDef.get(def.nonSSAId());
-            // Make a list of block/reg that we need to update
-            var bbList = new ArrayList<BasicBlock>();
-            var regList = new ArrayList<Register>();
-            for (var entries : defs.entrySet()) {
-                var bb = entries.getKey();
-                var reg = entries.getValue();
-                if (reg.equals(def)) {
-                    bbList.add(bb);
-                    regList.add(reg);
-                }
-            }
-            // Now replace the phi def
-            for (int i = 0; i < bbList.size(); i++) {
-                var bb = bbList.get(i);
-                var reg = regList.get(i);
-                defs.replace(bb, reg, newValue);
-            }
+        private void replaceDefs(Register oldValue, Register newValue) {
+            var defs = currentDef.get(oldValue.nonSSAId());
+            for (var bb: defs.keySet())
+                defs.replace(bb, oldValue, newValue);
         }
 
         // reference implementation https://github.com/dibyendumajumdar/libfirm/blob/master/ir/ir/ircons.c#L97
@@ -1055,7 +1038,7 @@ private void replacePhiValueAndUsers(Instruction.Phi phi, Register newValue) {
             }
             // Since the phi is replaced by newvalue
             // we must also update the memoized defs
-            replaceDefs(phi, newValue);
+            replaceDefs(oldValue, newValue);
         }
 
         private List<Instruction> getUsesExcept(Instruction.Phi phi) {

From ba4e9c19892b0193d9697d3082ef42e958f97fe0 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Sun, 25 May 2025 18:55:30 +0100
Subject: [PATCH 084/119] #53 Move method

---
 .../ezlang/compiler/CompiledFunction.java      | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
index 9731d25..b1a2b45 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
@@ -965,15 +965,6 @@ private Register addPhiOperands(Register variable, Instruction.Phi phi) {
             return tryRemovingPhi(phi);
         }
 
-        // The Phi's def was replaced
-        // We must also replace all occurrences of the phi def from the memoized defs
-        // per Basic Block
-        private void replaceDefs(Register oldValue, Register newValue) {
-            var defs = currentDef.get(oldValue.nonSSAId());
-            for (var bb: defs.keySet())
-                defs.replace(bb, oldValue, newValue);
-        }
-
         // reference implementation https://github.com/dibyendumajumdar/libfirm/blob/master/ir/ir/ircons.c#L97
         private Register tryRemovingPhi(Instruction.Phi phi) {
             Register same = null;
@@ -1051,6 +1042,15 @@ private List<Instruction> getUsesExcept(Instruction.Phi phi) {
             return useList;
         }
 
+        // The Phi's def was replaced
+        // We must also replace all occurrences of the phi def from the memoized defs
+        // per Basic Block
+        private void replaceDefs(Register oldValue, Register newValue) {
+            var defs = currentDef.get(oldValue.nonSSAId());
+            for (var bb: defs.keySet())
+                defs.replace(bb, oldValue, newValue);
+        }
+
         @Override
         public Operand read(Operand operand) {
             // We have to consider temps too because of boolean expressions

From f9be158efdff30c7e4a1f071a920b1bd2ad4a42c Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Sun, 25 May 2025 19:16:45 +0100
Subject: [PATCH 085/119] Update README

---
 README.md | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/README.md b/README.md
index 430ba59..c15ed27 100644
--- a/README.md
+++ b/README.md
@@ -24,12 +24,12 @@ The project is under development and subject to change. At this point in time, w
 * [parser](./parser/README.md) - a recursive descent parser and AST.
 * [types](/types/README.md) - the type definitions.
 * [semantic](./semantic/README.md) - semantic analyzer.
-* [stackvm](./stackvm/README.md) - a compiler that generates IR for a stack based virtual machine.
+* [stackvm](./stackvm/README.md) - a compiler that generates IR for a stack based virtual machine. There is no interpreter for this instruction set yet.
 * [registervm](./registervm/README.md) - a compiler that generates a so called three-address IR and an interpreter that can execute the IR.
 * [optvm](./optvm/README.md) - WIP optimizing compiler with SSA transformation, constant propagation, graph coloring register allocation 
   targeting an abstract machine. Includes Interpreter to run the abstract machine.
-* [seaofnodes](./seaofnodes/README.md) - WIP compiler that generates Sea of Nodes IR, using SoN backend from [Simple](https://github.com/SeaOfNodes/Simple).
-  Will generate native code.
+* [seaofnodes](./seaofnodes/README.md) - WIP compiler that generates Sea of Nodes IR, using SoN backend from [Simple Chapter 21](https://github.com/SeaOfNodes/Simple).
+  Generate native code for X86-64, AArch64 and RISC-V.
 
 ## How can you contribute?
 

From f6d58a9774fb0258f2c179966f7480d5956dec2a Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Sun, 25 May 2025 20:10:38 +0100
Subject: [PATCH 086/119] Update README

---
 README.md       |  2 +-
 optvm/README.md | 35 ++++++++++++++++++++++++++++-------
 2 files changed, 29 insertions(+), 8 deletions(-)

diff --git a/README.md b/README.md
index c15ed27..5a47f23 100644
--- a/README.md
+++ b/README.md
@@ -29,7 +29,7 @@ The project is under development and subject to change. At this point in time, w
 * [optvm](./optvm/README.md) - WIP optimizing compiler with SSA transformation, constant propagation, graph coloring register allocation 
   targeting an abstract machine. Includes Interpreter to run the abstract machine.
 * [seaofnodes](./seaofnodes/README.md) - WIP compiler that generates Sea of Nodes IR, using SoN backend from [Simple Chapter 21](https://github.com/SeaOfNodes/Simple).
-  Generate native code for X86-64, AArch64 and RISC-V.
+  Generates native code for X86-64, AArch64 and RISC-V.
 
 ## How can you contribute?
 
diff --git a/optvm/README.md b/optvm/README.md
index 6775bd6..fad1ad9 100644
--- a/optvm/README.md
+++ b/optvm/README.md
@@ -11,7 +11,7 @@ a physical machine. Therefore, all our optimization passes will work on the inst
 
 A VM / Interpreter is provided that can run the generated code.
 
-## Guide
+## Intermediate Representation
 
 * [Register](src/main/java/com/compilerprogramming/ezlang/compiler/Register.java) - implements a virtual register. Virtual registers
   have a name, type, id, frameSlot - the id is unique, but the name is not. Initially the compiler generates unique registers for every local
@@ -28,22 +28,43 @@ A VM / Interpreter is provided that can run the generated code.
   as operands. Instruction can have at most one definition; but an instruction can have multiple use operands. Operands can hold registers or
   constants or pointers to basic blocks.
 * [Instruction](src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java) - Instructions - sequential instructions reside in
-  basic blocks. Some instructions define variables (registers) and some use them. 
+  basic blocks. Some instructions define variables (registers) and some use them.
+
+## Dominators and Liveness Analysis
+
 * [DominatorTree](src/main/java/com/compilerprogramming/ezlang/compiler/DominatorTree.java) - Calculates dominator tree and dominance frontiers.
 * [LiveSet](src/main/java/com/compilerprogramming/ezlang/compiler/LiveSet.java) - Bitset used to track liveness of registers. We exploit the fact that 
   each register has a unique integer ID and these ids are allocated in a sequential manner.
 * [Liveness](src/main/java/com/compilerprogramming/ezlang/compiler/Liveness.java) - Liveness calculator, works for both SSA and non-SSA forms. Computes
   liveness data per basic block - mainly live-out. Note that the interference graph builder starts here and computes instruction level liveness as necessary.
-* [EnterSSA](src/main/java/com/compilerprogramming/ezlang/compiler/EnterSSA.java) - Transforms into SSA, using [algorithm by Preston Briggs](https://dl.acm.org/doi/10.5555/295545.295551). This is one of available mechanisms to transform the IR into SSA. 
-  The other alternative is to generate SSA IR directly from the AST, using [Braun's method](https://dl.acm.org/doi/10.1007/978-3-642-37051-9_6) - this option is integrated into the
-  [compiler]((src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java)) and enabled using an option.
+
+## Static Single Assignment Form
+
+* [EnterSSA](src/main/java/com/compilerprogramming/ezlang/compiler/EnterSSA.java) - Transforms into SSA, using [algorithm by Preston Briggs](https://dl.acm.org/doi/10.5555/295545.295551). This is the traditional method of constructing 
+  SSA Form using Dominator Trees. The input to this transformation is regular IR, output is SSA IR.
+* Incremental SSA - This method generate SSA IR directly from the AST, using [Braun's algorithm](https://dl.acm.org/doi/10.1007/978-3-642-37051-9_6) - this is integrated into the
+  [compiler]((src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java)) itself and can be enabled using an option.
 * [ExitSSA](src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA.java) - Exits SSA form, using algorithm by Preston Briggs.
+
+## Optimizations on SSA Form
+
 * [SparseConditionalConstantPropagation](src/main/java/com/compilerprogramming/ezlang/compiler/SparseConditionalConstantPropagation.java) - Conditional Constant Propagation on SSA form (SCCP)
 * [ConstantComparisonPropagation](src/main/java/com/compilerprogramming/ezlang/compiler/ConstantComparisonPropagation.java) - Detects equals and not equals against constants within conditionals,
-   and inserts variables with appropriately specialized type within the dominated blocks, so that a second pass of SCCP can further optimize code.
+   and inserts scoped variables with appropriately specialized type within the dominated blocks, so that a second pass of SCCP can further optimize code.
 * [SSAEdges](src/main/java/com/compilerprogramming/ezlang/compiler/SSAEdges.java) - SSAEdges are def-use chains used by SCCP algorithm, and also generated during incremental SSA construction using Braun's method.
+
+## Loops 
+
+These components are not used yet
+
 * [LoopFinder](src/main/java/com/compilerprogramming/ezlang/compiler/LoopFinder.java) - Discovers loops. (Not used yet)
 * [LoopNest](src/main/java/com/compilerprogramming/ezlang/compiler/LoopNest.java) - Representation of loop nesting. (Not used yet)
+
+## Chaitin Graph Coloring Register Allocation
+
+Note that our goal is to target an abstract machine rather than a real CPU, so we optimize for minimum VM registers, but we have 
+unlimited amount of those.
+
 * [InterferenceGraph](src/main/java/com/compilerprogramming/ezlang/compiler/InterferenceGraph.java) - Representation of an Interference Graph
   required by the register allocator.
 * [InterferenceGraphBuilder](src/main/java/com/compilerprogramming/ezlang/compiler/InterferenceGraphBuilder.java) - Constructs an InterferenceGraph for a set
@@ -63,6 +84,6 @@ A VM / Interpreter is provided that can run the generated code.
 ## VM/Interpreter
 
 A simple VM / Interpreter is provided that can run the IR, both pre and post optimizations.
-The SSA form is not executable, it must be transformed out of SSA for execuation.
+The SSA form is not executable, hence the IR must be transformed out of SSA for execution.
 
 * [VM/Interpreter](src/main/java/com/compilerprogramming/ezlang/interpreter)

From 2ed09da65d43c3880b6d990b22f21f31cc42db20 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Sun, 25 May 2025 20:20:22 +0100
Subject: [PATCH 087/119] Fix link

---
 optvm/README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/optvm/README.md b/optvm/README.md
index fad1ad9..dc96951 100644
--- a/optvm/README.md
+++ b/optvm/README.md
@@ -43,7 +43,7 @@ A VM / Interpreter is provided that can run the generated code.
 * [EnterSSA](src/main/java/com/compilerprogramming/ezlang/compiler/EnterSSA.java) - Transforms into SSA, using [algorithm by Preston Briggs](https://dl.acm.org/doi/10.5555/295545.295551). This is the traditional method of constructing 
   SSA Form using Dominator Trees. The input to this transformation is regular IR, output is SSA IR.
 * Incremental SSA - This method generate SSA IR directly from the AST, using [Braun's algorithm](https://dl.acm.org/doi/10.1007/978-3-642-37051-9_6) - this is integrated into the
-  [compiler]((src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java)) itself and can be enabled using an option.
+  [compiler](src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java) itself and can be enabled using an option.
 * [ExitSSA](src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA.java) - Exits SSA form, using algorithm by Preston Briggs.
 
 ## Optimizations on SSA Form

From a99053fc90489104792df495c1febde8a4fb3b60 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Mon, 26 May 2025 00:28:34 +0100
Subject: [PATCH 088/119] A quicksort test case

---
 .../ezlang/interpreter/TestInterpreter.java   | 63 +++++++++++++++++++
 .../ezlang/interpreter/TestInterpreter.java   | 63 +++++++++++++++++++
 2 files changed, 126 insertions(+)

diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java b/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java
index 456d232..9e54bfa 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java
@@ -794,4 +794,67 @@ func main()->Int
         Assert.assertTrue(value instanceof Value.IntegerValue integerValue &&
                 integerValue.value == 1);
     }
+
+    @Test
+    public void testFunction110() {
+        String src = """
+func swap(arr: [Int], i: Int, j: Int) {
+    var tmp = arr[i];
+    arr[i] = arr[j];
+    arr[j] = tmp;
+}
+
+func partition(arr: [Int], low: Int, high: Int)->Int {
+    var pivot = arr[high];
+    var i = low;
+    var j = low;
+    while (j < high) {
+        if (arr[j] < pivot) {
+            swap(arr, i, j);
+            i = i + 1;
+        }
+        j = j + 1;
+    }
+    swap(arr, i, high);
+    return i;
+}
+
+func quicksort(arr: [Int], low: Int, high: Int) {
+    if (low < high) {
+        var p = partition(arr, low, high);
+        quicksort(arr, low, p - 1);
+        quicksort(arr, p + 1, high);
+    }
+}
+
+func eq(a: [Int], b: [Int], n: Int)->Int
+{
+    var result = 1
+    var i = 0
+    while (i < n)
+    {
+        if (a[i] != b[i])
+        {
+            result = 0
+            break
+        }
+        i = i + 1
+    }
+    return result
+}
+
+func main()->Int
+{
+    var nums = new [Int]{33, 10, 55, 71, 29, 3};
+    var expected = new [Int]{3,10,29,33,55,71}
+    quicksort(nums, 0, 5);
+    return eq(nums,expected,6)
+}
+""";
+        var value = compileAndRun(src, "main");
+        Assert.assertNotNull(value);
+        Assert.assertTrue(value instanceof Value.IntegerValue integerValue &&
+                integerValue.value == 1);
+    }
+
 }
diff --git a/registervm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java b/registervm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java
index 3667caa..4734aff 100644
--- a/registervm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java
+++ b/registervm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java
@@ -434,4 +434,67 @@ func main()->Int
         Assert.assertTrue(value instanceof Value.IntegerValue integerValue &&
                 integerValue.value == 1);
     }
+
+    @Test
+    public void testFunction110() {
+        String src = """
+func swap(arr: [Int], i: Int, j: Int) {
+    var tmp = arr[i];
+    arr[i] = arr[j];
+    arr[j] = tmp;
+}
+
+func partition(arr: [Int], low: Int, high: Int)->Int {
+    var pivot = arr[high];
+    var i = low;
+    var j = low;
+    while (j < high) {
+        if (arr[j] < pivot) {
+            swap(arr, i, j);
+            i = i + 1;
+        }
+        j = j + 1;
+    }
+    swap(arr, i, high);
+    return i;
+}
+
+func quicksort(arr: [Int], low: Int, high: Int) {
+    if (low < high) {
+        var p = partition(arr, low, high);
+        quicksort(arr, low, p - 1);
+        quicksort(arr, p + 1, high);
+    }
+}
+
+func eq(a: [Int], b: [Int], n: Int)->Int
+{
+    var result = 1
+    var i = 0
+    while (i < n)
+    {
+        if (a[i] != b[i])
+        {
+            result = 0
+            break
+        }
+        i = i + 1
+    }
+    return result
+}
+
+func main()->Int
+{
+    var nums = new [Int]{33, 10, 55, 71, 29, 3};
+    var expected = new [Int]{3,10,29,33,55,71}
+    quicksort(nums, 0, 5);
+    return eq(nums,expected,6)
+}
+""";
+        var value = compileAndRun(src, "main");
+        Assert.assertNotNull(value);
+        Assert.assertTrue(value instanceof Value.IntegerValue integerValue &&
+                integerValue.value == 1);
+    }
+
 }

From aeeb7304e96dd97dfcb05a40a1bfcf2cef6dd87b Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <dibyendu@redukti.com>
Date: Sat, 7 Jun 2025 15:17:16 +0100
Subject: [PATCH 089/119] Before SSA ID is the nonSSAID

---
 .../com/compilerprogramming/ezlang/compiler/Register.java | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Register.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Register.java
index 42895b9..9f87393 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Register.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Register.java
@@ -78,13 +78,11 @@ public String name() {
     }
 
     /**
-     * The nonSSAID is valid as a frame slot prior to SSA conversion,
-     * and following register assignment.
-     * During SSA form this is not valid for registers that are instances of SSARegister.
+     * The ID shared by all versions of a Register.
+     * @see SSARegister
      */
     public int nonSSAId() {
-        //assert frameSlot >= 0; // assert inteferes with verbose display
-        return frameSlot;
+        return id;
     }
     public void updateSlot(int slot) {
         this.frameSlot = slot;

From ce25e480a513e516a56aa4f9549f0ea2a4f2b2e8 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Sat, 7 Jun 2025 23:13:48 +0100
Subject: [PATCH 090/119] some cleanups

---
 .../ezlang/compiler/Instruction.java          | 56 +++++++++++++------
 .../SparseConditionalConstantPropagation.java |  5 +-
 2 files changed, 41 insertions(+), 20 deletions(-)

diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java
index 0aa50ff..f7a3c6b 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java
@@ -1,5 +1,6 @@
 package com.compilerprogramming.ezlang.compiler;
 
+import com.compilerprogramming.ezlang.exceptions.CompilerException;
 import com.compilerprogramming.ezlang.types.Type;
 
 import java.util.ArrayList;
@@ -30,11 +31,10 @@ public abstract class Instruction {
     protected Operand[] uses;
     public BasicBlock block;
 
-    protected Instruction(int opcode, Operand... uses) {
+    protected Instruction(int opcode) {
         this.opcode = opcode;
         this.def = null;
-        this.uses = new Operand[uses.length];
-        System.arraycopy(uses, 0, this.uses, 0, uses.length);
+        this.uses = new Operand[0];
     }
     protected Instruction(int opcode, Operand.RegisterOperand def, Operand... uses) {
         this.opcode = opcode;
@@ -49,9 +49,23 @@ public String toString() {
         return toStr(new StringBuilder()).toString();
     }
 
+    /**
+     * Does this instruction define a var?
+     */
     public boolean definesVar() { return def != null; }
+    /**
+     * If the instruction defines a var then return the Register else null
+     */
     public Register def() { return def != null ? def.reg: null; }
+    public void replaceDef(Register newDef) {
+        if (def == null) throw new IllegalStateException();
+        def = def.copy(newDef);
+    }
 
+    /**
+     * Get the registers used by this instruction. Non register operands are
+     * not included.
+     */
     public List<Register> uses() {
         List<Register> useList = null;
         for (int i = 0; i < uses.length; i++) {
@@ -64,10 +78,14 @@ public List<Register> uses() {
         if (useList == null) useList = Collections.emptyList();
         return useList;
     }
-    public void replaceDef(Register newReg) {
-        if (def == null) throw new IllegalStateException();
-        def = def.copy(newReg);
-    }
+
+    /**
+     * Replaces existing register uses with new ones. This api is not great
+     * as it requires user to supply registers in the same order as returned by
+     * the method {@link #uses()}.
+     *
+     * FIXME replace this with a better api
+     */
     public void replaceUses(Register[] newUses) {
         int j = 0;
         for (int i = 0; i < uses.length; i++) {
@@ -76,7 +94,14 @@ public void replaceUses(Register[] newUses) {
                 uses[i] = registerOperand.copy(newUses[j++]);
             }
         }
+        // Sanity check that we replaced the full set of registers
+        if (j != newUses.length)
+            throw new CompilerException("Error - supplied registers do not replace all uses");
     }
+
+    /**
+     * Replaces all occurrences of source with target in the uses list of the instruction
+     */
     public boolean replaceUse(Register source, Register target) {
         boolean replaced = false;
         for (int i = 0; i < uses.length; i++) {
@@ -88,7 +113,11 @@ public boolean replaceUse(Register source, Register target) {
         }
         return replaced;
     }
-    public void replaceWithConstant(Register register, Operand.ConstantOperand constantOperand) {
+
+    /**
+     * Replaces all uses of given register with the constant operand
+     */
+    public void replaceUseWithConstant(Register register, Operand.ConstantOperand constantOperand) {
         for (int i = 0; i < uses.length; i++) {
             Operand operand = uses[i];
             if (operand != null && operand instanceof Operand.RegisterOperand registerOperand && registerOperand.reg.id == register.id) {
@@ -96,15 +125,6 @@ public void replaceWithConstant(Register register, Operand.ConstantOperand const
             }
         }
     }
-    public static class NoOp extends Instruction {
-        public NoOp() {
-            super(I_NOOP);
-        }
-        @Override
-        public StringBuilder toStr(StringBuilder sb) {
-            return sb.append("noop");
-        }
-    }
 
     public static class Move extends Instruction {
         public Move(Operand from, Operand to) {
@@ -384,7 +404,7 @@ public Register def() {
             throw new UnsupportedOperationException();
         }
         @Override
-        public void replaceDef(Register newReg) {
+        public void replaceDef(Register newDef) {
             throw new UnsupportedOperationException();
         }
         @Override
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SparseConditionalConstantPropagation.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SparseConditionalConstantPropagation.java
index 38f0bc6..36edb74 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SparseConditionalConstantPropagation.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SparseConditionalConstantPropagation.java
@@ -88,7 +88,8 @@ private void visitInstruction(Instruction instruction) {
                         flowWorklist.push(s);
                     }
                 }
-            } else if (instruction.definesVar() || instruction instanceof Instruction.Phi) {
+            }
+            else if (instruction.definesVar() || instruction instanceof Instruction.Phi) {
                 var def = instruction instanceof Instruction.Phi phi ? phi.value() : instruction.def();
                 // Push all uses (instructions) of the def into the worklist
                 SSAEdges.SSADef ssaDef = ssaEdges.get(def);
@@ -207,7 +208,7 @@ private void replaceVarsWithConstants() {
                 // replace uses with constant
                 for (var usingInstruction: defUseChain.useList) {
                     if (executableBlocks.get(usingInstruction.block.bid))
-                        usingInstruction.replaceWithConstant(register, constant);
+                        usingInstruction.replaceUseWithConstant(register, constant);
                 }
                 defUseChain.useList.clear();
                 var block = defUseChain.instruction.block;

From 17ba8a2b81e60cb6d4c521e1466146d142bb1b02 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <dibyendu@redukti.com>
Date: Sat, 9 Aug 2025 20:50:15 +0100
Subject: [PATCH 091/119] #54 If a cbr instruction follows a copy instruction
 created due to phi removal, then update the cbr instruction to use the
 replacement

---
 .../com/compilerprogramming/ezlang/compiler/ExitSSA.java   | 7 +++++++
 .../ezlang/compiler/TestSSATransform.java                  | 2 +-
 2 files changed, 8 insertions(+), 1 deletion(-)

diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA.java
index 0828f98..af66aff 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA.java
@@ -245,6 +245,13 @@ private CopyItem isCycle(List<CopyItem> copySet, Register src) {
     private void addMoveAtBBEnd(BasicBlock block, Register src, Register dest) {
         var inst = new Instruction.Move(new Operand.RegisterOperand(src), new Operand.RegisterOperand(dest));
         insertAtEnd(block, inst);
+        // If the copy instruction is followed by a cbr which uses the old var
+        // then we need to update the cbr instruction
+        // This is not specified in the Briggs paper but t
+        var brInst = block.instructions.getLast();
+        if (brInst instanceof Instruction.ConditionalBranch cbr) {
+            cbr.replaceUse(src,dest);
+        }
     }
     /* Insert a copy from constant src to dst at end of BB */
     private void addMoveAtBBEnd(BasicBlock block, Operand.ConstantOperand src, Register dest) {
diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
index 02aeb01..8897512 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
@@ -3156,7 +3156,7 @@ func bug(N: Int)
     if %t2_0 goto L3 else goto L4
 L3:
     p_5 = p_1
-    if p_1 goto L5 else goto L6
+    if p_5 goto L5 else goto L6
 L5:
     %t3_0 = p_1+1
     p_4 = %t3_0

From 1881b277aa81bf6dab7fa4a02235e6e80bf41a5d Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Sun, 27 Jul 2025 17:42:00 +0100
Subject: [PATCH 092/119] Rename Type to EZType - and all sub types too so that
 it is easy to merge code from Simple

---
 .../ezlang/compiler/CompiledFunction.java     | 56 ++++++-------
 .../ezlang/compiler/Compiler.java             |  4 +-
 .../ezlang/compiler/Instruction.java          | 18 ++--
 .../ezlang/compiler/Operand.java              | 12 +--
 .../ezlang/compiler/Register.java             |  8 +-
 .../ezlang/compiler/RegisterPool.java         |  8 +-
 .../SparseConditionalConstantPropagation.java |  4 +-
 .../ezlang/interpreter/Interpreter.java       |  6 +-
 .../ezlang/interpreter/Value.java             | 10 +--
 .../compiler/TestInterferenceGraph.java       |  8 +-
 .../ezlang/compiler/TestLiveness.java         |  4 +-
 .../ezlang/compiler/TestSSATransform.java     |  7 +-
 .../ezlang/parser/AST.java                    |  4 +-
 .../ezlang/compiler/CompiledFunction.java     | 44 +++++-----
 .../ezlang/compiler/Compiler.java             |  4 +-
 .../ezlang/compiler/Instruction.java          | 18 ++--
 .../ezlang/compiler/Operand.java              | 14 ++--
 .../ezlang/interpreter/Interpreter.java       |  6 +-
 .../ezlang/interpreter/Value.java             | 10 +--
 .../ezlang/compiler/Compiler.java             | 50 +++++------
 .../ezlang/semantic/SemaAssignTypes.java      | 82 +++++++++----------
 .../ezlang/semantic/SemaDefineTypes.java      | 30 +++----
 .../ezlang/compiler/CompiledFunction.java     | 16 ++--
 .../ezlang/compiler/Compiler.java             |  4 +-
 .../ezlang/compiler/Instruction.java          | 10 +--
 .../ezlang/types/{Type.java => EZType.java}   | 70 ++++++++--------
 .../ezlang/types/Symbol.java                  | 16 ++--
 .../ezlang/types/TypeDictionary.java          | 60 +++++++-------
 .../ezlang/types/TestTypes.java               | 13 +--
 29 files changed, 294 insertions(+), 302 deletions(-)
 rename types/src/main/java/com/compilerprogramming/ezlang/types/{Type.java => EZType.java} (74%)

diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
index b1a2b45..d2c1d40 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
@@ -4,7 +4,7 @@
 import com.compilerprogramming.ezlang.parser.AST;
 import com.compilerprogramming.ezlang.types.Scope;
 import com.compilerprogramming.ezlang.types.Symbol;
-import com.compilerprogramming.ezlang.types.Type;
+import com.compilerprogramming.ezlang.types.EZType;
 import com.compilerprogramming.ezlang.types.TypeDictionary;
 
 import java.util.*;
@@ -17,7 +17,7 @@ public class CompiledFunction {
     public BasicBlock currentBlock;
     private BasicBlock currentBreakTarget;
     private BasicBlock currentContinueTarget;
-    public Type.TypeFunction functionType;
+    public EZType.EZTypeFunction functionType;
     public final RegisterPool registerPool;
     private final TypeDictionary typeDictionary;
 
@@ -39,7 +39,7 @@ public class CompiledFunction {
 
     public CompiledFunction(Symbol.FunctionTypeSymbol functionSymbol, TypeDictionary typeDictionary, EnumSet<Options> options) {
         AST.FuncDecl funcDecl = (AST.FuncDecl) functionSymbol.functionDecl;
-        this.functionType = (Type.TypeFunction) functionSymbol.type;
+        this.functionType = (EZType.EZTypeFunction) functionSymbol.type;
         this.registerPool = new RegisterPool();
         // Incremental SSA is an optional feature
         this.issa = (options != null && options.contains(Options.ISSA)) ? new IncrementalSSABraun(this) : new NoopIncrementalSSA();
@@ -60,8 +60,8 @@ public CompiledFunction(Symbol.FunctionTypeSymbol functionSymbol, TypeDictionary
     public CompiledFunction(Symbol.FunctionTypeSymbol functionSymbol, TypeDictionary typeDictionary) {
         this(functionSymbol,typeDictionary,null);
     }
-    public CompiledFunction(Type.TypeFunction functionType, TypeDictionary typeDictionary) {
-        this.functionType = (Type.TypeFunction) functionType;
+    public CompiledFunction(EZType.EZTypeFunction functionType, TypeDictionary typeDictionary) {
+        this.functionType = (EZType.EZTypeFunction) functionType;
         this.registerPool = new RegisterPool();
         this.issa = new NoopIncrementalSSA();        this.BID = 0;
         this.entry = this.currentBlock = createBlock();
@@ -325,7 +325,7 @@ private boolean compileExpr(AST.Expr expr) {
     private boolean compileCallExpr(AST.CallExpr callExpr) {
         compileExpr(callExpr.callee);
         var callee = pop();
-        Type.TypeFunction calleeType = null;
+        EZType.EZTypeFunction calleeType = null;
         if (callee instanceof Operand.LocalFunctionOperand functionOperand)
             calleeType = functionOperand.functionType;
         else throw new CompilerException("Cannot call a non function type");
@@ -347,20 +347,20 @@ private boolean compileCallExpr(AST.CallExpr callExpr) {
         for (int i = 0; i < args.size(); i++)
             pop();
         Operand.TempRegisterOperand ret = null;
-        if (callExpr.callee.type instanceof Type.TypeFunction tf &&
-                !(tf.returnType instanceof Type.TypeVoid)) {
+        if (callExpr.callee.type instanceof EZType.EZTypeFunction tf &&
+                !(tf.returnType instanceof EZType.EZTypeVoid)) {
             ret = createTemp(tf.returnType);
         }
         codeCall(returnStackPos, ret, calleeType, args.toArray(new Operand.RegisterOperand[args.size()]));
         return false;
     }
 
-    private Type.TypeStruct getStructType(Type t) {
-        if (t instanceof Type.TypeStruct typeStruct) {
+    private EZType.EZTypeStruct getStructType(EZType t) {
+        if (t instanceof EZType.EZTypeStruct typeStruct) {
             return typeStruct;
         }
-        else if (t instanceof Type.TypeNullable ptr &&
-                ptr.baseType instanceof Type.TypeStruct typeStruct) {
+        else if (t instanceof EZType.EZTypeNullable ptr &&
+                ptr.baseType instanceof EZType.EZTypeStruct typeStruct) {
             return typeStruct;
         }
         else
@@ -368,7 +368,7 @@ else if (t instanceof Type.TypeNullable ptr &&
     }
 
     private boolean compileFieldExpr(AST.GetFieldExpr fieldExpr) {
-        Type.TypeStruct typeStruct = getStructType(fieldExpr.object.type);
+        EZType.EZTypeStruct typeStruct = getStructType(fieldExpr.object.type);
         int fieldIndex = typeStruct.getFieldIndex(fieldExpr.fieldName);
         if (fieldIndex < 0)
             throw new CompilerException("Field " + fieldExpr.fieldName + " not found");
@@ -391,7 +391,7 @@ private boolean compileArrayIndexExpr(AST.ArrayLoadExpr arrayIndexExpr) {
     }
 
     private boolean compileSetFieldExpr(AST.SetFieldExpr setFieldExpr) {
-        Type.TypeStruct structType = (Type.TypeStruct) setFieldExpr.object.type;
+        EZType.EZTypeStruct structType = (EZType.EZTypeStruct) setFieldExpr.object.type;
         int fieldIndex = structType.getFieldIndex(setFieldExpr.fieldName);
         if (fieldIndex == -1)
             throw new CompilerException("Field " + setFieldExpr.fieldName + " not found in struct " + structType.name);
@@ -441,7 +441,7 @@ private boolean compileInitExpr(AST.InitExpr initExpr) {
     }
 
     private boolean compileSymbolExpr(AST.NameExpr symbolExpr) {
-        if (symbolExpr.type instanceof Type.TypeFunction functionType)
+        if (symbolExpr.type instanceof EZType.EZTypeFunction functionType)
             pushOperand(new Operand.LocalFunctionOperand(functionType));
         else {
             Symbol.VarSymbol varSymbol = (Symbol.VarSymbol) symbolExpr.symbol;
@@ -549,29 +549,29 @@ private boolean compileUnaryExpr(AST.UnaryExpr unaryExpr) {
     }
 
     private boolean compileConstantExpr(AST.LiteralExpr constantExpr) {
-        if (constantExpr.type instanceof Type.TypeInteger)
+        if (constantExpr.type instanceof EZType.EZTypeInteger)
             pushConstant(constantExpr.value.num.intValue(), constantExpr.type);
-        else if (constantExpr.type instanceof Type.TypeNull)
+        else if (constantExpr.type instanceof EZType.EZTypeNull)
             pushNullConstant(constantExpr.type);
         else throw new CompilerException("Invalid constant type");
         return false;
     }
 
-    private void pushConstant(long value, Type type) {
+    private void pushConstant(long value, EZType type) {
         pushOperand(new Operand.ConstantOperand(value, type));
     }
 
-    private void pushNullConstant(Type type) {
+    private void pushNullConstant(EZType type) {
         pushOperand(new Operand.NullConstantOperand(type));
     }
 
-    private Operand.TempRegisterOperand createTemp(Type type) {
+    private Operand.TempRegisterOperand createTemp(EZType type) {
         var tempRegister = new Operand.TempRegisterOperand(registerPool.newTempReg(type));
         pushOperand(tempRegister);
         return tempRegister;
     }
 
-    Type typeOfOperand(Operand operand) {
+    EZType typeOfOperand(Operand operand) {
         if (operand instanceof Operand.ConstantOperand constant)
             return constant.type;
         else if (operand instanceof Operand.NullConstantOperand nullConstantOperand)
@@ -582,7 +582,7 @@ else if (operand instanceof Operand.RegisterOperand registerOperand)
     }
 
     private Operand.TempRegisterOperand createTempAndMove(Operand src) {
-        Type type = typeOfOperand(src);
+        EZType type = typeOfOperand(src);
         var temp = createTemp(type);
         codeMove(src, temp);
         return temp;
@@ -644,16 +644,16 @@ else if (indexed instanceof Operand.LoadFieldOperand loadFieldOperand)
             codeMove(value, indexed);
     }
 
-    private void codeNew(Type type, AST.Expr len, AST.Expr initVal) {
-        if (type instanceof Type.TypeArray typeArray)
+    private void codeNew(EZType type, AST.Expr len, AST.Expr initVal) {
+        if (type instanceof EZType.EZTypeArray typeArray)
             codeNewArray(typeArray, len, initVal);
-        else if (type instanceof Type.TypeStruct typeStruct)
+        else if (type instanceof EZType.EZTypeStruct typeStruct)
             codeNewStruct(typeStruct);
         else
             throw new CompilerException("Unexpected type: " + type);
     }
 
-    private void codeNewArray(Type.TypeArray typeArray, AST.Expr len, AST.Expr initVal) {
+    private void codeNewArray(EZType.EZTypeArray typeArray, AST.Expr len, AST.Expr initVal) {
         var temp = createTemp(typeArray);
         Operand lenOperand = null;
         Operand initValOperand = null;
@@ -685,7 +685,7 @@ private void codeNewArray(Type.TypeArray typeArray, AST.Expr len, AST.Expr initV
         code(insn);
     }
 
-    private void codeNewStruct(Type.TypeStruct typeStruct) {
+    private void codeNewStruct(EZType.EZTypeStruct typeStruct) {
         var temp = createTemp(typeStruct);
         var target = (Operand.RegisterOperand) issa.write(temp);
         var insn = new Instruction.NewStruct(typeStruct, target);
@@ -729,7 +729,7 @@ private void codeCBR(BasicBlock block, Operand condition, BasicBlock trueBlock,
 
     private void codeCall(int newBase,
                           Operand.RegisterOperand targetOperand,
-                          Type.TypeFunction calleeType,
+                          EZType.EZTypeFunction calleeType,
                           Operand.RegisterOperand ...arguments) {
         if (targetOperand != null)
             targetOperand = (Operand.RegisterOperand) issa.write(targetOperand);
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java
index b15a157..7f33025 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java
@@ -5,7 +5,7 @@
 import com.compilerprogramming.ezlang.semantic.SemaAssignTypes;
 import com.compilerprogramming.ezlang.semantic.SemaDefineTypes;
 import com.compilerprogramming.ezlang.types.Symbol;
-import com.compilerprogramming.ezlang.types.Type;
+import com.compilerprogramming.ezlang.types.EZType;
 import com.compilerprogramming.ezlang.types.TypeDictionary;
 
 import java.util.EnumSet;
@@ -15,7 +15,7 @@ public class Compiler {
     private void compile(TypeDictionary typeDictionary, EnumSet<Options> options) {
         for (Symbol symbol: typeDictionary.getLocalSymbols()) {
             if (symbol instanceof Symbol.FunctionTypeSymbol functionSymbol) {
-                Type.TypeFunction functionType = (Type.TypeFunction) functionSymbol.type;
+                EZType.EZTypeFunction functionType = (EZType.EZTypeFunction) functionSymbol.type;
                 var function = new CompiledFunction(functionSymbol, typeDictionary, options);
                 if (options.contains(Options.DUMP_INITIAL_IR))
                     function.dumpIR(false, "Initial IR");
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java
index f7a3c6b..3ea0f44 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java
@@ -1,7 +1,7 @@
 package com.compilerprogramming.ezlang.compiler;
 
 import com.compilerprogramming.ezlang.exceptions.CompilerException;
-import com.compilerprogramming.ezlang.types.Type;
+import com.compilerprogramming.ezlang.types.EZType;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -139,16 +139,16 @@ public StringBuilder toStr(StringBuilder sb) {
     }
 
     public static class NewArray extends Instruction {
-        public final Type.TypeArray type;
-        public NewArray(Type.TypeArray type, Operand.RegisterOperand destOperand) {
+        public final EZType.EZTypeArray type;
+        public NewArray(EZType.EZTypeArray type, Operand.RegisterOperand destOperand) {
             super(I_NEW_ARRAY, destOperand);
             this.type = type;
         }
-        public NewArray(Type.TypeArray type, Operand.RegisterOperand destOperand, Operand len) {
+        public NewArray(EZType.EZTypeArray type, Operand.RegisterOperand destOperand, Operand len) {
             super(I_NEW_ARRAY, destOperand, len);
             this.type = type;
         }
-        public NewArray(Type.TypeArray type, Operand.RegisterOperand destOperand, Operand len, Operand initValue) {
+        public NewArray(EZType.EZTypeArray type, Operand.RegisterOperand destOperand, Operand len, Operand initValue) {
             super(I_NEW_ARRAY, destOperand, len, initValue);
             this.type = type;
         }
@@ -170,8 +170,8 @@ public StringBuilder toStr(StringBuilder sb) {
     }
 
     public static class NewStruct extends Instruction {
-        public final Type.TypeStruct type;
-        public NewStruct(Type.TypeStruct type, Operand.RegisterOperand destOperand) {
+        public final EZType.EZTypeStruct type;
+        public NewStruct(EZType.EZTypeStruct type, Operand.RegisterOperand destOperand) {
             super(I_NEW_STRUCT, destOperand);
             this.type = type;
         }
@@ -324,9 +324,9 @@ public StringBuilder toStr(StringBuilder sb) {
     }
 
     public static class Call extends Instruction {
-        public final Type.TypeFunction callee;
+        public final EZType.EZTypeFunction callee;
         public final int newbase;
-        public Call(int newbase, Operand.RegisterOperand returnOperand, Type.TypeFunction callee, Operand.RegisterOperand... args) {
+        public Call(int newbase, Operand.RegisterOperand returnOperand, EZType.EZTypeFunction callee, Operand.RegisterOperand... args) {
             super(I_CALL, returnOperand, args);
             this.callee = callee;
             this.newbase = newbase;
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Operand.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Operand.java
index 8ff84e4..f6af988 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Operand.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Operand.java
@@ -1,15 +1,15 @@
 package com.compilerprogramming.ezlang.compiler;
 
 import com.compilerprogramming.ezlang.types.Symbol;
-import com.compilerprogramming.ezlang.types.Type;
+import com.compilerprogramming.ezlang.types.EZType;
 
 public class Operand {
 
-    Type type;
+    EZType type;
 
     public static class ConstantOperand extends Operand {
         public final long value;
-        public ConstantOperand(long value, Type type) {
+        public ConstantOperand(long value, EZType type) {
             this.value = value;
             this.type = type;
         }
@@ -20,7 +20,7 @@ public String toString() {
     }
 
     public static class NullConstantOperand extends Operand {
-        public NullConstantOperand(Type type) {
+        public NullConstantOperand(EZType type) {
             this.type = type;
         }
         @Override
@@ -61,8 +61,8 @@ public RegisterOperand copy(Register register) {
     }
 
     public static class LocalFunctionOperand extends Operand {
-        public final Type.TypeFunction functionType;
-        public LocalFunctionOperand(Type.TypeFunction functionType) {
+        public final EZType.EZTypeFunction functionType;
+        public LocalFunctionOperand(EZType.EZTypeFunction functionType) {
             this.functionType = functionType;
         }
         @Override
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Register.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Register.java
index 9f87393..09190a1 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Register.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Register.java
@@ -1,6 +1,6 @@
 package com.compilerprogramming.ezlang.compiler;
 
-import com.compilerprogramming.ezlang.types.Type;
+import com.compilerprogramming.ezlang.types.EZType;
 
 /**
  * Represents a Virtual Register in the abstract machine.
@@ -44,7 +44,7 @@ public class Register {
     /**
      * The type of the register
      */
-    public final Type type;
+    public final EZType type;
     /**
      * The location of this register relative to the base
      * of the executing function. Multiple registers may share the same
@@ -52,10 +52,10 @@ public class Register {
      */
     protected int frameSlot;
 
-    public Register(int id, String name, Type type) {
+    public Register(int id, String name, EZType type) {
         this(id,name,type,id);  // Initially frame slot is set to the unique ID
     }
-    protected Register(int id, String name, Type type, int frameSlot) {
+    protected Register(int id, String name, EZType type, int frameSlot) {
         this.id = id;
         this.name = name;
         this.type = type;
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/RegisterPool.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/RegisterPool.java
index 88469af..9a0a77a 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/RegisterPool.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/RegisterPool.java
@@ -1,6 +1,6 @@
 package com.compilerprogramming.ezlang.compiler;
 
-import com.compilerprogramming.ezlang.types.Type;
+import com.compilerprogramming.ezlang.types.EZType;
 
 import java.util.ArrayList;
 
@@ -18,20 +18,20 @@ public class RegisterPool {
     public Register getReg(int regNumber) {
         return registers.get(regNumber);
     }
-    public Register newReg(String baseName, Type type) {
+    public Register newReg(String baseName, EZType type) {
         var id = registers.size();
         var reg = new Register(id, baseName, type);
         registers.add(reg);
         return reg;
     }
-    public Register newTempReg(Type type) {
+    public Register newTempReg(EZType type) {
         var id = registers.size();
         var name = "%t"+id;
         var reg = new Register(id, name, type);
         registers.add(reg);
         return reg;
     }
-    public Register newTempReg(String baseName, Type type) {
+    public Register newTempReg(String baseName, EZType type) {
         var id = registers.size();
         var name = baseName+"_"+id;
         var reg = new Register(id, name, type);
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SparseConditionalConstantPropagation.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SparseConditionalConstantPropagation.java
index 36edb74..ba96971 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SparseConditionalConstantPropagation.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SparseConditionalConstantPropagation.java
@@ -1,7 +1,7 @@
 package com.compilerprogramming.ezlang.compiler;
 
 import com.compilerprogramming.ezlang.exceptions.CompilerException;
-import com.compilerprogramming.ezlang.types.Type;
+import com.compilerprogramming.ezlang.types.EZType;
 
 import java.util.*;
 
@@ -361,7 +361,7 @@ private boolean evalInstruction(Instruction instruction) {
                 } else throw new IllegalStateException();
             }
             case Instruction.Call callInst -> {
-                if (!(callInst.callee.returnType instanceof Type.TypeVoid)) {
+                if (!(callInst.callee.returnType instanceof EZType.EZTypeVoid)) {
                     var cell = valueLattice.get(callInst.returnOperand().reg);
                     changed = cell.setKind(V_VARYING);
                 }
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/interpreter/Interpreter.java b/optvm/src/main/java/com/compilerprogramming/ezlang/interpreter/Interpreter.java
index 654d2df..31912dc 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/interpreter/Interpreter.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/interpreter/Interpreter.java
@@ -7,7 +7,7 @@
 import com.compilerprogramming.ezlang.exceptions.CompilerException;
 import com.compilerprogramming.ezlang.exceptions.InterpreterException;
 import com.compilerprogramming.ezlang.types.Symbol;
-import com.compilerprogramming.ezlang.types.Type;
+import com.compilerprogramming.ezlang.types.EZType;
 import com.compilerprogramming.ezlang.types.TypeDictionary;
 
 public class Interpreter {
@@ -122,7 +122,7 @@ else if (arg instanceof Operand.NullConstantOperand) {
                     Frame newFrame = new Frame(frame, baseReg, callInst.callee);
                     interpret(execStack, newFrame);
                     // Copy return value in expected location
-                    if (!(callInst.callee.returnType instanceof Type.TypeVoid)) {
+                    if (!(callInst.callee.returnType instanceof EZType.EZTypeVoid)) {
                         execStack.stack[base + callInst.returnOperand().frameSlot()] = execStack.stack[baseReg];
                     }
                 }
@@ -302,7 +302,7 @@ public Frame(Symbol.FunctionTypeSymbol functionSymbol) {
             this.bytecodeFunction = (CompiledFunction) functionSymbol.code();
         }
 
-        Frame(Frame caller, int base, Type.TypeFunction functionType) {
+        Frame(Frame caller, int base, EZType.EZTypeFunction functionType) {
             this.caller = caller;
             this.base = base;
             this.bytecodeFunction = (CompiledFunction) functionType.code;
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/interpreter/Value.java b/optvm/src/main/java/com/compilerprogramming/ezlang/interpreter/Value.java
index 6717daa..fc46c3b 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/interpreter/Value.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/interpreter/Value.java
@@ -1,6 +1,6 @@
 package com.compilerprogramming.ezlang.interpreter;
 
-import com.compilerprogramming.ezlang.types.Type;
+import com.compilerprogramming.ezlang.types.EZType;
 
 import java.util.ArrayList;
 
@@ -15,9 +15,9 @@ static public class NullValue extends Value {
         public NullValue() {}
     }
     static public class ArrayValue extends Value {
-        public final Type.TypeArray arrayType;
+        public final EZType.EZTypeArray arrayType;
         public final ArrayList<Value> values;
-        public ArrayValue(Type.TypeArray arrayType, long len, Value initValue) {
+        public ArrayValue(EZType.EZTypeArray arrayType, long len, Value initValue) {
             this.arrayType = arrayType;
             values = new ArrayList<>();
             for (long i = 0; i < len; i++) {
@@ -26,9 +26,9 @@ public ArrayValue(Type.TypeArray arrayType, long len, Value initValue) {
         }
     }
     static public class StructValue extends Value {
-        public final Type.TypeStruct structType;
+        public final EZType.EZTypeStruct structType;
         public final Value[] fields;
-        public StructValue(Type.TypeStruct structType) {
+        public StructValue(EZType.EZTypeStruct structType) {
             this.structType = structType;
             this.fields = new Value[structType.numFields()];
         }
diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestInterferenceGraph.java b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestInterferenceGraph.java
index 8018cba..e20846f 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestInterferenceGraph.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestInterferenceGraph.java
@@ -1,7 +1,7 @@
 package com.compilerprogramming.ezlang.compiler;
 
 import com.compilerprogramming.ezlang.types.Symbol;
-import com.compilerprogramming.ezlang.types.Type;
+import com.compilerprogramming.ezlang.types.EZType;
 import com.compilerprogramming.ezlang.types.TypeDictionary;
 import org.junit.Assert;
 import org.junit.Test;
@@ -10,7 +10,7 @@ public class TestInterferenceGraph {
 
     private CompiledFunction buildTest1() {
         TypeDictionary typeDictionary = new TypeDictionary();
-        Type.TypeFunction functionType = new Type.TypeFunction("foo");
+        EZType.EZTypeFunction functionType = new EZType.EZTypeFunction("foo");
         var argSymbol = new Symbol.ParameterSymbol("a", typeDictionary.INT);
         functionType.addArg(argSymbol);
         functionType.setReturnType(typeDictionary.INT);
@@ -81,7 +81,7 @@ public void test1() {
      */
     private CompiledFunction buildTest2() {
         TypeDictionary typeDictionary = new TypeDictionary();
-        Type.TypeFunction functionType = new Type.TypeFunction("foo");
+        EZType.EZTypeFunction functionType = new EZType.EZTypeFunction("foo");
         functionType.setReturnType(typeDictionary.VOID);
         CompiledFunction function = new CompiledFunction(functionType, typeDictionary);
         RegisterPool regPool = function.registerPool;
@@ -159,7 +159,7 @@ public void test3() {
     /* Test move does not interfere with uses */
     public static CompiledFunction buildTest4() {
         TypeDictionary typeDictionary = new TypeDictionary();
-        Type.TypeFunction functionType = new Type.TypeFunction("foo");
+        EZType.EZTypeFunction functionType = new EZType.EZTypeFunction("foo");
         functionType.setReturnType(typeDictionary.VOID);
         CompiledFunction function = new CompiledFunction(functionType, typeDictionary);
         RegisterPool regPool = function.registerPool;
diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestLiveness.java b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestLiveness.java
index 7a324b2..94c95e6 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestLiveness.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestLiveness.java
@@ -1,7 +1,7 @@
 package com.compilerprogramming.ezlang.compiler;
 
 import com.compilerprogramming.ezlang.types.Symbol;
-import com.compilerprogramming.ezlang.types.Type;
+import com.compilerprogramming.ezlang.types.EZType;
 import com.compilerprogramming.ezlang.types.TypeDictionary;
 import org.junit.Assert;
 import org.junit.Test;
@@ -245,7 +245,7 @@ func foo(a: Int,b: Int)
     /* page 448 Engineering a Compiler */
     static CompiledFunction buildTest3() {
         TypeDictionary typeDictionary = new TypeDictionary();
-        Type.TypeFunction functionType = new Type.TypeFunction("foo");
+        EZType.EZTypeFunction functionType = new EZType.EZTypeFunction("foo");
         functionType.setReturnType(typeDictionary.INT);
         CompiledFunction function = new CompiledFunction(functionType, typeDictionary);
         RegisterPool regPool = function.registerPool;
diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
index 8897512..5e926e3 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
@@ -1,10 +1,9 @@
 package com.compilerprogramming.ezlang.compiler;
 
 import com.compilerprogramming.ezlang.types.Symbol;
-import com.compilerprogramming.ezlang.types.Type;
+import com.compilerprogramming.ezlang.types.EZType;
 import com.compilerprogramming.ezlang.types.TypeDictionary;
 import org.junit.Assert;
-import org.junit.Ignore;
 import org.junit.Test;
 
 import java.util.Arrays;
@@ -628,7 +627,7 @@ func bar(arg: Int)->Int {
      */
     static CompiledFunction buildLostCopyTest() {
         TypeDictionary typeDictionary = new TypeDictionary();
-        Type.TypeFunction functionType = new Type.TypeFunction("foo");
+        EZType.EZTypeFunction functionType = new EZType.EZTypeFunction("foo");
         var argSymbol = new Symbol.ParameterSymbol("p", typeDictionary.INT);
         functionType.addArg(argSymbol);
         functionType.setReturnType(typeDictionary.INT);
@@ -699,7 +698,7 @@ public void testLostCopyProblem() {
      */
     static CompiledFunction buildSwapTest() {
         TypeDictionary typeDictionary = new TypeDictionary();
-        Type.TypeFunction functionType = new Type.TypeFunction("foo");
+        EZType.EZTypeFunction functionType = new EZType.EZTypeFunction("foo");
         var argSymbol = new Symbol.ParameterSymbol("p", typeDictionary.INT);
         functionType.addArg(argSymbol);
         functionType.setReturnType(typeDictionary.VOID);
diff --git a/parser/src/main/java/com/compilerprogramming/ezlang/parser/AST.java b/parser/src/main/java/com/compilerprogramming/ezlang/parser/AST.java
index 162dd29..8bd6020 100644
--- a/parser/src/main/java/com/compilerprogramming/ezlang/parser/AST.java
+++ b/parser/src/main/java/com/compilerprogramming/ezlang/parser/AST.java
@@ -3,7 +3,7 @@
 import com.compilerprogramming.ezlang.lexer.Token;
 import com.compilerprogramming.ezlang.types.Scope;
 import com.compilerprogramming.ezlang.types.Symbol;
-import com.compilerprogramming.ezlang.types.Type;
+import com.compilerprogramming.ezlang.types.EZType;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -162,7 +162,7 @@ public void accept(ASTVisitor visitor) {
     }
 
     public abstract static class Expr extends AST {
-        public Type type;
+        public EZType type;
     }
 
     public abstract static class TypeExpr extends Expr {
diff --git a/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java b/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
index 3071909..936ab2b 100644
--- a/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
+++ b/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
@@ -4,7 +4,7 @@
 import com.compilerprogramming.ezlang.parser.AST;
 import com.compilerprogramming.ezlang.types.Scope;
 import com.compilerprogramming.ezlang.types.Symbol;
-import com.compilerprogramming.ezlang.types.Type;
+import com.compilerprogramming.ezlang.types.EZType;
 import com.compilerprogramming.ezlang.types.TypeDictionary;
 
 import java.util.ArrayList;
@@ -281,7 +281,7 @@ private boolean compileExpr(AST.Expr expr) {
     private boolean compileCallExpr(AST.CallExpr callExpr) {
         compileExpr(callExpr.callee);
         var callee = pop();
-        Type.TypeFunction calleeType = null;
+        EZType.EZTypeFunction calleeType = null;
         if (callee instanceof Operand.LocalFunctionOperand functionOperand)
             calleeType = functionOperand.functionType;
         else throw new CompilerException("Cannot call a non function type");
@@ -303,8 +303,8 @@ private boolean compileCallExpr(AST.CallExpr callExpr) {
         for (int i = 0; i < args.size(); i++)
             pop();
         Operand.TempRegisterOperand ret = null;
-        if (callExpr.callee.type instanceof Type.TypeFunction tf &&
-                !(tf.returnType instanceof Type.TypeVoid)) {
+        if (callExpr.callee.type instanceof EZType.EZTypeFunction tf &&
+                !(tf.returnType instanceof EZType.EZTypeVoid)) {
             ret = createTemp(tf.returnType);
             assert ret.regnum-maxLocalReg == returnStackPos;
         }
@@ -312,12 +312,12 @@ private boolean compileCallExpr(AST.CallExpr callExpr) {
         return false;
     }
 
-    private Type.TypeStruct getStructType(Type t) {
-        if (t instanceof Type.TypeStruct typeStruct) {
+    private EZType.EZTypeStruct getStructType(EZType t) {
+        if (t instanceof EZType.EZTypeStruct typeStruct) {
             return typeStruct;
         }
-        else if (t instanceof Type.TypeNullable ptr &&
-                ptr.baseType instanceof Type.TypeStruct typeStruct) {
+        else if (t instanceof EZType.EZTypeNullable ptr &&
+                ptr.baseType instanceof EZType.EZTypeStruct typeStruct) {
             return typeStruct;
         }
         else
@@ -325,7 +325,7 @@ else if (t instanceof Type.TypeNullable ptr &&
     }
 
     private boolean compileFieldExpr(AST.GetFieldExpr fieldExpr) {
-        Type.TypeStruct typeStruct = getStructType(fieldExpr.object.type);
+        EZType.EZTypeStruct typeStruct = getStructType(fieldExpr.object.type);
         int fieldIndex = typeStruct.getFieldIndex(fieldExpr.fieldName);
         if (fieldIndex < 0)
             throw new CompilerException("Field " + fieldExpr.fieldName + " not found");
@@ -348,7 +348,7 @@ private boolean compileArrayIndexExpr(AST.ArrayLoadExpr arrayIndexExpr) {
     }
 
     private boolean compileSetFieldExpr(AST.SetFieldExpr setFieldExpr) {
-        Type.TypeStruct structType = (Type.TypeStruct) setFieldExpr.object.type;
+        EZType.EZTypeStruct structType = (EZType.EZTypeStruct) setFieldExpr.object.type;
         int fieldIndex = structType.getFieldIndex(setFieldExpr.fieldName);
         if (fieldIndex == -1)
             throw new CompilerException("Field " + setFieldExpr.fieldName + " not found in struct " + structType.name);
@@ -398,7 +398,7 @@ private boolean compileInitExpr(AST.InitExpr initExpr) {
     }
 
     private boolean compileSymbolExpr(AST.NameExpr symbolExpr) {
-        if (symbolExpr.type instanceof Type.TypeFunction functionType)
+        if (symbolExpr.type instanceof EZType.EZTypeFunction functionType)
             pushOperand(new Operand.LocalFunctionOperand(functionType));
         else {
             Symbol.VarSymbol varSymbol = (Symbol.VarSymbol) symbolExpr.symbol;
@@ -506,23 +506,23 @@ private boolean compileUnaryExpr(AST.UnaryExpr unaryExpr) {
     }
 
     private boolean compileConstantExpr(AST.LiteralExpr constantExpr) {
-        if (constantExpr.type instanceof Type.TypeInteger)
+        if (constantExpr.type instanceof EZType.EZTypeInteger)
             pushConstant(constantExpr.value.num.intValue(), constantExpr.type);
-        else if (constantExpr.type instanceof Type.TypeNull)
+        else if (constantExpr.type instanceof EZType.EZTypeNull)
             pushNullConstant(constantExpr.type);
         else throw new CompilerException("Invalid constant type");
         return false;
     }
 
-    private void pushConstant(long value, Type type) {
+    private void pushConstant(long value, EZType type) {
         pushOperand(new Operand.ConstantOperand(value, type));
     }
 
-    private void pushNullConstant(Type type) {
+    private void pushNullConstant(EZType type) {
         pushOperand(new Operand.NullConstantOperand(type));
     }
 
-    private Operand.TempRegisterOperand createTemp(Type type) {
+    private Operand.TempRegisterOperand createTemp(EZType type) {
         var tempRegister = new Operand.TempRegisterOperand(virtualStack.size()+maxLocalReg, type);
         pushOperand(tempRegister);
         if (maxStackSize < virtualStack.size())
@@ -530,7 +530,7 @@ private Operand.TempRegisterOperand createTemp(Type type) {
         return tempRegister;
     }
 
-    Type typeOfOperand(Operand operand) {
+    EZType typeOfOperand(Operand operand) {
         if (operand instanceof Operand.ConstantOperand constant)
             return constant.type;
         else if (operand instanceof Operand.NullConstantOperand nullConstantOperand)
@@ -541,7 +541,7 @@ else if (operand instanceof Operand.RegisterOperand registerOperand)
     }
 
     private Operand.TempRegisterOperand createTempAndMove(Operand src) {
-        Type type = typeOfOperand(src);
+        EZType type = typeOfOperand(src);
         var temp = createTemp(type);
         code(new Instruction.Move(src, temp));
         return temp;
@@ -603,10 +603,10 @@ else if (indexed instanceof Operand.LoadFieldOperand loadFieldOperand)
             code(new Instruction.Move(value, indexed));
     }
 
-    private void codeNew(Type type, AST.Expr len, AST.Expr initVal) {
-        if (type instanceof Type.TypeArray typeArray)
+    private void codeNew(EZType type, AST.Expr len, AST.Expr initVal) {
+        if (type instanceof EZType.EZTypeArray typeArray)
             codeNewArray(typeArray, len, initVal);
-        else if (type instanceof Type.TypeStruct typeStruct) {
+        else if (type instanceof EZType.EZTypeStruct typeStruct) {
             var temp = createTemp(type);
             code(new Instruction.NewStruct(typeStruct, temp));
         }
@@ -614,7 +614,7 @@ else if (type instanceof Type.TypeStruct typeStruct) {
             throw new CompilerException("Unexpected type: " + type);
     }
 
-    private void codeNewArray(Type.TypeArray typeArray, AST.Expr len, AST.Expr initVal) {
+    private void codeNewArray(EZType.EZTypeArray typeArray, AST.Expr len, AST.Expr initVal) {
         var temp = createTemp(typeArray);
         Operand lenOperand = null;
         Operand initValOperand = null;
diff --git a/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java b/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java
index 37eaea0..d76569b 100644
--- a/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java
+++ b/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java
@@ -5,7 +5,7 @@
 import com.compilerprogramming.ezlang.semantic.SemaAssignTypes;
 import com.compilerprogramming.ezlang.semantic.SemaDefineTypes;
 import com.compilerprogramming.ezlang.types.Symbol;
-import com.compilerprogramming.ezlang.types.Type;
+import com.compilerprogramming.ezlang.types.EZType;
 import com.compilerprogramming.ezlang.types.TypeDictionary;
 
 import java.util.BitSet;
@@ -15,7 +15,7 @@ public class Compiler {
     private void compile(TypeDictionary typeDictionary) {
         for (Symbol symbol: typeDictionary.getLocalSymbols()) {
             if (symbol instanceof Symbol.FunctionTypeSymbol functionSymbol) {
-                Type.TypeFunction functionType = (Type.TypeFunction) functionSymbol.type;
+                EZType.EZTypeFunction functionType = (EZType.EZTypeFunction) functionSymbol.type;
                 functionType.code = new CompiledFunction(functionSymbol, typeDictionary);
             }
         }
diff --git a/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java b/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java
index 73b5673..2386dbd 100644
--- a/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java
+++ b/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java
@@ -1,6 +1,6 @@
 package com.compilerprogramming.ezlang.compiler;
 
-import com.compilerprogramming.ezlang.types.Type;
+import com.compilerprogramming.ezlang.types.EZType;
 
 public abstract class Instruction {
 
@@ -57,16 +57,16 @@ public StringBuilder toStr(StringBuilder sb) {
     }
 
     public static class NewArray extends Instruction {
-        public final Type.TypeArray type;
-        public NewArray(Type.TypeArray type, Operand.RegisterOperand destOperand) {
+        public final EZType.EZTypeArray type;
+        public NewArray(EZType.EZTypeArray type, Operand.RegisterOperand destOperand) {
             super(I_NEW_ARRAY, destOperand);
             this.type = type;
         }
-        public NewArray(Type.TypeArray type, Operand.RegisterOperand destOperand, Operand len) {
+        public NewArray(EZType.EZTypeArray type, Operand.RegisterOperand destOperand, Operand len) {
             super(I_NEW_ARRAY, destOperand, len);
             this.type = type;
         }
-        public NewArray(Type.TypeArray type, Operand.RegisterOperand destOperand, Operand len, Operand initValue) {
+        public NewArray(EZType.EZTypeArray type, Operand.RegisterOperand destOperand, Operand len, Operand initValue) {
             super(I_NEW_ARRAY, destOperand, len, initValue);
             this.type = type;
         }
@@ -88,8 +88,8 @@ public StringBuilder toStr(StringBuilder sb) {
     }
 
     public static class NewStruct extends Instruction {
-        public final Type.TypeStruct type;
-        public NewStruct(Type.TypeStruct type, Operand.RegisterOperand destOperand) {
+        public final EZType.EZTypeStruct type;
+        public NewStruct(EZType.EZTypeStruct type, Operand.RegisterOperand destOperand) {
             super(I_NEW_STRUCT, destOperand);
             this.type = type;
         }
@@ -244,9 +244,9 @@ public StringBuilder toStr(StringBuilder sb) {
     }
 
     public static class Call extends Instruction {
-        public final Type.TypeFunction callee;
+        public final EZType.EZTypeFunction callee;
         public final int newbase;
-        public Call(int newbase, Operand.RegisterOperand returnOperand, Type.TypeFunction callee, Operand.RegisterOperand... args) {
+        public Call(int newbase, Operand.RegisterOperand returnOperand, EZType.EZTypeFunction callee, Operand.RegisterOperand... args) {
             super(I_CALL, returnOperand, args);
             this.callee = callee;
             this.newbase = newbase;
diff --git a/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/Operand.java b/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/Operand.java
index 54188da..d2315f3 100644
--- a/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/Operand.java
+++ b/registervm/src/main/java/com/compilerprogramming/ezlang/compiler/Operand.java
@@ -1,14 +1,14 @@
 package com.compilerprogramming.ezlang.compiler;
 
-import com.compilerprogramming.ezlang.types.Type;
+import com.compilerprogramming.ezlang.types.EZType;
 
 public class Operand {
 
-    Type type;
+    EZType type;
 
     public static class ConstantOperand extends Operand {
         public final long value;
-        public ConstantOperand(long value, Type type) {
+        public ConstantOperand(long value, EZType type) {
             this.value = value;
             this.type = type;
         }
@@ -19,7 +19,7 @@ public String toString() {
     }
 
     public static class NullConstantOperand extends Operand {
-        public NullConstantOperand(Type type) {
+        public NullConstantOperand(EZType type) {
             this.type = type;
         }
         @Override
@@ -49,8 +49,8 @@ public String toString() {
     }
 
     public static class LocalFunctionOperand extends Operand {
-        public final Type.TypeFunction functionType;
-        public LocalFunctionOperand(Type.TypeFunction functionType) {
+        public final EZType.EZTypeFunction functionType;
+        public LocalFunctionOperand(EZType.EZTypeFunction functionType) {
             this.functionType = functionType;
         }
         @Override
@@ -65,7 +65,7 @@ public String toString() {
      * register number from start of temp area.
      */
     public static class TempRegisterOperand extends RegisterOperand {
-        public TempRegisterOperand(int regnum, Type type) {
+        public TempRegisterOperand(int regnum, EZType type) {
             super(regnum);
             this.type = type;
         }
diff --git a/registervm/src/main/java/com/compilerprogramming/ezlang/interpreter/Interpreter.java b/registervm/src/main/java/com/compilerprogramming/ezlang/interpreter/Interpreter.java
index f441455..62f7411 100644
--- a/registervm/src/main/java/com/compilerprogramming/ezlang/interpreter/Interpreter.java
+++ b/registervm/src/main/java/com/compilerprogramming/ezlang/interpreter/Interpreter.java
@@ -7,7 +7,7 @@
 import com.compilerprogramming.ezlang.exceptions.CompilerException;
 import com.compilerprogramming.ezlang.exceptions.InterpreterException;
 import com.compilerprogramming.ezlang.types.Symbol;
-import com.compilerprogramming.ezlang.types.Type;
+import com.compilerprogramming.ezlang.types.EZType;
 import com.compilerprogramming.ezlang.types.TypeDictionary;
 
 public class Interpreter {
@@ -122,7 +122,7 @@ else if (arg instanceof Operand.NullConstantOperand) {
                     Frame newFrame = new Frame(frame, baseReg, callInst.callee);
                     interpret(execStack, newFrame);
                     // Copy return value in expected location
-                    if (!(callInst.callee.returnType instanceof Type.TypeVoid)) {
+                    if (!(callInst.callee.returnType instanceof EZType.EZTypeVoid)) {
                         execStack.stack[base + callInst.returnOperand().frameSlot()] = execStack.stack[baseReg];
                     }
                 }
@@ -301,7 +301,7 @@ public Frame(Symbol.FunctionTypeSymbol functionSymbol) {
             this.bytecodeFunction = (CompiledFunction) functionSymbol.code();
         }
 
-        Frame(Frame caller, int base, Type.TypeFunction functionType) {
+        Frame(Frame caller, int base, EZType.EZTypeFunction functionType) {
             this.caller = caller;
             this.base = base;
             this.bytecodeFunction = (CompiledFunction) functionType.code;
diff --git a/registervm/src/main/java/com/compilerprogramming/ezlang/interpreter/Value.java b/registervm/src/main/java/com/compilerprogramming/ezlang/interpreter/Value.java
index 6717daa..fc46c3b 100644
--- a/registervm/src/main/java/com/compilerprogramming/ezlang/interpreter/Value.java
+++ b/registervm/src/main/java/com/compilerprogramming/ezlang/interpreter/Value.java
@@ -1,6 +1,6 @@
 package com.compilerprogramming.ezlang.interpreter;
 
-import com.compilerprogramming.ezlang.types.Type;
+import com.compilerprogramming.ezlang.types.EZType;
 
 import java.util.ArrayList;
 
@@ -15,9 +15,9 @@ static public class NullValue extends Value {
         public NullValue() {}
     }
     static public class ArrayValue extends Value {
-        public final Type.TypeArray arrayType;
+        public final EZType.EZTypeArray arrayType;
         public final ArrayList<Value> values;
-        public ArrayValue(Type.TypeArray arrayType, long len, Value initValue) {
+        public ArrayValue(EZType.EZTypeArray arrayType, long len, Value initValue) {
             this.arrayType = arrayType;
             values = new ArrayList<>();
             for (long i = 0; i < len; i++) {
@@ -26,9 +26,9 @@ public ArrayValue(Type.TypeArray arrayType, long len, Value initValue) {
         }
     }
     static public class StructValue extends Value {
-        public final Type.TypeStruct structType;
+        public final EZType.EZTypeStruct structType;
         public final Value[] fields;
-        public StructValue(Type.TypeStruct structType) {
+        public StructValue(EZType.EZTypeStruct structType) {
             this.structType = structType;
             this.fields = new Value[structType.numFields()];
         }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java
index 3d7e546..813b708 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java
@@ -12,7 +12,7 @@
 import com.compilerprogramming.ezlang.semantic.SemaDefineTypes;
 import com.compilerprogramming.ezlang.types.Scope;
 import com.compilerprogramming.ezlang.types.Symbol;
-import com.compilerprogramming.ezlang.types.Type;
+import com.compilerprogramming.ezlang.types.EZType;
 import com.compilerprogramming.ezlang.types.TypeDictionary;
 
 import java.util.ArrayList;
@@ -105,10 +105,10 @@ private void populateTypes(Map<String, SONType> structTypes) {
         // First process struct types
         for (var symbol: typeDictionary.getLocalSymbols()) {
             if (symbol instanceof Symbol.TypeSymbol typeSymbol) {
-                if (typeSymbol.type instanceof Type.TypeStruct typeStruct) {
+                if (typeSymbol.type instanceof EZType.EZTypeStruct typeStruct) {
                     createSONStructType(structTypes,typeSymbol.name, typeStruct);
                 }
-                else if (typeSymbol.type instanceof Type.TypeArray typeArray) {
+                else if (typeSymbol.type instanceof EZType.EZTypeArray typeArray) {
                     getSONType(structTypes, typeArray);
                 }
             }
@@ -130,7 +130,7 @@ private void processFunctions() {
     }
 
     private void createFunctionType(Map<String, SONType> structTypes, Symbol.FunctionTypeSymbol functionSymbol) {
-        Type.TypeFunction functionType = (Type.TypeFunction) functionSymbol.type;
+        EZType.EZTypeFunction functionType = (EZType.EZTypeFunction) functionSymbol.type;
         Ary<SONType> params = new Ary<>(SONType.class);
         for (var symbol: functionType.args) {
             if (symbol instanceof Symbol.ParameterSymbol parameterSymbol) {
@@ -143,11 +143,11 @@ private void createFunctionType(Map<String, SONType> structTypes, Symbol.Functio
         structTypes.put(functionSymbol.name, tfp);
     }
 
-    private void createSONStructType(Map<String, SONType> structTypes, String typeName, Type.TypeStruct typeStruct) {
+    private void createSONStructType(Map<String, SONType> structTypes, String typeName, EZType.EZTypeStruct typeStruct) {
         Ary<Field> fs = new Ary<>(Field.class);
         for (int i = 0; i < typeStruct.numFields(); i++) {
             String name = typeStruct.getFieldName(i);
-            Type type = typeStruct.getField(name); // FIXME
+            EZType type = typeStruct.getField(name); // FIXME
             fs.push(new Field(name,getSONType(structTypes,type),_code.getALIAS(),false));
         }
         // A Struct type may have been created before because of
@@ -169,33 +169,33 @@ private void createSONStructType(Map<String, SONType> structTypes, String typeNa
         }
     }
 
-    private String getSONTypeName(Type type) {
-        if (type instanceof Type.TypeFunction typeFunction) {
+    private String getSONTypeName(EZType type) {
+        if (type instanceof EZType.EZTypeFunction typeFunction) {
             return typeFunction.name;
         }
-        else if (type instanceof Type.TypeArray typeArray) {
+        else if (type instanceof EZType.EZTypeArray typeArray) {
             return "*[" + getSONTypeName(typeArray.getElementType()) + "]";
         }
-        else if (type instanceof Type.TypeStruct typeStruct) {
+        else if (type instanceof EZType.EZTypeStruct typeStruct) {
             return "*" + typeStruct.name;
         }
-        else if (type instanceof Type.TypeInteger ||
-                 type instanceof Type.TypeVoid) {
+        else if (type instanceof EZType.EZTypeInteger ||
+                 type instanceof EZType.EZTypeVoid) {
             return SONTypeInteger.BOT.str();
         }
-        else if (type instanceof Type.TypeNull) {
+        else if (type instanceof EZType.EZTypeNull) {
             return SONTypeNil.NIL.str();
         }
-        else if (type instanceof Type.TypeNullable typeNullable) {
+        else if (type instanceof EZType.EZTypeNullable typeNullable) {
             return getSONTypeName(typeNullable.baseType)+"?";
         }
         else throw new CompilerException("Not yet implemented " + type.name());
     }
 
-    private SONType getSONType(Map<String, SONType> structTypes, Type type) {
+    private SONType getSONType(Map<String, SONType> structTypes, EZType type) {
         SONType t = structTypes.get(type.name());
         if (t != null) return t;
-        if (type instanceof Type.TypeStruct) {
+        if (type instanceof EZType.EZTypeStruct) {
             // For struct types in EeZee language a reference
             // to T means *T in SoN
             // Create SON struct type
@@ -206,7 +206,7 @@ private SONType getSONType(Map<String, SONType> structTypes, Type type) {
             structTypes.put(type.name(), ptr);
             return ptr;
         }
-        else if (type instanceof Type.TypeArray typeArray) {
+        else if (type instanceof EZType.EZTypeArray typeArray) {
             // A reference to array in EeZee means
             // *array in SoN
             SONType elementType = getSONType(structTypes,typeArray.getElementType());
@@ -215,7 +215,7 @@ else if (type instanceof Type.TypeArray typeArray) {
             structTypes.put(typeArray.name(), ptr); // Array type name is not same as ptr str()
             return ptr;
         }
-        else if (type instanceof Type.TypeNullable typeNullable) {
+        else if (type instanceof EZType.EZTypeNullable typeNullable) {
             SONType baseType = getSONType(structTypes,typeNullable.baseType);
             SONTypeMemPtr ptr = null;
             if (baseType instanceof SONTypeMemPtr ptr1) {
@@ -229,7 +229,7 @@ else if (type instanceof Type.TypeNullable typeNullable) {
             structTypes.put(typeNullable.name(), ptr);
             return ptr;
         }
-        else if (type instanceof Type.TypeVoid) {
+        else if (type instanceof EZType.EZTypeVoid) {
             return structTypes.get("Int"); // Only allowed in return types
         }
         throw new CompilerException("Count not find type " + type.name());
@@ -551,12 +551,12 @@ private Node newStruct( SONTypeStruct obj, Node size) {
     }
 
     private Node compileNewExpr(AST.NewExpr newExpr) {
-        Type type = newExpr.type;
-        if (type instanceof Type.TypeArray typeArray) {
+        EZType type = newExpr.type;
+        if (type instanceof EZType.EZTypeArray typeArray) {
             SONTypeMemPtr tarray = (SONTypeMemPtr) TYPES.get(typeArray.name());
             return newArray(tarray._obj,newExpr.len==null?ZERO:compileExpr(newExpr.len));
         }
-        else if (type instanceof Type.TypeStruct typeStruct) {
+        else if (type instanceof EZType.EZTypeStruct typeStruct) {
             SONTypeMemPtr tptr = (SONTypeMemPtr) TYPES.get(typeStruct.name());
             return newStruct(tptr._obj,con(tptr._obj.offset(tptr._obj._fields.length)));
         }
@@ -625,15 +625,15 @@ private Node compileBinaryExpr(AST.BinaryExpr binaryExpr) {
     }
 
     private Node compileConstantExpr(AST.LiteralExpr constantExpr) {
-        if (constantExpr.type instanceof Type.TypeInteger)
+        if (constantExpr.type instanceof EZType.EZTypeInteger)
             return con(constantExpr.value.num.intValue());
-        else if (constantExpr.type instanceof Type.TypeNull)
+        else if (constantExpr.type instanceof EZType.EZTypeNull)
             return NIL;
         else throw new CompilerException("Invalid constant type");
     }
 
     private Node compileSymbolExpr(AST.NameExpr symbolExpr) {
-        if (symbolExpr.type instanceof Type.TypeFunction functionType)
+        if (symbolExpr.type instanceof EZType.EZTypeFunction functionType)
             return con(TYPES.get(functionType.name));
         else {
             Symbol.VarSymbol varSymbol = (Symbol.VarSymbol) symbolExpr.symbol;
diff --git a/semantic/src/main/java/com/compilerprogramming/ezlang/semantic/SemaAssignTypes.java b/semantic/src/main/java/com/compilerprogramming/ezlang/semantic/SemaAssignTypes.java
index 743ac68..77d3066 100644
--- a/semantic/src/main/java/com/compilerprogramming/ezlang/semantic/SemaAssignTypes.java
+++ b/semantic/src/main/java/com/compilerprogramming/ezlang/semantic/SemaAssignTypes.java
@@ -5,7 +5,7 @@
 import com.compilerprogramming.ezlang.parser.AST;
 import com.compilerprogramming.ezlang.parser.ASTVisitor;
 import com.compilerprogramming.ezlang.types.Scope;
-import com.compilerprogramming.ezlang.types.Type;
+import com.compilerprogramming.ezlang.types.EZType;
 import com.compilerprogramming.ezlang.types.TypeDictionary;
 
 /**
@@ -71,17 +71,17 @@ public ASTVisitor visit(AST.BinaryExpr binaryExpr, boolean enter) {
                 return this;
             validType(binaryExpr.expr1.type, true);
             validType(binaryExpr.expr2.type, true);
-            if (binaryExpr.expr1.type instanceof Type.TypeInteger &&
-                binaryExpr.expr2.type instanceof Type.TypeInteger) {
+            if (binaryExpr.expr1.type instanceof EZType.EZTypeInteger &&
+                binaryExpr.expr2.type instanceof EZType.EZTypeInteger) {
                 // booleans are int too
                 binaryExpr.type = typeDictionary.INT;
             }
-            else if (((binaryExpr.expr1.type instanceof Type.TypeNull &&
-                     binaryExpr.expr2.type instanceof Type.TypeNullable) ||
-                    (binaryExpr.expr1.type instanceof Type.TypeNullable &&
-                     binaryExpr.expr2.type instanceof Type.TypeNull) ||
-                    (binaryExpr.expr1.type instanceof Type.TypeNull &&
-                     binaryExpr.expr2.type instanceof Type.TypeNull)) &&
+            else if (((binaryExpr.expr1.type instanceof EZType.EZTypeNull &&
+                     binaryExpr.expr2.type instanceof EZType.EZTypeNullable) ||
+                    (binaryExpr.expr1.type instanceof EZType.EZTypeNullable &&
+                     binaryExpr.expr2.type instanceof EZType.EZTypeNull) ||
+                    (binaryExpr.expr1.type instanceof EZType.EZTypeNull &&
+                     binaryExpr.expr2.type instanceof EZType.EZTypeNull)) &&
                     (binaryExpr.op.str.equals("==") || binaryExpr.op.str.equals("!="))) {
                 binaryExpr.type = typeDictionary.INT;
             }
@@ -98,7 +98,7 @@ public ASTVisitor visit(AST.UnaryExpr unaryExpr, boolean enter) {
             return this;
         }
         validType(unaryExpr.expr.type, false);
-        if (unaryExpr.expr.type instanceof Type.TypeInteger ti) {
+        if (unaryExpr.expr.type instanceof EZType.EZTypeInteger ti) {
             unaryExpr.type = unaryExpr.expr.type;
         }
         else {
@@ -114,12 +114,12 @@ public ASTVisitor visit(AST.GetFieldExpr fieldExpr, boolean enter) {
         if (fieldExpr.type != null)
             return this;
         validType(fieldExpr.object.type, false);
-        Type.TypeStruct structType = null;
-        if (fieldExpr.object.type instanceof Type.TypeStruct ts) {
+        EZType.EZTypeStruct structType = null;
+        if (fieldExpr.object.type instanceof EZType.EZTypeStruct ts) {
             structType = ts;
         }
-        else if (fieldExpr.object.type instanceof Type.TypeNullable ptr &&
-                ptr.baseType instanceof Type.TypeStruct ts) {
+        else if (fieldExpr.object.type instanceof EZType.EZTypeNullable ptr &&
+                ptr.baseType instanceof EZType.EZTypeStruct ts) {
             structType = ts;
         }
         else
@@ -138,15 +138,15 @@ public ASTVisitor visit(AST.SetFieldExpr fieldExpr, boolean enter) {
         if (fieldExpr.type != null)
             return this;
         validType(fieldExpr.object.type, true);
-        Type.TypeStruct structType = null;
-        if (fieldExpr.object.type instanceof Type.TypeStruct ts) {
+        EZType.EZTypeStruct structType = null;
+        if (fieldExpr.object.type instanceof EZType.EZTypeStruct ts) {
             structType = ts;
         }
-        else if (fieldExpr.object.type instanceof Type.TypeNullable ptr &&
-                ptr.baseType instanceof Type.TypeStruct ts) {
+        else if (fieldExpr.object.type instanceof EZType.EZTypeNullable ptr &&
+                ptr.baseType instanceof EZType.EZTypeStruct ts) {
             structType = ts;
         }
-        else if (fieldExpr.object.type instanceof Type.TypeArray typeArray) {
+        else if (fieldExpr.object.type instanceof EZType.EZTypeArray typeArray) {
             if (fieldExpr.fieldName.equals("len"))
                 checkAssignmentCompatible(typeDictionary.INT,fieldExpr.value.type);
             else if (fieldExpr.fieldName.equals("value"))
@@ -174,7 +174,7 @@ public ASTVisitor visit(AST.CallExpr callExpr, boolean enter) {
             if (callExpr.type != null)
                 return this;
             validType(callExpr.callee.type, false);
-            if (callExpr.callee.type instanceof Type.TypeFunction f) {
+            if (callExpr.callee.type instanceof EZType.EZTypeFunction f) {
                 callExpr.type = f.returnType;
             }
             else
@@ -232,17 +232,17 @@ public ASTVisitor visit(AST.ArrayLoadExpr arrayIndexExpr, boolean enter) {
             if (arrayIndexExpr.type != null)
                 return this;
             validType(arrayIndexExpr.array.type, false);
-            Type.TypeArray arrayType = null;
-            if (arrayIndexExpr.array.type instanceof Type.TypeArray ta) {
+            EZType.EZTypeArray arrayType = null;
+            if (arrayIndexExpr.array.type instanceof EZType.EZTypeArray ta) {
                 arrayType = ta;
             }
-            else if (arrayIndexExpr.array.type instanceof Type.TypeNullable ptr &&
-                    ptr.baseType instanceof Type.TypeArray ta) {
+            else if (arrayIndexExpr.array.type instanceof EZType.EZTypeNullable ptr &&
+                    ptr.baseType instanceof EZType.EZTypeArray ta) {
                 arrayType = ta;
             }
             else
                 throw new CompilerException("Unexpected array type " + arrayIndexExpr.array.type);
-            if (!(arrayIndexExpr.expr.type instanceof Type.TypeInteger))
+            if (!(arrayIndexExpr.expr.type instanceof EZType.EZTypeInteger))
                 throw new CompilerException("Array index must be integer type");
             arrayIndexExpr.type = arrayType.getElementType();
             validType(arrayIndexExpr.type, false);
@@ -256,17 +256,17 @@ public ASTVisitor visit(AST.ArrayStoreExpr arrayIndexExpr, boolean enter) {
             if (arrayIndexExpr.type != null)
                 return this;
             validType(arrayIndexExpr.array.type, false);
-            Type.TypeArray arrayType = null;
-            if (arrayIndexExpr.array.type instanceof Type.TypeArray ta) {
+            EZType.EZTypeArray arrayType = null;
+            if (arrayIndexExpr.array.type instanceof EZType.EZTypeArray ta) {
                 arrayType = ta;
             }
-            else if (arrayIndexExpr.array.type instanceof Type.TypeNullable ptr &&
-                    ptr.baseType instanceof Type.TypeArray ta) {
+            else if (arrayIndexExpr.array.type instanceof EZType.EZTypeNullable ptr &&
+                    ptr.baseType instanceof EZType.EZTypeArray ta) {
                 arrayType = ta;
             }
             else
                 throw new CompilerException("Unexpected array type " + arrayIndexExpr.array.type);
-            if (!(arrayIndexExpr.expr.type instanceof Type.TypeInteger))
+            if (!(arrayIndexExpr.expr.type instanceof EZType.EZTypeInteger))
                 throw new CompilerException("Array index must be integer type");
             arrayIndexExpr.type = arrayType.getElementType();
             validType(arrayIndexExpr.type, false);
@@ -285,15 +285,15 @@ public ASTVisitor visit(AST.NewExpr newExpr, boolean enter) {
         if (newExpr.typeExpr.type == null)
             throw new CompilerException("Unresolved type in new expression");
         validType(newExpr.typeExpr.type, false);
-        if (newExpr.typeExpr.type instanceof Type.TypeNullable)
+        if (newExpr.typeExpr.type instanceof EZType.EZTypeNullable)
             throw new CompilerException("new cannot be used to create a Nullable type");
-        if (newExpr.typeExpr.type instanceof Type.TypeStruct typeStruct) {
+        if (newExpr.typeExpr.type instanceof EZType.EZTypeStruct typeStruct) {
             newExpr.type = newExpr.typeExpr.type;
         }
-        else if (newExpr.typeExpr.type instanceof Type.TypeArray arrayType) {
+        else if (newExpr.typeExpr.type instanceof EZType.EZTypeArray arrayType) {
             newExpr.type = newExpr.typeExpr.type;
             if (newExpr.len != null) {
-                if (!(newExpr.len.type instanceof Type.TypeInteger))
+                if (!(newExpr.len.type instanceof EZType.EZTypeInteger))
                     throw new CompilerException("Array len must be integer type");
                 if (newExpr.initValue != null) {
                     if (!arrayType.getElementType().isAssignable(newExpr.initValue.type))
@@ -315,9 +315,9 @@ public ASTVisitor visit(AST.InitExpr initExpr, boolean enter) {
         if (initExpr.type != null)
             return this;
         validType(initExpr.newExpr.type, false);
-        if (initExpr.newExpr.type instanceof Type.TypeNullable)
+        if (initExpr.newExpr.type instanceof EZType.EZTypeNullable)
             throw new CompilerException("new cannot be used to create a Nullable type");
-        if (initExpr.newExpr.type instanceof Type.TypeStruct typeStruct) {
+        if (initExpr.newExpr.type instanceof EZType.EZTypeStruct typeStruct) {
             for (AST.Expr expr: initExpr.initExprList) {
                 if (expr instanceof AST.SetFieldExpr setFieldExpr) {
                     var fieldType = typeStruct.getField(setFieldExpr.fieldName);
@@ -325,7 +325,7 @@ public ASTVisitor visit(AST.InitExpr initExpr, boolean enter) {
                 }
             }
         }
-        else if (initExpr.newExpr.type instanceof Type.TypeArray arrayType) {
+        else if (initExpr.newExpr.type instanceof EZType.EZTypeArray arrayType) {
             if (initExpr.initExprList.size() > 0)
                 initExpr.initExprList.removeIf(e->e instanceof AST.InitFieldExpr);
             for (AST.Expr expr: initExpr.initExprList) {
@@ -368,12 +368,12 @@ public ASTVisitor visit(AST.ContinueStmt continueStmt, boolean enter) {
     public ASTVisitor visit(AST.ReturnStmt returnStmt, boolean enter) {
         if (enter)
             return this;
-        Type.TypeFunction functionType = (Type.TypeFunction) currentFuncDecl.symbol.type;
+        EZType.EZTypeFunction functionType = (EZType.EZTypeFunction) currentFuncDecl.symbol.type;
         if (returnStmt.expr != null) {
             validType(returnStmt.expr.type, true);
             checkAssignmentCompatible(functionType.returnType, returnStmt.expr.type);
         }
-        else if (!(functionType.returnType instanceof Type.TypeVoid)) {
+        else if (!(functionType.returnType instanceof EZType.EZTypeVoid)) {
             throw new CompilerException("A return value of type " + functionType.returnType + " is expected");
         }
         return this;
@@ -436,7 +436,7 @@ public void analyze(AST.Program program) {
         program.accept(this);
     }
 
-    private void validType(Type t, boolean allowNull) {
+    private void validType(EZType t, boolean allowNull) {
         if (t == null)
             throw new CompilerException("Undefined type");
         if (t == typeDictionary.UNKNOWN)
@@ -445,7 +445,7 @@ private void validType(Type t, boolean allowNull) {
             throw new CompilerException("Null type not allowed");
     }
 
-    private void checkAssignmentCompatible(Type var, Type value) {
+    private void checkAssignmentCompatible(EZType var, EZType value) {
         if (!var.isAssignable(value))
             throw new CompilerException("Value of type " + value + " cannot be assigned to type " + var);
     }
diff --git a/semantic/src/main/java/com/compilerprogramming/ezlang/semantic/SemaDefineTypes.java b/semantic/src/main/java/com/compilerprogramming/ezlang/semantic/SemaDefineTypes.java
index e5d3498..3610660 100644
--- a/semantic/src/main/java/com/compilerprogramming/ezlang/semantic/SemaDefineTypes.java
+++ b/semantic/src/main/java/com/compilerprogramming/ezlang/semantic/SemaDefineTypes.java
@@ -5,7 +5,7 @@
 import com.compilerprogramming.ezlang.parser.ASTVisitor;
 import com.compilerprogramming.ezlang.types.Scope;
 import com.compilerprogramming.ezlang.types.Symbol;
-import com.compilerprogramming.ezlang.types.Type;
+import com.compilerprogramming.ezlang.types.EZType;
 import com.compilerprogramming.ezlang.types.TypeDictionary;
 
 /**
@@ -46,7 +46,7 @@ public ASTVisitor visit(AST.FuncDecl funcDecl, boolean enter) {
             // Install a symbol for the function,
             // type is not fully formed at this stage
             // as parameters nad return values must be added
-            Symbol funcSymbol = new Symbol.FunctionTypeSymbol(funcDecl.name, new Type.TypeFunction(funcDecl.name), funcDecl);
+            Symbol funcSymbol = new Symbol.FunctionTypeSymbol(funcDecl.name, new EZType.EZTypeFunction(funcDecl.name), funcDecl);
             typeDictionary.install(funcDecl.name, funcSymbol);
             // Set up the function decl so that when we visit the parameters
             // and return type we know where to add
@@ -65,7 +65,7 @@ public ASTVisitor visit(AST.StructDecl structDecl, boolean enter) {
         if (enter) {
             Symbol structSymbol = typeDictionary.lookup(structDecl.name);
             if (structSymbol != null) {
-                if (structSymbol.type instanceof Type.TypeStruct lookupStructType) {
+                if (structSymbol.type instanceof EZType.EZTypeStruct lookupStructType) {
                     if (!lookupStructType.pending)
                         throw new CompilerException("Struct type " + structDecl.name + " is already declared");
                 }
@@ -73,7 +73,7 @@ public ASTVisitor visit(AST.StructDecl structDecl, boolean enter) {
                     throw new CompilerException("Symbol " + structDecl.name + " is already declared");
             }
             else {
-                Type.TypeStruct structType = new Type.TypeStruct(structDecl.name);
+                EZType.EZTypeStruct structType = new EZType.EZTypeStruct(structDecl.name);
                 structSymbol = new Symbol.TypeSymbol(structDecl.name, structType);
                 typeDictionary.install(structDecl.name, structSymbol);
             }
@@ -107,7 +107,7 @@ else if (varDecl.varType == AST.VarType.FUNCTION_PARAMETER && currentFuncDecl !=
         else {
             if (varDecl.varType == AST.VarType.STRUCT_FIELD
                     && currentStructDecl != null) {
-                Type.TypeStruct type = (Type.TypeStruct) currentStructDecl.symbol.type;
+                EZType.EZTypeStruct type = (EZType.EZTypeStruct) currentStructDecl.symbol.type;
                 type.addField(varDecl.name, varDecl.typeExpr.type);
             }
             else if (varDecl.varType == AST.VarType.FUNCTION_PARAMETER
@@ -115,7 +115,7 @@ else if (varDecl.varType == AST.VarType.FUNCTION_PARAMETER
                     && currentScope == currentFuncDecl.scope) {
                 if (currentScope.localLookup(varDecl.name) != null)
                     throw new CompilerException("Parameter " + varDecl.name + " is already declared");
-                Type.TypeFunction type = (Type.TypeFunction) currentFuncDecl.symbol.type;
+                EZType.EZTypeFunction type = (EZType.EZTypeFunction) currentFuncDecl.symbol.type;
                 varDecl.symbol = currentScope.install(varDecl.name, new Symbol.ParameterSymbol(varDecl.name, varDecl.typeExpr.type));
                 type.addArg(varDecl.symbol);
             }
@@ -153,26 +153,26 @@ public ASTVisitor visit(AST.CallExpr callExpr, boolean enter) {
         return this;
     }
 
-    Type getNullableSimpleType(AST.NullableSimpleTypeExpr simpleTypeExpr) {
+    EZType getNullableSimpleType(AST.NullableSimpleTypeExpr simpleTypeExpr) {
         String baseTypeName = simpleTypeExpr.baseTypeName();
         Symbol typeSymbol = typeDictionary.lookup(baseTypeName);
-        Type baseType;
+        EZType baseType;
         if (typeSymbol == null)
-            baseType = typeDictionary.intern(new Type.TypeStruct(baseTypeName));
+            baseType = typeDictionary.intern(new EZType.EZTypeStruct(baseTypeName));
         else
             baseType = typeSymbol.type;
         if (baseType.isPrimitive())
             throw new CompilerException("Cannot make Nullable instance of primitive type");
-        return typeDictionary.intern(new Type.TypeNullable(baseType));
+        return typeDictionary.intern(new EZType.EZTypeNullable(baseType));
     }
 
-    Type getSimpleType(AST.SimpleTypeExpr simpleTypeExpr) {
+    EZType getSimpleType(AST.SimpleTypeExpr simpleTypeExpr) {
         if (simpleTypeExpr instanceof AST.NullableSimpleTypeExpr nullableSimpleTypeExpr)
             return getNullableSimpleType(nullableSimpleTypeExpr);
         String typeName = simpleTypeExpr.name();
         Symbol typeSymbol = typeDictionary.lookup(typeName);
         if (typeSymbol == null) {
-            return  typeDictionary.intern(new Type.TypeStruct(typeName));
+            return  typeDictionary.intern(new EZType.EZTypeStruct(typeName));
         }
         return typeSymbol.type;
     }
@@ -193,7 +193,7 @@ public ASTVisitor visit(AST.NullableSimpleTypeExpr simpleTypeExpr, boolean enter
         return this;
     }
 
-    Type getArrayType(AST.ArrayTypeExpr arrayTypeExpr) {
+    EZType getArrayType(AST.ArrayTypeExpr arrayTypeExpr) {
         if (arrayTypeExpr instanceof AST.NullableArrayTypeExpr nullableArrayTypeExpr)
             return getNullableArrayType(nullableArrayTypeExpr);
         var elemTypeExpr = arrayTypeExpr.elementType;
@@ -201,7 +201,7 @@ Type getArrayType(AST.ArrayTypeExpr arrayTypeExpr) {
         return typeDictionary.makeArrayType(elemType, false);
     }
 
-    Type getNullableArrayType(AST.NullableArrayTypeExpr arrayTypeExpr) {
+    EZType getNullableArrayType(AST.NullableArrayTypeExpr arrayTypeExpr) {
         var elemTypeExpr = arrayTypeExpr.elementType;
         var elemType = getSimpleType(elemTypeExpr);
         return typeDictionary.makeArrayType(elemType, true);
@@ -229,7 +229,7 @@ public ASTVisitor visit(AST.ReturnTypeExpr returnTypeExpr, boolean enter) {
             // We override the visitor and visit the return type here because
             // we need to associate the return type to the function's return type
             // The visitor mechanism doesn't allow us to associate values between two steps
-            Type.TypeFunction type = (Type.TypeFunction) currentFuncDecl.symbol.type;
+            EZType.EZTypeFunction type = (EZType.EZTypeFunction) currentFuncDecl.symbol.type;
             if (returnTypeExpr.returnType != null) {
                 returnTypeExpr.returnType.accept(this);
                 returnTypeExpr.type = returnTypeExpr.returnType.type;
diff --git a/stackvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java b/stackvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
index 4d384c1..d19f547 100644
--- a/stackvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
+++ b/stackvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
@@ -4,7 +4,7 @@
 import com.compilerprogramming.ezlang.parser.AST;
 import com.compilerprogramming.ezlang.types.Scope;
 import com.compilerprogramming.ezlang.types.Symbol;
-import com.compilerprogramming.ezlang.types.Type;
+import com.compilerprogramming.ezlang.types.EZType;
 
 public class CompiledFunction {
 
@@ -258,12 +258,12 @@ private boolean compileCallExpr(AST.CallExpr callExpr) {
         return false;
     }
 
-    private Type.TypeStruct getStructType(Type t) {
-        if (t instanceof Type.TypeStruct typeStruct) {
+    private EZType.EZTypeStruct getStructType(EZType t) {
+        if (t instanceof EZType.EZTypeStruct typeStruct) {
             return typeStruct;
         }
-        else if (t instanceof Type.TypeNullable ptr &&
-                ptr.baseType instanceof Type.TypeStruct typeStruct) {
+        else if (t instanceof EZType.EZTypeNullable ptr &&
+                ptr.baseType instanceof EZType.EZTypeStruct typeStruct) {
             return typeStruct;
         }
         else
@@ -271,7 +271,7 @@ else if (t instanceof Type.TypeNullable ptr &&
     }
 
     private boolean compileFieldExpr(AST.GetFieldExpr fieldExpr) {
-        Type.TypeStruct typeStruct = getStructType(fieldExpr.object.type);
+        EZType.EZTypeStruct typeStruct = getStructType(fieldExpr.object.type);
         int fieldIndex = typeStruct.getFieldIndex(fieldExpr.fieldName);
         if (fieldIndex < 0)
             throw new CompilerException("Field " + fieldExpr.fieldName + " not found");
@@ -291,7 +291,7 @@ private boolean compileArrayIndexExpr(AST.ArrayLoadExpr arrayIndexExpr) {
     }
 
     private boolean compileSetFieldExpr(AST.SetFieldExpr setFieldExpr) {
-        Type.TypeStruct structType = (Type.TypeStruct) setFieldExpr.object.type;
+        EZType.EZTypeStruct structType = (EZType.EZTypeStruct) setFieldExpr.object.type;
         int fieldIndex = structType.getFieldIndex(setFieldExpr.fieldName);
         if (fieldIndex == -1)
             throw new CompilerException("Field " + setFieldExpr.fieldName + " not found in struct " + structType.name);
@@ -334,7 +334,7 @@ private boolean compileInitExpr(AST.InitExpr initExpr) {
     }
 
     private boolean compileSymbolExpr(AST.NameExpr symbolExpr) {
-        if (symbolExpr.type instanceof Type.TypeFunction functionType)
+        if (symbolExpr.type instanceof EZType.EZTypeFunction functionType)
             code(new Instruction.LoadFunction(functionType));
         else {
             Symbol.VarSymbol varSymbol = (Symbol.VarSymbol) symbolExpr.symbol;
diff --git a/stackvm/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java b/stackvm/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java
index 5c0b365..a4f5c4d 100644
--- a/stackvm/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java
+++ b/stackvm/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java
@@ -5,7 +5,7 @@
 import com.compilerprogramming.ezlang.semantic.SemaAssignTypes;
 import com.compilerprogramming.ezlang.semantic.SemaDefineTypes;
 import com.compilerprogramming.ezlang.types.Symbol;
-import com.compilerprogramming.ezlang.types.Type;
+import com.compilerprogramming.ezlang.types.EZType;
 import com.compilerprogramming.ezlang.types.TypeDictionary;
 
 import java.util.BitSet;
@@ -15,7 +15,7 @@ public class Compiler {
     private void compile(TypeDictionary typeDictionary) {
         for (Symbol symbol: typeDictionary.getLocalSymbols()) {
             if (symbol instanceof Symbol.FunctionTypeSymbol functionSymbol) {
-                Type.TypeFunction functionType = (Type.TypeFunction) functionSymbol.type;
+                EZType.EZTypeFunction functionType = (EZType.EZTypeFunction) functionSymbol.type;
                 functionType.code = new CompiledFunction(functionSymbol);
             }
         }
diff --git a/stackvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java b/stackvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java
index 7fd42cd..fc0237d 100644
--- a/stackvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java
+++ b/stackvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java
@@ -1,7 +1,7 @@
 package com.compilerprogramming.ezlang.compiler;
 
 
-import com.compilerprogramming.ezlang.types.Type;
+import com.compilerprogramming.ezlang.types.EZType;
 
 public class Instruction {
 
@@ -98,8 +98,8 @@ public UnaryOp(int opcode) {
     }
 
     public static class LoadFunction extends Instruction {
-        public Type.TypeFunction functionType;
-        public LoadFunction(Type.TypeFunction typeFunction) {
+        public EZType.EZTypeFunction functionType;
+        public LoadFunction(EZType.EZTypeFunction typeFunction) {
             super(LOAD_FUNC);
             this.functionType = typeFunction;
         }
@@ -122,8 +122,8 @@ public StringBuilder toStr(StringBuilder sb) {
     }
 
     public static class New extends Instruction {
-        public final Type type;
-        public New(Type type) {
+        public final EZType type;
+        public New(EZType type) {
             super(NEW);
             this.type = type;
         }
diff --git a/types/src/main/java/com/compilerprogramming/ezlang/types/Type.java b/types/src/main/java/com/compilerprogramming/ezlang/types/EZType.java
similarity index 74%
rename from types/src/main/java/com/compilerprogramming/ezlang/types/Type.java
rename to types/src/main/java/com/compilerprogramming/ezlang/types/EZType.java
index 1c8514c..22f98ce 100644
--- a/types/src/main/java/com/compilerprogramming/ezlang/types/Type.java
+++ b/types/src/main/java/com/compilerprogramming/ezlang/types/EZType.java
@@ -10,7 +10,7 @@
  * Currently, we support Int, Struct, and Array of Int/Struct.
  * Arrays and Structs are reference types.
  */
-public abstract class Type {
+public abstract class EZType {
 
     // Type classes
     static final byte TVOID = 0;
@@ -25,7 +25,7 @@ public abstract class Type {
     public final byte tclass;    // type class
     public final String name;      // type name, always unique
 
-    protected Type(byte tclass, String name) {
+    protected EZType(byte tclass, String name) {
         this.tclass = tclass;
         this.name = name;
     }
@@ -37,7 +37,7 @@ protected Type(byte tclass, String name) {
     public boolean equals(Object o) {
         if (this == o) return true;
         if (o == null || getClass() != o.getClass()) return false;
-        Type type = (Type) o;
+        EZType type = (EZType) o;
         return tclass == type.tclass && Objects.equals(name, type.name);
     }
 
@@ -52,16 +52,16 @@ public String toString() {
     }
     public String name() { return name; }
 
-    public boolean isAssignable(Type other) {
-        if (other == null || other instanceof TypeVoid || other instanceof TypeUnknown)
+    public boolean isAssignable(EZType other) {
+        if (other == null || other instanceof EZTypeVoid || other instanceof EZTypeUnknown)
             return false;
         if (this == other || equals(other)) return true;
-        if (this instanceof TypeNullable nullable) {
-            if (other instanceof TypeNull)
+        if (this instanceof EZTypeNullable nullable) {
+            if (other instanceof EZTypeNull)
                 return true;
             return nullable.baseType.isAssignable(other);
         }
-        else if (other instanceof TypeNullable nullable) {
+        else if (other instanceof EZTypeNullable nullable) {
             // At compile time we allow nullable value to be
             // assigned to base type, but null check must be inserted
             // Optimizer may remove null check
@@ -74,25 +74,25 @@ else if (other instanceof TypeNullable nullable) {
      * Represents no type - useful for defining functions
      * that do not return a value
      */
-    public static class TypeVoid extends Type {
-        public TypeVoid() {
+    public static class EZTypeVoid extends EZType {
+        public EZTypeVoid() {
             super(TVOID, "$Void");
         }
     }
 
-    public static class TypeUnknown extends Type {
-        public TypeUnknown() {
+    public static class EZTypeUnknown extends EZType {
+        public EZTypeUnknown() {
             super(TUNKNOWN, "$Unknown");
         }
     }
 
-    public static class TypeNull extends Type {
-        public TypeNull() { super(TNULL, "$Null"); }
+    public static class EZTypeNull extends EZType {
+        public EZTypeNull() { super(TNULL, "$Null"); }
     }
 
-    public static class TypeInteger extends Type {
+    public static class EZTypeInteger extends EZType {
 
-        public TypeInteger() {
+        public EZTypeInteger() {
             super (TINT, "Int");
         }
         @Override
@@ -101,15 +101,15 @@ public boolean isPrimitive() {
         }
     }
 
-    public static class TypeStruct extends Type {
+    public static class EZTypeStruct extends EZType {
         ArrayList<String> fieldNames = new ArrayList<>();
-        ArrayList<Type> fieldTypes = new ArrayList<>();
+        ArrayList<EZType> fieldTypes = new ArrayList<>();
         public boolean pending = true;
 
-        public TypeStruct(String name) {
+        public EZTypeStruct(String name) {
             super(TSTRUCT, name);
         }
-        public void addField(String name, Type type) {
+        public void addField(String name, EZType type) {
             if (!pending)
                 throw new CompilerException("Cannot add field to an already defined struct");
             if (fieldNames.contains(name))
@@ -124,13 +124,13 @@ public String describe() {
             sb.append("struct ").append(name()).append("{");
             for (int i = 0; i < fieldNames.size(); i++) {
                 String fieldName = fieldNames.get(i);
-                Type fieldType = fieldTypes.get(i);
+                EZType fieldType = fieldTypes.get(i);
                 sb.append(fieldName).append(": ").append(fieldType.name()).append(";");
             }
             sb.append("}");
             return sb.toString();
         }
-        public Type getField(String name) {
+        public EZType getField(String name) {
             int index = fieldNames.indexOf(name);
             if (index < 0)
                 return null;
@@ -144,40 +144,40 @@ public int getFieldIndex(String name) {
         public void complete() { pending = false; }
     }
 
-    public static class TypeArray extends Type {
-        Type elementType;
+    public static class EZTypeArray extends EZType {
+        EZType elementType;
 
-        public TypeArray(Type baseType) {
+        public EZTypeArray(EZType baseType) {
             super(TARRAY, "[" + baseType.name() + "]");
             this.elementType = baseType;
-            if (baseType instanceof TypeArray)
+            if (baseType instanceof EZTypeArray)
                 throw new CompilerException("Array of array type not supported");
         }
-        public Type getElementType() {
+        public EZType getElementType() {
             return elementType;
         }
     }
 
     // This is really a dedicated Union type for T|Null.
-    public static class TypeNullable extends Type {
-        public final Type baseType;
-        public TypeNullable(Type baseType) {
+    public static class EZTypeNullable extends EZType {
+        public final EZType baseType;
+        public EZTypeNullable(EZType baseType) {
             super(TNULLABLE, baseType.name()+"?");
             this.baseType = baseType;
         }
     }
 
-    public static class TypeFunction extends Type {
+    public static class EZTypeFunction extends EZType {
         public final List<Symbol> args = new ArrayList<>();
-        public Type returnType;
+        public EZType returnType;
         public Object code;
-        public TypeFunction(String name) {
+        public EZTypeFunction(String name) {
             super(TFUNC, name);
         }
         public void addArg(Symbol arg) {
             args.add(arg);
         }
-        public void setReturnType(Type returnType) {
+        public void setReturnType(EZType returnType) {
             this.returnType = returnType;
         }
         public String describe() {
@@ -191,7 +191,7 @@ public String describe() {
                 sb.append(arg.name).append(": ").append(arg.type.name());
             }
             sb.append(")");
-            if (!(returnType instanceof Type.TypeVoid)) {
+            if (!(returnType instanceof EZTypeVoid)) {
                 sb.append("->").append(returnType.name());
             }
             return sb.toString();
diff --git a/types/src/main/java/com/compilerprogramming/ezlang/types/Symbol.java b/types/src/main/java/com/compilerprogramming/ezlang/types/Symbol.java
index c15a596..bd5d776 100644
--- a/types/src/main/java/com/compilerprogramming/ezlang/types/Symbol.java
+++ b/types/src/main/java/com/compilerprogramming/ezlang/types/Symbol.java
@@ -1,34 +1,32 @@
 package com.compilerprogramming.ezlang.types;
 
-import java.util.Objects;
-
 /**
  * A symbol is something that has a name and a type.
  */
 public abstract class Symbol {
 
     public final String name;
-    public Type type;
+    public EZType type;
 
-    protected Symbol(String name, Type type) {
+    protected Symbol(String name, EZType type) {
         this.name = name;
         this.type = type;
     }
 
     public static class TypeSymbol extends Symbol {
-        public TypeSymbol(String name, Type type) {
+        public TypeSymbol(String name, EZType type) {
             super(name, type);
         }
     }
 
     public static class FunctionTypeSymbol extends Symbol {
         public final Object functionDecl;
-        public FunctionTypeSymbol(String name, Type.TypeFunction type, Object functionDecl) {
+        public FunctionTypeSymbol(String name, EZType.EZTypeFunction type, Object functionDecl) {
             super(name, type);
             this.functionDecl = functionDecl;
         }
         public Object code() {
-            Type.TypeFunction function = (Type.TypeFunction) type;
+            EZType.EZTypeFunction function = (EZType.EZTypeFunction) type;
             return function.code;
         }
     }
@@ -36,13 +34,13 @@ public Object code() {
     public static class VarSymbol extends Symbol {
         // Values assigned by bytecode compiler
         public int regNumber;
-        public VarSymbol(String name, Type type) {
+        public VarSymbol(String name, EZType type) {
             super(name, type);
         }
     }
 
     public static class ParameterSymbol extends VarSymbol {
-        public ParameterSymbol(String name, Type type) {
+        public ParameterSymbol(String name, EZType type) {
             super(name, type);
         }
     }
diff --git a/types/src/main/java/com/compilerprogramming/ezlang/types/TypeDictionary.java b/types/src/main/java/com/compilerprogramming/ezlang/types/TypeDictionary.java
index 9fc233b..18dcf83 100644
--- a/types/src/main/java/com/compilerprogramming/ezlang/types/TypeDictionary.java
+++ b/types/src/main/java/com/compilerprogramming/ezlang/types/TypeDictionary.java
@@ -3,56 +3,56 @@
 import com.compilerprogramming.ezlang.exceptions.CompilerException;
 
 public class TypeDictionary extends Scope {
-    public final Type.TypeUnknown UNKNOWN;
-    public final Type.TypeInteger INT;
-    public final Type.TypeNull NULL;
-    public final Type.TypeVoid VOID;
+    public final EZType.EZTypeUnknown UNKNOWN;
+    public final EZType.EZTypeInteger INT;
+    public final EZType.EZTypeNull NULL;
+    public final EZType.EZTypeVoid VOID;
 
     public TypeDictionary() {
         super(null);
-        INT = (Type.TypeInteger) intern(new Type.TypeInteger());
-        UNKNOWN = (Type.TypeUnknown) intern(new Type.TypeUnknown());
-        NULL = (Type.TypeNull) intern(new Type.TypeNull());
-        VOID = (Type.TypeVoid) intern(new Type.TypeVoid());
+        INT = (EZType.EZTypeInteger) intern(new EZType.EZTypeInteger());
+        UNKNOWN = (EZType.EZTypeUnknown) intern(new EZType.EZTypeUnknown());
+        NULL = (EZType.EZTypeNull) intern(new EZType.EZTypeNull());
+        VOID = (EZType.EZTypeVoid) intern(new EZType.EZTypeVoid());
     }
-    public Type makeArrayType(Type elementType, boolean isNullable) {
+    public EZType makeArrayType(EZType elementType, boolean isNullable) {
         switch (elementType) {
-            case Type.TypeInteger ti -> {
-                var arrayType = intern(new Type.TypeArray(ti));
-                return isNullable ? intern(new Type.TypeNullable(arrayType)) : arrayType;
+            case EZType.EZTypeInteger ti -> {
+                var arrayType = intern(new EZType.EZTypeArray(ti));
+                return isNullable ? intern(new EZType.EZTypeNullable(arrayType)) : arrayType;
             }
-            case Type.TypeStruct ts -> {
-                var arrayType = intern(new Type.TypeArray(ts));
-                return isNullable ? intern(new Type.TypeNullable(arrayType)) : arrayType;
+            case EZType.EZTypeStruct ts -> {
+                var arrayType = intern(new EZType.EZTypeArray(ts));
+                return isNullable ? intern(new EZType.EZTypeNullable(arrayType)) : arrayType;
             }
-            case Type.TypeNullable nullable when nullable.baseType instanceof Type.TypeStruct -> {
-                var arrayType = intern(new Type.TypeArray(elementType));
-                return isNullable ? intern(new Type.TypeNullable(arrayType)) : arrayType;
+            case EZType.EZTypeNullable nullable when nullable.baseType instanceof EZType.EZTypeStruct -> {
+                var arrayType = intern(new EZType.EZTypeArray(elementType));
+                return isNullable ? intern(new EZType.EZTypeNullable(arrayType)) : arrayType;
             }
             case null, default -> throw new CompilerException("Unsupported array element type: " + elementType);
         }
     }
-    public Type intern(Type type) {
+    public EZType intern(EZType type) {
         Symbol symbol = lookup(type.name());
         if (symbol != null) return symbol.type;
         return install(type.name(), new Symbol.TypeSymbol(type.name(), type)).type;
     }
-    public Type merge(Type t1, Type t2) {
-        if (t1 instanceof Type.TypeNull && t2 instanceof Type.TypeStruct) {
-            return intern(new Type.TypeNullable(t2));
+    public EZType merge(EZType t1, EZType t2) {
+        if (t1 instanceof EZType.EZTypeNull && t2 instanceof EZType.EZTypeStruct) {
+            return intern(new EZType.EZTypeNullable(t2));
         }
-        else if (t2 instanceof Type.TypeNull && t1 instanceof Type.TypeStruct) {
-            return intern(new Type.TypeNullable(t1));
+        else if (t2 instanceof EZType.EZTypeNull && t1 instanceof EZType.EZTypeStruct) {
+            return intern(new EZType.EZTypeNullable(t1));
         }
-        else if (t1 instanceof Type.TypeArray && t2 instanceof Type.TypeNull) {
-            return intern(new Type.TypeNullable(t1));
+        else if (t1 instanceof EZType.EZTypeArray && t2 instanceof EZType.EZTypeNull) {
+            return intern(new EZType.EZTypeNullable(t1));
         }
-        else if (t2 instanceof Type.TypeArray && t1 instanceof Type.TypeNull) {
-            return intern(new Type.TypeNullable(t2));
+        else if (t2 instanceof EZType.EZTypeArray && t1 instanceof EZType.EZTypeNull) {
+            return intern(new EZType.EZTypeNullable(t2));
         }
-        else if (t1 instanceof Type.TypeUnknown)
+        else if (t1 instanceof EZType.EZTypeUnknown)
             return t2;
-        else if (t2 instanceof Type.TypeUnknown)
+        else if (t2 instanceof EZType.EZTypeUnknown)
             return t1;
         else if (!t1.equals(t2))
             throw new CompilerException("Unsupported merge type: " + t1 + " and " + t2);
diff --git a/types/src/test/java/com/compilerprogramming/ezlang/types/TestTypes.java b/types/src/test/java/com/compilerprogramming/ezlang/types/TestTypes.java
index 7cb6dc7..3354854 100644
--- a/types/src/test/java/com/compilerprogramming/ezlang/types/TestTypes.java
+++ b/types/src/test/java/com/compilerprogramming/ezlang/types/TestTypes.java
@@ -1,22 +1,17 @@
 package com.compilerprogramming.ezlang.types;
 
-import org.junit.Assert;
-import org.junit.Test;
-
-import java.util.ArrayList;
-
 public class TestTypes {
 
-    Type buildStruct1(TypeDictionary typeDictionary) {
-        Type.TypeStruct s = new Type.TypeStruct("S1");
+    EZType buildStruct1(TypeDictionary typeDictionary) {
+        EZType.EZTypeStruct s = new EZType.EZTypeStruct("S1");
         s.addField("a", typeDictionary.INT);
         s.addField("b", typeDictionary.INT);
         s.complete();
         return typeDictionary.intern(s);
     }
 
-    Type buildStruct2(TypeDictionary typeDictionary) {
-        Type.TypeStruct s = new Type.TypeStruct("S2");
+    EZType buildStruct2(TypeDictionary typeDictionary) {
+        EZType.EZTypeStruct s = new EZType.EZTypeStruct("S2");
         s.addField("a", typeDictionary.INT);
         s.addField("b", typeDictionary.INT);
         s.complete();

From 9f52d795c119dd76cb960e308c024fde24f6b238 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Sun, 27 Jul 2025 17:48:08 +0100
Subject: [PATCH 093/119] Rename SONType to Type - and all sub types too so
 that it is easy to merge code from Simple

---
 .../ezlang/compiler/Compiler.java             | 138 +++++++++---------
 .../ezlang/compiler/Var.java                  |  18 +--
 .../ezlang/compiler/codegen/BuildLRG.java     |   5 +-
 .../ezlang/compiler/codegen/CodeGen.java      |  26 ++--
 .../ezlang/compiler/codegen/ElfFile.java      |   3 +-
 .../ezlang/compiler/codegen/Encoding.java     |  23 ++-
 .../compiler/codegen/GlobalCodeMotion.java    |   4 +-
 .../ezlang/compiler/codegen/Machine.java      |   8 +-
 .../ezlang/compiler/nodes/AddFNode.java       |  16 +-
 .../ezlang/compiler/nodes/AddNode.java        |  27 ++--
 .../ezlang/compiler/nodes/AndNode.java        |  26 ++--
 .../ezlang/compiler/nodes/BoolNode.java       |  32 ++--
 .../ezlang/compiler/nodes/CFGNode.java        |   4 +-
 .../ezlang/compiler/nodes/CProjNode.java      |  16 +-
 .../ezlang/compiler/nodes/CallEndNode.java    |  20 +--
 .../ezlang/compiler/nodes/CallNode.java       |  19 ++-
 .../ezlang/compiler/nodes/CalleeSaveNode.java |   7 +-
 .../ezlang/compiler/nodes/CastNode.java       |   8 +-
 .../ezlang/compiler/nodes/ConstantNode.java   |  20 +--
 .../ezlang/compiler/nodes/CtrlNode.java       |   4 +-
 .../ezlang/compiler/nodes/DivFNode.java       |  16 +-
 .../ezlang/compiler/nodes/DivNode.java        |  22 +--
 .../ezlang/compiler/nodes/FRefNode.java       |   4 +-
 .../ezlang/compiler/nodes/FunNode.java        |  22 +--
 .../ezlang/compiler/nodes/IfNode.java         |  17 +--
 .../ezlang/compiler/nodes/LoadNode.java       |  20 +--
 .../ezlang/compiler/nodes/LogicalNode.java    |   6 +-
 .../ezlang/compiler/nodes/LoopNode.java       |  14 +-
 .../compiler/nodes/MachConcreteNode.java      |   4 +-
 .../ezlang/compiler/nodes/MemMergeNode.java   |   6 +-
 .../ezlang/compiler/nodes/MemOpNode.java      |  20 +--
 .../ezlang/compiler/nodes/MinusFNode.java     |  12 +-
 .../ezlang/compiler/nodes/MinusNode.java      |  16 +-
 .../ezlang/compiler/nodes/MulFNode.java       |  18 +--
 .../ezlang/compiler/nodes/MulNode.java        |  32 ++--
 .../ezlang/compiler/nodes/NeverNode.java      |   7 +-
 .../ezlang/compiler/nodes/NewNode.java        |  36 ++---
 .../ezlang/compiler/nodes/Node.java           |  28 ++--
 .../ezlang/compiler/nodes/NotNode.java        |  14 +-
 .../ezlang/compiler/nodes/OrNode.java         |  25 ++--
 .../ezlang/compiler/nodes/ParmNode.java       |   4 +-
 .../ezlang/compiler/nodes/PhiNode.java        |  36 ++---
 .../ezlang/compiler/nodes/ProjNode.java       |  14 +-
 .../ezlang/compiler/nodes/ReadOnlyNode.java   |  12 +-
 .../ezlang/compiler/nodes/RegionNode.java     |  10 +-
 .../ezlang/compiler/nodes/ReturnNode.java     |  30 ++--
 .../ezlang/compiler/nodes/RoundF32Node.java   |  14 +-
 .../ezlang/compiler/nodes/SarNode.java        |  28 ++--
 .../ezlang/compiler/nodes/ScopeNode.java      |  10 +-
 .../ezlang/compiler/nodes/ShlNode.java        |  26 ++--
 .../ezlang/compiler/nodes/ShrNode.java        |  27 ++--
 .../ezlang/compiler/nodes/SplitNode.java      |   4 +-
 .../ezlang/compiler/nodes/StartNode.java      |   9 +-
 .../ezlang/compiler/nodes/StopNode.java       |   6 +-
 .../ezlang/compiler/nodes/StoreNode.java      |  26 ++--
 .../ezlang/compiler/nodes/StructNode.java     |  12 +-
 .../ezlang/compiler/nodes/SubFNode.java       |  16 +-
 .../ezlang/compiler/nodes/SubNode.java        |  22 +--
 .../ezlang/compiler/nodes/ToFloatNode.java    |  18 +--
 .../ezlang/compiler/nodes/XCtrlNode.java      |   4 +-
 .../ezlang/compiler/nodes/XorNode.java        |  24 +--
 .../compiler/nodes/cpus/arm/CallARM.java      |   8 +-
 .../compiler/nodes/cpus/arm/FloatARM.java     |   5 +-
 .../compiler/nodes/cpus/arm/IntARM.java       |   6 +-
 .../compiler/nodes/cpus/arm/LoadARM.java      |   6 +-
 .../compiler/nodes/cpus/arm/MemOpARM.java     |   4 +-
 .../compiler/nodes/cpus/arm/NegARM.java       |   1 -
 .../compiler/nodes/cpus/arm/StoreARM.java     |   6 +-
 .../compiler/nodes/cpus/arm/SubIARM.java      |   1 -
 .../compiler/nodes/cpus/arm/TFPARM.java       |   2 -
 .../compiler/nodes/cpus/arm/UJmpARM.java      |   4 +-
 .../ezlang/compiler/nodes/cpus/arm/arm.java   |  60 ++++----
 .../compiler/nodes/cpus/riscv/AUIPC.java      |   4 +-
 .../compiler/nodes/cpus/riscv/CallRISC.java   |   8 +-
 .../compiler/nodes/cpus/riscv/FltRISC.java    |   2 -
 .../compiler/nodes/cpus/riscv/ImmRISC.java    |   4 +-
 .../compiler/nodes/cpus/riscv/IntRISC.java    |   6 +-
 .../ezlang/compiler/nodes/cpus/riscv/LUI.java |  12 +-
 .../compiler/nodes/cpus/riscv/MemOpRISC.java  |  34 ++---
 .../compiler/nodes/cpus/riscv/TFPRISC.java    |   4 +-
 .../compiler/nodes/cpus/riscv/UJmpRISC.java   |   4 +-
 .../compiler/nodes/cpus/riscv/riscv.java      |  60 ++++----
 .../nodes/cpus/x86_64_v2/CallX86.java         |   8 +-
 .../nodes/cpus/x86_64_v2/CmpMemX86.java       |  10 +-
 .../compiler/nodes/cpus/x86_64_v2/ImmX86.java |   1 -
 .../compiler/nodes/cpus/x86_64_v2/IntX86.java |  14 +-
 .../nodes/cpus/x86_64_v2/LoadX86.java         |  31 ++--
 .../nodes/cpus/x86_64_v2/MemOpX86.java        |   4 +-
 .../compiler/nodes/cpus/x86_64_v2/NewX86.java |   1 -
 .../nodes/cpus/x86_64_v2/SplitX86.java        |  10 +-
 .../nodes/cpus/x86_64_v2/StoreX86.java        |   7 +-
 .../compiler/nodes/cpus/x86_64_v2/TFPX86.java |  12 +-
 .../nodes/cpus/x86_64_v2/UJmpX86.java         |   4 +-
 .../nodes/cpus/x86_64_v2/x86_64_v2.java       |  80 +++++-----
 .../ezlang/compiler/print/ASMPrinter.java     |  12 +-
 .../ezlang/compiler/sontypes/Field.java       |  22 +--
 .../sontypes/{SONType.java => Type.java}      |  80 +++++-----
 .../{SONTypeFloat.java => TypeFloat.java}     |  40 ++---
 .../{SONTypeFunPtr.java => TypeFunPtr.java}   |  51 +++----
 .../{SONTypeInteger.java => TypeInteger.java} |  54 +++----
 .../{SONTypeMem.java => TypeMem.java}         |  30 ++--
 .../{SONTypeMemPtr.java => TypeMemPtr.java}   |  48 +++---
 .../{SONTypeNil.java => TypeNil.java}         |  28 ++--
 .../{SONTypePtr.java => TypePtr.java}         |  32 ++--
 .../{SONTypeRPC.java => TypeRPC.java}         |  28 ++--
 .../{SONTypeStruct.java => TypeStruct.java}   |  44 +++---
 .../{SONTypeTuple.java => TypeTuple.java}     |  60 ++++----
 107 files changed, 1020 insertions(+), 1047 deletions(-)
 rename seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/{SONType.java => Type.java} (79%)
 rename seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/{SONTypeFloat.java => TypeFloat.java} (63%)
 rename seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/{SONTypeFunPtr.java => TypeFunPtr.java} (62%)
 rename seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/{SONTypeInteger.java => TypeInteger.java} (67%)
 rename seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/{SONTypeMem.java => TypeMem.java} (59%)
 rename seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/{SONTypeMemPtr.java => TypeMemPtr.java} (60%)
 rename seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/{SONTypeNil.java => TypeNil.java} (61%)
 rename seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/{SONTypePtr.java => TypePtr.java} (51%)
 rename seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/{SONTypeRPC.java => TypeRPC.java} (74%)
 rename seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/{SONTypeStruct.java => TypeStruct.java} (84%)
 rename seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/{SONTypeTuple.java => TypeTuple.java} (50%)

diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java
index 813b708..87b9624 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java
@@ -57,22 +57,22 @@ public class Compiler {
     // Mapping from a type name to a Type.  The string name matches
     // `type.str()` call.  No TypeMemPtrs are in here, because Simple does not
     // have C-style '*ptr' references.
-    public static HashMap<String, SONType> TYPES = new HashMap<>();
+    public static HashMap<String, Type> TYPES = new HashMap<>();
 
     private ArrayList<Node> ctorStack = new ArrayList<>();
 
-    public Compiler(CodeGen codeGen, SONTypeInteger arg) {
+    public Compiler(CodeGen codeGen, TypeInteger arg) {
         this._code = codeGen;
     }
 
     public void parse() {
         this.typeDictionary = createAST(_code._src);
-        Map<String, SONType> types = new HashMap<>();
+        Map<String, Type> types = new HashMap<>();
         populateDefaultTypes(types);
         populateTypes(types);
         TYPES.putAll(types);
-        ZERO  = con(SONTypeInteger.ZERO).keep();
-        NIL  = con(SONType.NIL).keep();
+        ZERO  = con(TypeInteger.ZERO).keep();
+        NIL  = con(Type.NIL).keep();
         XCTRL= new XCtrlNode().peephole().keep();
         processFunctions();
     }
@@ -89,19 +89,19 @@ public TypeDictionary createAST(String src) {
     }
 
 
-    private void populateDefaultTypes(Map<String,SONType> types) {
+    private void populateDefaultTypes(Map<String, Type> types) {
         // Pre-create int, [int] and *[int] types
-        types.put(typeDictionary.INT.name(), SONTypeInteger.BOT);
-        var intArrayType = SONTypeStruct.makeAry(SONTypeInteger.U32, _code.getALIAS(), SONTypeInteger.BOT, _code.getALIAS());
-        var ptrIntArrayType = SONTypeMemPtr.make(intArrayType);
+        types.put(typeDictionary.INT.name(), TypeInteger.BOT);
+        var intArrayType = TypeStruct.makeAry(TypeInteger.U32, _code.getALIAS(), TypeInteger.BOT, _code.getALIAS());
+        var ptrIntArrayType = TypeMemPtr.make(intArrayType);
         types.put("[" + typeDictionary.INT.name() + "]", ptrIntArrayType);
         // Also get the types created by default
-        for (SONType t: SONType.gather()) {
+        for (Type t: Type.gather()) {
             types.put(t.str(), t);
         }
     }
 
-    private void populateTypes(Map<String, SONType> structTypes) {
+    private void populateTypes(Map<String, Type> structTypes) {
         // First process struct types
         for (var symbol: typeDictionary.getLocalSymbols()) {
             if (symbol instanceof Symbol.TypeSymbol typeSymbol) {
@@ -129,21 +129,21 @@ private void processFunctions() {
         }
     }
 
-    private void createFunctionType(Map<String, SONType> structTypes, Symbol.FunctionTypeSymbol functionSymbol) {
+    private void createFunctionType(Map<String, Type> structTypes, Symbol.FunctionTypeSymbol functionSymbol) {
         EZType.EZTypeFunction functionType = (EZType.EZTypeFunction) functionSymbol.type;
-        Ary<SONType> params = new Ary<>(SONType.class);
+        Ary<Type> params = new Ary<>(Type.class);
         for (var symbol: functionType.args) {
             if (symbol instanceof Symbol.ParameterSymbol parameterSymbol) {
-                SONType paramType = getSONType(structTypes, parameterSymbol.type);
+                Type paramType = getSONType(structTypes, parameterSymbol.type);
                 params.push(paramType);
             }
         }
         var retType = getSONType(structTypes, functionType.returnType);
-        SONTypeFunPtr tfp = _code.makeFun2(new SONTypeTuple(params.asAry()),retType);
+        TypeFunPtr tfp = _code.makeFun2(new TypeTuple(params.asAry()),retType);
         structTypes.put(functionSymbol.name, tfp);
     }
 
-    private void createSONStructType(Map<String, SONType> structTypes, String typeName, EZType.EZTypeStruct typeStruct) {
+    private void createSONStructType(Map<String, Type> structTypes, String typeName, EZType.EZTypeStruct typeStruct) {
         Ary<Field> fs = new Ary<>(Field.class);
         for (int i = 0; i < typeStruct.numFields(); i++) {
             String name = typeStruct.getFieldName(i);
@@ -152,10 +152,10 @@ private void createSONStructType(Map<String, SONType> structTypes, String typeNa
         }
         // A Struct type may have been created before because of
         // reference from itself; in which case we need to update that
-        SONType fref = structTypes.get(typeName);
+        Type fref = structTypes.get(typeName);
         if (fref != null) {
-            if (fref instanceof SONTypeMemPtr ptr &&
-                ptr._obj instanceof SONTypeStruct ts) {
+            if (fref instanceof TypeMemPtr ptr &&
+                ptr._obj instanceof TypeStruct ts) {
                 assert ts._fields.length == 0;
                 // Add the fields to the existing type
                 ts._fields = fs.asAry();
@@ -163,8 +163,8 @@ private void createSONStructType(Map<String, SONType> structTypes, String typeNa
             else throw new CompilerException("Expected struct type " + typeName + " but got " + fref);
         }
         else {
-            var ts = SONTypeStruct.make(typeName, fs.asAry());
-            var ptr = SONTypeMemPtr.make((byte)2,ts);
+            var ts = TypeStruct.make(typeName, fs.asAry());
+            var ptr = TypeMemPtr.make((byte)2,ts);
             structTypes.put(typeName,ptr);
         }
     }
@@ -181,10 +181,10 @@ else if (type instanceof EZType.EZTypeStruct typeStruct) {
         }
         else if (type instanceof EZType.EZTypeInteger ||
                  type instanceof EZType.EZTypeVoid) {
-            return SONTypeInteger.BOT.str();
+            return TypeInteger.BOT.str();
         }
         else if (type instanceof EZType.EZTypeNull) {
-            return SONTypeNil.NIL.str();
+            return TypeNil.NIL.str();
         }
         else if (type instanceof EZType.EZTypeNullable typeNullable) {
             return getSONTypeName(typeNullable.baseType)+"?";
@@ -192,16 +192,16 @@ else if (type instanceof EZType.EZTypeNullable typeNullable) {
         else throw new CompilerException("Not yet implemented " + type.name());
     }
 
-    private SONType getSONType(Map<String, SONType> structTypes, EZType type) {
-        SONType t = structTypes.get(type.name());
+    private Type getSONType(Map<String, Type> structTypes, EZType type) {
+        Type t = structTypes.get(type.name());
         if (t != null) return t;
         if (type instanceof EZType.EZTypeStruct) {
             // For struct types in EeZee language a reference
             // to T means *T in SoN
             // Create SON struct type
-            SONTypeStruct ts = SONTypeStruct.make(type.name, new Field[0]);
+            TypeStruct ts = TypeStruct.make(type.name, new Field[0]);
             // Now create *T
-            SONTypeMemPtr ptr = SONTypeMemPtr.make((byte)2,ts);
+            TypeMemPtr ptr = TypeMemPtr.make((byte)2,ts);
             // EeZee T maps to SoN *T
             structTypes.put(type.name(), ptr);
             return ptr;
@@ -209,23 +209,23 @@ private SONType getSONType(Map<String, SONType> structTypes, EZType type) {
         else if (type instanceof EZType.EZTypeArray typeArray) {
             // A reference to array in EeZee means
             // *array in SoN
-            SONType elementType = getSONType(structTypes,typeArray.getElementType());
-            SONTypeStruct ts = SONTypeStruct.makeArray(SONTypeInteger.U32, _code.getALIAS(), elementType, _code.getALIAS());
-            SONTypeMemPtr ptr = SONTypeMemPtr.make((byte)2,ts);
+            Type elementType = getSONType(structTypes,typeArray.getElementType());
+            TypeStruct ts = TypeStruct.makeArray(TypeInteger.U32, _code.getALIAS(), elementType, _code.getALIAS());
+            TypeMemPtr ptr = TypeMemPtr.make((byte)2,ts);
             structTypes.put(typeArray.name(), ptr); // Array type name is not same as ptr str()
             return ptr;
         }
         else if (type instanceof EZType.EZTypeNullable typeNullable) {
-            SONType baseType = getSONType(structTypes,typeNullable.baseType);
-            SONTypeMemPtr ptr = null;
-            if (baseType instanceof SONTypeMemPtr ptr1) {
+            Type baseType = getSONType(structTypes,typeNullable.baseType);
+            TypeMemPtr ptr = null;
+            if (baseType instanceof TypeMemPtr ptr1) {
                 if (ptr1.nullable())
                     ptr = ptr1;
                 else
-                    ptr = SONTypeMemPtr.make((byte)3,ptr1._obj);
+                    ptr = TypeMemPtr.make((byte)3,ptr1._obj);
             }
             else
-                ptr = SONTypeMemPtr.make((byte)2,(SONTypeStruct) baseType);
+                ptr = TypeMemPtr.make((byte)2,(TypeStruct) baseType);
             structTypes.put(typeNullable.name(), ptr);
             return ptr;
         }
@@ -244,7 +244,7 @@ private void defineScopedVars(Scope scope, ScopeNode scopeNode, FunNode fun) {
         for (Symbol symbol: scope.getLocalSymbols()) {
             if (symbol instanceof Symbol.VarSymbol varSymbol) {
                 varSymbol.regNumber = REGNUM++;
-                SONType sonType = TYPES.get(varSymbol.type.name());
+                Type sonType = TYPES.get(varSymbol.type.name());
                 if (sonType == null)
                     throw new CompilerException("Unknown SON Type "+varSymbol.type.name());
                 Node init = null;
@@ -259,13 +259,13 @@ private void defineScopedVars(Scope scope, ScopeNode scopeNode, FunNode fun) {
 
     private void generateFunction(Symbol.FunctionTypeSymbol functionTypeSymbol) {
         _scope = new ScopeNode();
-        _scope.define(ScopeNode.CTRL, SONType.CONTROL   , false, null);
-        _scope.define(ScopeNode.MEM0, SONTypeMem.BOT    , false, null);
+        _scope.define(ScopeNode.CTRL, Type.CONTROL   , false, null);
+        _scope.define(ScopeNode.MEM0, TypeMem.BOT    , false, null);
 
         ctrl(XCTRL);
         _scope.mem(new MemMergeNode(false));
 
-        var funType = (SONTypeFunPtr) TYPES.get(functionTypeSymbol.name);
+        var funType = (TypeFunPtr) TYPES.get(functionTypeSymbol.name);
         if (funType == null) throw new CompilerException("Function " + functionTypeSymbol.name + " not found");
 
         // Parse whole program, as-if function header "{ int arg -> body }"
@@ -295,7 +295,7 @@ Node showGraph() {
     /**
      *  Parses a function body, assuming the header is parsed.
      */
-    private ReturnNode generateFunctionBody(Symbol.FunctionTypeSymbol functionTypeSymbol,SONTypeFunPtr sig) {
+    private ReturnNode generateFunctionBody(Symbol.FunctionTypeSymbol functionTypeSymbol, TypeFunPtr sig) {
         // Stack parser state on the local Java stack, and unstack it later
         Node oldctrl = ctrl().keep();
         Node oldmem  = _scope.mem().keep();
@@ -310,13 +310,13 @@ private ReturnNode generateFunctionBody(Symbol.FunctionTypeSymbol functionTypeSy
         // the exact single function).
         _code.link(fun);
 
-        Node rpc = new ParmNode("$rpc",0,SONTypeRPC.BOT,fun,con(SONTypeRPC.BOT)).peephole();
+        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(null,null).init();
         assert r.inProgress();
-        PhiNode rmem = new PhiNode(ScopeNode.MEM0,SONTypeMem.BOT,r,null).init();
-        PhiNode rrez = new PhiNode(ScopeNode.ARG0,SONType.BOTTOM,r,null).init();
+        PhiNode rmem = new PhiNode(ScopeNode.MEM0, TypeMem.BOT,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();
@@ -330,7 +330,7 @@ private ReturnNode generateFunctionBody(Symbol.FunctionTypeSymbol functionTypeSy
         // Private mem alias tracking per function
         MemMergeNode mem = new MemMergeNode(true);
         mem.addDef(null);       // Alias#0
-        mem.addDef(new ParmNode(ScopeNode.MEM0,1,SONTypeMem.BOT,fun,con(SONTypeMem.BOT)).peephole()); // All aliases
+        mem.addDef(new ParmNode(ScopeNode.MEM0,1, TypeMem.BOT,fun,con(TypeMem.BOT)).peephole()); // All aliases
         _scope.mem(mem);
         // All args, "as-if" called externally
         AST.FuncDecl funcDecl = (AST.FuncDecl) functionTypeSymbol.functionDecl;
@@ -341,7 +341,7 @@ private ReturnNode generateFunctionBody(Symbol.FunctionTypeSymbol functionTypeSy
         Node last = compileStatement(funcDecl.block);
 
         // Last expression is the return
-        if( ctrl()._type==SONType.CONTROL )
+        if( ctrl()._type== Type.CONTROL )
             fun.addReturn(ctrl(), _scope.mem().merge(), last);
 
         // Pop off the inProgress node on the multi-exit Region merge
@@ -411,15 +411,15 @@ private Node compileExpr(AST.Expr expr) {
     private Node compileFieldExpr(AST.GetFieldExpr getFieldExpr) {
         Node objPtr = compileExpr(getFieldExpr.object).keep();
         // Sanity check expr for being a reference
-        if( !(objPtr._type instanceof SONTypeMemPtr ptr) ) {
+        if( !(objPtr._type instanceof TypeMemPtr ptr) ) {
             throw new CompilerException("Unexpected type " + objPtr._type.str());
         }
         String name = getFieldExpr.fieldName;
-        SONTypeStruct base = ptr._obj;
+        TypeStruct base = ptr._obj;
         int fidx = base.find(name);
         if( fidx == -1 ) throw error("Accessing unknown field '" + name + "' from '" + ptr.str() + "'");
         Field f = base._fields[fidx];  // Field from field index
-        SONType tf = f._type;
+        Type tf = f._type;
         // Field offset; fixed for structs
         Node off = con(base.offset(fidx)).keep();
         Node load = new LoadNode(name, f._alias, tf, memAlias(f._alias), objPtr, off);
@@ -439,16 +439,16 @@ private Node compileSetFieldExpr(AST.SetFieldExpr setFieldExpr) {
         else
             objPtr = compileExpr(setFieldExpr.object);
         // Sanity check expr for being a reference
-        if( !(objPtr._type instanceof SONTypeMemPtr ptr) ) {
+        if( !(objPtr._type instanceof TypeMemPtr ptr) ) {
             throw new CompilerException("Unexpected type " + objPtr._type.str());
         }
         Node val = compileExpr(setFieldExpr.value).keep();
         String name = setFieldExpr.fieldName;
-        SONTypeStruct base = ptr._obj;
+        TypeStruct base = ptr._obj;
         int fidx = base.find(name);
         if( fidx == -1 ) throw error("Accessing unknown field '" + name + "' from '" + ptr.str() + "'");
         Field f = base._fields[fidx];  // Field from field index
-        SONType tf = f._type;
+        Type tf = f._type;
         Node mem = memAlias(f._alias);
         Node st = new StoreNode(f._fname,f._alias,tf,mem,objPtr,con(base.offset(fidx)),val.unkeep(),true).peephole();
         memAlias(f._alias,st);
@@ -458,17 +458,17 @@ private Node compileSetFieldExpr(AST.SetFieldExpr setFieldExpr) {
     private Node compileArrayIndexExpr(AST.ArrayLoadExpr arrayLoadExpr) {
         Node objPtr = compileExpr(arrayLoadExpr.array).keep();
         // Sanity check expr for being a reference
-        if( !(objPtr._type instanceof SONTypeMemPtr ptr) ) {
+        if( !(objPtr._type instanceof TypeMemPtr ptr) ) {
             throw new CompilerException("Unexpected type " + objPtr._type.str());
         }
         String name = "[]";
-        SONTypeStruct base = ptr._obj;
+        TypeStruct base = ptr._obj;
         Node index = compileExpr(arrayLoadExpr.expr).keep();
         Node off = peep(new AddNode(con(base.aryBase()),peep(new ShlNode(index.unkeep(),con(base.aryScale()))))).keep();
         int fidx = base.find(name);
         if( fidx == -1 ) throw error("Accessing unknown field '" + name + "' from '" + ptr.str() + "'");
         Field f = base._fields[fidx];  // Field from field index
-        SONType tf = f._type;
+        Type tf = f._type;
         Node load = new LoadNode(name, f._alias, tf, memAlias(f._alias), objPtr, off);
         // Arrays include control, as a proxy for a safety range check
         // Structs don't need this; they only need a NPE check which is
@@ -487,18 +487,18 @@ private Node compileArrayStoreExpr(AST.ArrayStoreExpr arrayStoreExpr) {
         else
             objPtr = compileExpr(arrayStoreExpr.array);
         // Sanity check expr for being a reference
-        if( !(objPtr._type instanceof SONTypeMemPtr ptr) ) {
+        if( !(objPtr._type instanceof TypeMemPtr ptr) ) {
             throw new CompilerException("Unexpected type " + objPtr._type.str());
         }
         String name = "[]";
-        SONTypeStruct base = ptr._obj;
+        TypeStruct base = ptr._obj;
         Node index = compileExpr(arrayStoreExpr.expr).keep();
         Node off = peep(new AddNode(con(base.aryBase()),peep(new ShlNode(index.unkeep(),con(base.aryScale()))))).keep();
         Node val = compileExpr(arrayStoreExpr.value).keep();
         int fidx = base.find(name);
         if( fidx == -1 ) throw error("Accessing unknown field '" + name + "' from '" + ptr.str() + "'");
         Field f = base._fields[fidx];  // Field from field index
-        SONType tf = f._type;
+        Type tf = f._type;
         Node mem = memAlias(f._alias);
         Node st = new StoreNode(f._fname,f._alias,tf,mem,objPtr,off.unkeep(),val.unkeep(),true).peephole();
         memAlias(f._alias,st);
@@ -509,7 +509,7 @@ private Node compileInitExpr(AST.InitExpr initExpr) {
         Node objPtr = compileExpr(initExpr.newExpr).keep();
         ctorStack.add(objPtr);
         // Sanity check expr for being a reference
-        if( !(objPtr._type instanceof SONTypeMemPtr ptr) ) {
+        if( !(objPtr._type instanceof TypeMemPtr ptr) ) {
             throw new CompilerException("Unexpected type " + objPtr._type.str());
         }
         if (initExpr.initExprList != null && !initExpr.initExprList.isEmpty()) {
@@ -521,7 +521,7 @@ private Node compileInitExpr(AST.InitExpr initExpr) {
         return objPtr.unkeep();
     }
 
-    private Node newArray(SONTypeStruct ary, Node len) {
+    private Node newArray(TypeStruct ary, Node len) {
         int base = ary.aryBase ();
         int scale= ary.aryScale();
         Node size = peep(new AddNode(con(base),peep(new ShlNode(len.keep(),con(scale)))));
@@ -531,7 +531,7 @@ private Node newArray(SONTypeStruct ary, Node len) {
      * Return a NewNode initialized memory.
      * @param obj is the declared type, with GLB fields
      */
-    private Node newStruct( SONTypeStruct obj, Node size) {
+    private Node newStruct(TypeStruct obj, Node size) {
         Field[] fs = obj._fields;
         if( fs==null )
             throw error("Unknown struct type '" + obj._name + "'");
@@ -544,7 +544,7 @@ private Node newStruct( SONTypeStruct obj, Node size) {
         for( int i = 0; i < len; i++ )
             ns[2+i] = memAlias(fs[i]._alias);
         // FIXME make
-        Node nnn = new NewNode(SONTypeMemPtr.make(obj), ns).peephole().keep();
+        Node nnn = new NewNode(TypeMemPtr.make(obj), ns).peephole().keep();
         for( int i = 0; i < len; i++ )
             memAlias(fs[i]._alias, new ProjNode(nnn,i+2,memName(fs[i]._alias)).peephole());
         return new ProjNode(nnn.unkeep(),1,obj._name).peephole();
@@ -553,11 +553,11 @@ private Node newStruct( SONTypeStruct obj, Node size) {
     private Node compileNewExpr(AST.NewExpr newExpr) {
         EZType type = newExpr.type;
         if (type instanceof EZType.EZTypeArray typeArray) {
-            SONTypeMemPtr tarray = (SONTypeMemPtr) TYPES.get(typeArray.name());
+            TypeMemPtr tarray = (TypeMemPtr) TYPES.get(typeArray.name());
             return newArray(tarray._obj,newExpr.len==null?ZERO:compileExpr(newExpr.len));
         }
         else if (type instanceof EZType.EZTypeStruct typeStruct) {
-            SONTypeMemPtr tptr = (SONTypeMemPtr) TYPES.get(typeStruct.name());
+            TypeMemPtr tptr = (TypeMemPtr) TYPES.get(typeStruct.name());
             return newStruct(tptr._obj,con(tptr._obj.offset(tptr._obj._fields.length)));
         }
         else
@@ -653,9 +653,9 @@ private Node compileSymbolExpr(AST.NameExpr symbolExpr) {
      */
     private Node compileCallExpr(AST.CallExpr callExpr) {
         Node expr = compileExpr(callExpr.callee);
-        if( expr._type == SONType.NIL )
+        if( expr._type == Type.NIL )
             throw error("Calling a null function pointer");
-        if( !(expr instanceof FRefNode) && !expr._type.isa(SONTypeFunPtr.BOT) )
+        if( !(expr instanceof FRefNode) && !expr._type.isa(TypeFunPtr.BOT) )
             throw error("Expected a function but got "+expr._type.glb().str());
         expr.keep();            // Keep while parsing args
 
@@ -674,11 +674,11 @@ private Node compileCallExpr(AST.CallExpr callExpr) {
         for( Node arg : args )
             arg.unkeep();
         // Dead into the call?  Skip all the node gen
-        if( ctrl()._type == SONType.XCONTROL ) {
+        if( ctrl()._type == Type.XCONTROL ) {
             for( Node arg : args )
                 if( arg.isUnused() )
                     arg.kill();
-            return con(SONType.TOP);
+            return con(Type.TOP);
         }
 
         // Into the call
@@ -962,8 +962,8 @@ private Node compileBlock(AST.BlockStmt block) {
         return last;
     }
 
-    public static Node con(long con ) { return con==0 ? ZERO : con(SONTypeInteger.constant(con));  }
-    public static ConstantNode con( SONType t ) { return (ConstantNode)new ConstantNode(t).peephole();  }
+    public static Node con(long con ) { return con==0 ? ZERO : con(TypeInteger.constant(con));  }
+    public static ConstantNode con( Type t ) { return (ConstantNode)new ConstantNode(t).peephole();  }
     public Node peep( Node n ) {
         // Peephole, then improve with lexically scoped guards
         return _scope.upcastGuard(n.peephole());
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/Var.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/Var.java
index 6016939..29327fa 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/Var.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/Var.java
@@ -10,36 +10,36 @@ public class Var {
 
     public final String _name;   // Declared name
     public int _idx;             // index in containing scope
-    private SONType _type;          // Declared type
+    private Type _type;          // Declared type
     public boolean _final;       // Final field
     public boolean _fref;        // Forward ref
 
-    public Var(int idx, String name, SONType type, boolean xfinal) {
+    public Var(int idx, String name, Type type, boolean xfinal) {
         this(idx,name,type,xfinal,false);
     }
-    public Var(int idx, String name, SONType type, boolean xfinal, boolean fref) {
+    public Var(int idx, String name, Type type, boolean xfinal, boolean fref) {
         _idx = idx;
         _name = name;
         _type = type;
         _final = xfinal;
         _fref = fref;
     }
-    public SONType type() {
+    public Type type() {
         if( !_type.isFRef() ) return _type;
         // Update self to no longer use the forward ref type
-        SONType def = Compiler.TYPES.get(((SONTypeMemPtr)_type)._obj._name);
+        Type def = Compiler.TYPES.get(((TypeMemPtr)_type)._obj._name);
         return (_type=_type.meet(def));
     }
-    public SONType lazyGLB() {
-        SONType t = type();
-        return t instanceof SONTypeMemPtr ? t : t.glb();
+    public Type lazyGLB() {
+        Type t = type();
+        return t instanceof TypeMemPtr ? t : t.glb();
     }
 
     // Forward reference variables (not types) must be BOTTOM and
     // distinct from inferred variables
     public boolean isFRef() { return _fref; }
 
-    public void defFRef(SONType type, boolean xfinal) {
+    public void defFRef(Type type, boolean xfinal) {
         assert isFRef() && xfinal;
         _type = type;
         _final = true;
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/BuildLRG.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/BuildLRG.java
index 1b4f17b..195506b 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/BuildLRG.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/BuildLRG.java
@@ -1,8 +1,7 @@
 package com.compilerprogramming.ezlang.compiler.codegen;
 
-import com.compilerprogramming.ezlang.compiler.Utils;
 import com.compilerprogramming.ezlang.compiler.nodes.*;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeMem;
+import com.compilerprogramming.ezlang.compiler.sontypes.TypeMem;
 
 abstract public class BuildLRG {
     // Compute live ranges in a single forwards pass.  Every def is a new live
@@ -17,7 +16,7 @@ abstract public class BuildLRG {
     public static boolean run(int round, RegAlloc alloc) {
         for( Node bb : alloc._code._cfg )
             for( Node n : bb.outs() ) {
-                if( n instanceof PhiNode phi && !(phi._type instanceof SONTypeMem) ) {
+                if( n instanceof PhiNode phi && !(phi._type instanceof TypeMem) ) {
                     // All Phi inputs end up with the same LRG.
                     // Pass 1: find any pre-existing LRG, to avoid make-then-Union a LRG
                     LRG lrg = alloc.lrg(phi);
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/CodeGen.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/CodeGen.java
index fa890bd..b5666e6 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/CodeGen.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/CodeGen.java
@@ -36,11 +36,11 @@ public enum Phase {
     // Compilation source code
     public final String _src;
     // Compile-time known initial argument type
-    public final SONTypeInteger _arg;
+    public final TypeInteger _arg;
 
     // ---------------------------
-    public CodeGen( String src ) { this(src, SONTypeInteger.BOT, 123L ); }
-    public CodeGen( String src, SONTypeInteger arg, long workListSeed ) {
+    public CodeGen( String src ) { this(src, TypeInteger.BOT, 123L ); }
+    public CodeGen(String src, TypeInteger arg, long workListSeed ) {
         CODE = this;
         _phase = null;
         _callingConv = null;
@@ -122,39 +122,39 @@ public int iDepthFrom(int idepth) {
 
     // Compute "function indices": FIDX.
     // Each new request at the same signature gets a new FIDX.
-    private final HashMap<SONTypeTuple,Integer> FIDXS = new HashMap<>();
-    public SONTypeFunPtr makeFun( SONTypeTuple sig, SONType ret ) {
+    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 SONTypeFunPtr.make((byte)2,sig,ret, 1L<<fidx );
+        return TypeFunPtr.make((byte)2,sig,ret, 1L<<fidx );
     }
-    public SONTypeFunPtr makeFun2( SONTypeTuple sig, SONType ret ) {
+    public TypeFunPtr makeFun2(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 new SONTypeFunPtr((byte)2,sig,ret, 1L<<fidx );
+        return new TypeFunPtr((byte)2,sig,ret, 1L<<fidx );
     }
     // Signature for MAIN
-    public SONTypeFunPtr _main = makeFun(SONTypeTuple.MAIN,SONType.BOTTOM);
+    public TypeFunPtr _main = makeFun(TypeTuple.MAIN, Type.BOTTOM);
     // Reverse from a constant function pointer to the IR function being called
-    public FunNode link( SONTypeFunPtr tfp ) {
+    public FunNode link( TypeFunPtr tfp ) {
         assert tfp.isConstant();
-        return _linker.get(tfp.makeFrom(SONType.BOTTOM));
+        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(SONType.BOTTOM),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<SONTypeFunPtr,FunNode> _linker = new HashMap<>();
+    private final HashMap<TypeFunPtr,FunNode> _linker = new HashMap<>();
 
     // Parser object
     public final Compiler P;
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/ElfFile.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/ElfFile.java
index 490b921..54bd967 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/ElfFile.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/ElfFile.java
@@ -9,7 +9,6 @@
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 import java.util.HashMap;
-import java.util.Map;
 
 public class ElfFile {
 
@@ -213,7 +212,7 @@ private void pushSection(Section s) {
     }
 
     /* creates function and stores where it starts*/
-    private final HashMap<SONTypeFunPtr,Symbol> _funcs = new HashMap<>();
+    private final HashMap<TypeFunPtr,Symbol> _funcs = new HashMap<>();
     private void encodeFunctions(SymbolSection symbols, DataSection text) {
         for( int i=0; i<_code._cfg._len; i++ ) {
             if( !(_code._cfg.at(i) instanceof FunNode fun) ) continue;
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/Encoding.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/Encoding.java
index 1fc124e..b37e8fe 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/Encoding.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/Encoding.java
@@ -1,7 +1,6 @@
 package com.compilerprogramming.ezlang.compiler.codegen;
 
 import com.compilerprogramming.ezlang.compiler.Ary;
-import com.compilerprogramming.ezlang.compiler.SB;
 import com.compilerprogramming.ezlang.compiler.Utils;
 import com.compilerprogramming.ezlang.compiler.nodes.*;
 import com.compilerprogramming.ezlang.compiler.sontypes.*;
@@ -47,12 +46,12 @@ public static class BAOS extends ByteArrayOutputStream {
     // Big Constant relocation info.
     public static class Relo {
         public final Node _op;
-        public final SONType _t;          // Constant type
+        public final Type _t;          // Constant type
         public final byte _off;        // Offset from start of opcode
         public final byte _elf;        // ELF relocation type, e.g. 2/PC32
         public int _target;      // Where constant is finally placede
         public int _opStart;     // Opcode start
-        Relo( Node op, SONType t, byte off, byte elf ) {
+        Relo(Node op, Type t, byte off, byte elf ) {
             _op=op;  _t=t;  _off=off; _elf=elf;
         }
     }
@@ -99,12 +98,12 @@ static void padN(int n, BAOS bits) {
     }
 
     // Convenience for writing log-N
-    static void addN( int log, SONType t, BAOS bits ) {
-        long x = t instanceof SONTypeInteger ti
+    static void addN(int log, Type t, BAOS bits ) {
+        long x = t instanceof TypeInteger ti
             ? ti.value()
             : log==3
-            ? Double.doubleToRawLongBits(    ((SONTypeFloat)t).value())
-            : Float.floatToRawIntBits((float)((SONTypeFloat)t).value());
+            ? Double.doubleToRawLongBits(    ((TypeFloat)t).value())
+            : Float.floatToRawIntBits((float)((TypeFloat)t).value());
         addN(log,x,bits);
     }
     static void addN( int log, long x, BAOS bits ) {
@@ -129,7 +128,7 @@ public Encoding relo( CallNode call ) {
         return this;
     }
     public Encoding relo( ConstantNode con ) {
-        SONTypeFunPtr tfp = (SONTypeFunPtr)con._con;
+        TypeFunPtr tfp = (TypeFunPtr)con._con;
         _internals.put(con,_code.link(tfp));
         return this;
     }
@@ -146,7 +145,7 @@ public Encoding external( Node call, String extern ) {
 
     // Store t as a 32/64 bit constant in the code space; generate RIP-relative
     // addressing to load it
-    public void largeConstant( Node relo, SONType t, int off, int elf ) {
+    public void largeConstant(Node relo, Type t, int off, int elf ) {
         assert t.isConstant();
         assert (byte)off == off;
         assert (byte)elf == elf;
@@ -423,7 +422,7 @@ void patchLocalRelocations() {
     void writeConstantPool( BAOS bits, boolean patch ) {
         padN(16,bits);
 
-        HashMap<SONType,Integer> targets = new HashMap<>();
+        HashMap<Type,Integer> targets = new HashMap<>();
 
         // By log size
         for( int log = 3; log >= 0; log-- ) {
@@ -436,8 +435,8 @@ void writeConstantPool( BAOS bits, boolean patch ) {
                     if( target==null ) {
                         targets.put(relo._t,target = bits.size());
                         // Put constant into code space.
-                        if( relo._t instanceof SONTypeTuple tt ) // Constant tuples put all entries
-                            for( SONType tx : tt._types )
+                        if( relo._t instanceof TypeTuple tt ) // Constant tuples put all entries
+                            for( Type tx : tt._types )
                                 addN(log,tx,bits);
                         else
                             addN(log,relo._t,bits);
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/GlobalCodeMotion.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/GlobalCodeMotion.java
index 627e7e0..1611468 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/GlobalCodeMotion.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/GlobalCodeMotion.java
@@ -166,11 +166,11 @@ private static void breadth(Node stop, Node[] ns, CFGNode[] late) {
                             // New makes new memory, never crushes load memory
                             !(memuse instanceof NewNode) &&
                             // Load-use directly defines memory
-                            (memuse._type instanceof SONTypeMem ||
+                            (memuse._type instanceof TypeMem ||
                              // Load-use directly defines memory
                              memuse instanceof CallNode ||
                              // Load-use indirectly defines memory
-                             (memuse._type instanceof SONTypeTuple tt && tt._types[ld._alias] instanceof SONTypeMem)) )
+                             (memuse._type instanceof TypeTuple tt && tt._types[ld._alias] instanceof TypeMem)) )
                             continue outer;
 
                 // All uses done, schedule
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/Machine.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/Machine.java
index 4115e57..3a9d9e9 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/Machine.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/Machine.java
@@ -1,7 +1,7 @@
 package com.compilerprogramming.ezlang.compiler.codegen;
 
 import com.compilerprogramming.ezlang.compiler.nodes.*;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFunPtr;
+import com.compilerprogramming.ezlang.compiler.sontypes.TypeFunPtr;
 
 abstract public class Machine {
     // Human readable machine name.  Something like "x86-64" or "arm" or "risc5"
@@ -21,9 +21,9 @@ abstract public class Machine {
     // for the function itself, or for *outgoing* calls, the maximum stack slot
     // given to the incoming function arguments (stack slots reserved for
     // incoming arguments).
-    public abstract RegMask callArgMask(SONTypeFunPtr tfp, int arg, int maxArgSlot);
+    public abstract RegMask callArgMask(TypeFunPtr tfp, int arg, int maxArgSlot);
     // Return register mask, based on signature (GPR vs FPR)
-    public abstract RegMask retMask(SONTypeFunPtr tfp);
+    public abstract RegMask retMask(TypeFunPtr tfp);
     // Return PC register
     public abstract int rpc();
     // Return a MachNode unconditional branch
@@ -92,6 +92,6 @@ abstract public class Machine {
     // Maximum stack slot (or 0) for the args in this TFP.  This will include
     // shadow slots if defined in the ABI, even if all arguments are passed in
     // registers.
-    public abstract short maxArgSlot(SONTypeFunPtr tfp);
+    public abstract short maxArgSlot(TypeFunPtr tfp);
 
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/AddFNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/AddFNode.java
index d8ecc97..b7ba969 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/AddFNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/AddFNode.java
@@ -1,7 +1,7 @@
 package com.compilerprogramming.ezlang.compiler.nodes;
 
-import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFloat;
+import com.compilerprogramming.ezlang.compiler.sontypes.Type;
+import com.compilerprogramming.ezlang.compiler.sontypes.TypeFloat;
 
 import java.util.BitSet;
 
@@ -20,11 +20,11 @@ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
     }
 
     @Override
-    public SONType compute() {
-        if (in(1)._type instanceof SONTypeFloat i0 &&
-            in(2)._type instanceof SONTypeFloat i1) {
+    public Type compute() {
+        if (in(1)._type instanceof TypeFloat i0 &&
+            in(2)._type instanceof TypeFloat i1) {
             if (i0.isConstant() && i1.isConstant())
-                return SONTypeFloat.constant(i0.value()+i1.value());
+                return TypeFloat.constant(i0.value()+i1.value());
         }
         return in(1)._type.meet(in(2)._type);
     }
@@ -32,10 +32,10 @@ public SONType compute() {
     @Override
     public Node idealize() {
         Node lhs = in(1);
-        SONType t2 = in(2)._type;
+        Type t2 = in(2)._type;
 
         // Add of 0.
-        if ( t2.isConstant() && t2 instanceof SONTypeFloat i && i.value()==0 )
+        if ( t2.isConstant() && t2 instanceof TypeFloat i && i.value()==0 )
             return lhs;
 
         return null;
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/AddNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/AddNode.java
index 060fba6..1b8af35 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/AddNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/AddNode.java
@@ -1,6 +1,5 @@
 package com.compilerprogramming.ezlang.compiler.nodes;
 
-import com.compilerprogramming.ezlang.compiler.Compiler;
 import com.compilerprogramming.ezlang.compiler.sontypes.*;
 
 import java.util.BitSet;
@@ -23,20 +22,20 @@ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
 
 
     @Override
-    public SONType compute() {
-        SONType t1 = in(1)._type, t2 = in(2)._type;
+    public Type compute() {
+        Type t1 = in(1)._type, t2 = in(2)._type;
         if( t1.isHigh() || t2.isHigh() )
-            return SONTypeInteger.TOP;
-        if( t1 instanceof SONTypeInteger i1 &&
-            t2 instanceof SONTypeInteger i2 ) {
+            return TypeInteger.TOP;
+        if( t1 instanceof TypeInteger i1 &&
+            t2 instanceof TypeInteger i2 ) {
             if (i1.isConstant() && i2.isConstant())
-                return SONTypeInteger.constant(i1.value()+i2.value());
+                return TypeInteger.constant(i1.value()+i2.value());
             // Fold ranges like {0-1} + {2-3} into {2-4}.
             if( !overflow(i1._min,i2._min) &&
                 !overflow(i1._max,i2._max) )
-                return SONTypeInteger.make(i1._min+i2._min,i1._max+i2._max);
+                return TypeInteger.make(i1._min+i2._min,i1._max+i2._max);
         }
-        return SONTypeInteger.BOT;
+        return TypeInteger.BOT;
     }
 
     static boolean overflow( long x, long y ) {
@@ -49,11 +48,11 @@ public Node idealize () {
         Node rhs = in(2);
         if( rhs instanceof AddNode add && add.err()!=null )
             return null;
-        SONType t2 = rhs._type;
+        Type t2 = rhs._type;
 
         // Add of 0.  We do not check for (0+x) because this will already
         // canonicalize to (x+0)
-        if( t2 == SONTypeInteger.ZERO )
+        if( t2 == TypeInteger.ZERO )
             return lhs;
 
         // Add of same to a multiply by 2
@@ -122,7 +121,7 @@ public Node idealize () {
     static Node phiCon(Node op, boolean rotate) {
         Node lhs = op.in(1);
         Node rhs = op.in(2);
-        if( rhs._type== SONTypeInteger.TOP ) return null;
+        if( rhs._type== TypeInteger.TOP ) return null;
         // LHS is either a Phi of constants, or another op with Phi of constants
         PhiNode lphi = pcon(lhs,op);
         if( rotate && lphi==null && lhs.nIns() > 2 ) {
@@ -175,8 +174,8 @@ static boolean spine_cmp( Node hi, Node lo, Node dep ) {
         if( lo._type.isConstant() ) return false;
         if( hi._type.isConstant() ) return true ;
 
-        if( lo instanceof PhiNode lphi && lphi.region()._type== SONType.XCONTROL ) return false;
-        if( hi instanceof PhiNode hphi && hphi.region()._type== SONType.XCONTROL ) return false;
+        if( lo instanceof PhiNode lphi && lphi.region()._type== Type.XCONTROL ) return false;
+        if( hi instanceof PhiNode hphi && hphi.region()._type== Type.XCONTROL ) return false;
 
         if( lo instanceof PhiNode && lo.allCons(dep) ) return false;
         if( hi instanceof PhiNode && hi.allCons(dep) ) return true ;
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/AndNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/AndNode.java
index f36997f..fb35217 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/AndNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/AndNode.java
@@ -1,7 +1,7 @@
 package com.compilerprogramming.ezlang.compiler.nodes;
 
-import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeInteger;
+import com.compilerprogramming.ezlang.compiler.sontypes.Type;
+import com.compilerprogramming.ezlang.compiler.sontypes.TypeInteger;
 
 public class AndNode extends LogicalNode {
     public AndNode(Node lhs, Node rhs) { super(lhs, rhs); }
@@ -12,28 +12,28 @@ public class AndNode extends LogicalNode {
     @Override public String glabel() { return "&"; }
 
     @Override
-    public SONType compute() {
-        SONType t1 = in(1)._type, t2 = in(2)._type;
+    public Type compute() {
+        Type t1 = in(1)._type, t2 = in(2)._type;
         if( t1.isHigh() || t2.isHigh() )
-            return SONTypeInteger.TOP;
-        if( t1 instanceof SONTypeInteger i0 &&
-            t2 instanceof SONTypeInteger i1 ) {
+            return TypeInteger.TOP;
+        if( t1 instanceof TypeInteger i0 &&
+            t2 instanceof TypeInteger i1 ) {
             if( i0.isConstant() && i1.isConstant() )
-                return SONTypeInteger.constant(i0.value()&i1.value());
+                return TypeInteger.constant(i0.value()&i1.value());
             // Sharpen allowed bits if either value is narrowed
             long mask = i0.mask() & i1.mask();
-            return mask < 0 ? SONTypeInteger.BOT : SONTypeInteger.make(0,mask);
+            return mask < 0 ? TypeInteger.BOT : TypeInteger.make(0,mask);
         }
-        return SONTypeInteger.BOT;
+        return TypeInteger.BOT;
     }
 
     @Override
     public Node idealize() {
         Node lhs = in(1);
         Node rhs = in(2);
-        SONType t1 = lhs._type;
-        SONType t2 = rhs._type;
-        if( !(t1 instanceof SONTypeInteger && t2 instanceof SONTypeInteger t2i) )
+        Type t1 = lhs._type;
+        Type t2 = rhs._type;
+        if( !(t1 instanceof TypeInteger && t2 instanceof TypeInteger t2i) )
             return null; // Malformed, e.g. (17 & 3.14)
 
         // And of -1.  We do not check for (-1&x) because this will already
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/BoolNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/BoolNode.java
index 8863de8..3248345 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/BoolNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/BoolNode.java
@@ -4,7 +4,7 @@
 import com.compilerprogramming.ezlang.compiler.Utils;
 import com.compilerprogramming.ezlang.compiler.sontypes.*;
 import java.util.BitSet;
-import static com.compilerprogramming.ezlang.compiler.sontypes.SONTypeInteger.*;
+import static com.compilerprogramming.ezlang.compiler.sontypes.TypeInteger.*;
 
 abstract public class BoolNode extends Node {
 
@@ -28,26 +28,26 @@ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
     }
 
     @Override
-    public SONTypeInteger compute() {
-        SONType t1 = in(1)._type;
-        SONType t2 = in(2)._type;
+    public TypeInteger compute() {
+        Type t1 = in(1)._type;
+        Type t2 = in(2)._type;
         // Exactly equals?
         if( t1.isHigh() || t2.isHigh() )
             return BOOL.dual();
         if( in(1)==in(2) )
             // LT fails, both EQ and LE succeed
             return this instanceof LT ? FALSE : TRUE;
-        if( t1 instanceof SONTypeInteger i1 &&
-            t2 instanceof SONTypeInteger i2 )
+        if( t1 instanceof TypeInteger i1 &&
+            t2 instanceof TypeInteger i2 )
             return doOp(i1,i2);
-        if( t1 instanceof SONTypeFloat f1 &&
-            t2 instanceof SONTypeFloat f2 &&
+        if( t1 instanceof TypeFloat f1 &&
+            t2 instanceof TypeFloat f2 &&
             f1.isConstant() && f2.isConstant() )
             return doOp(f1.value(), f2.value()) ? TRUE : FALSE;
         return BOOL;
     }
 
-    SONTypeInteger doOp(SONTypeInteger t1, SONTypeInteger t2) { throw Utils.TODO(); }
+    TypeInteger doOp(TypeInteger t1, TypeInteger t2) { throw Utils.TODO(); }
     boolean doOp(double lhs, double rhs) { throw Utils.TODO(); }
     Node copyF(Node lhs, Node rhs) { return null; }
     public boolean isFloat() { return false; }
@@ -64,10 +64,10 @@ public Node idealize() {
                 // con==noncon becomes noncon==con
                 if( in(1) instanceof ConstantNode || in(1)._nid > in(2)._nid )
                 // Equals sorts by NID otherwise: non.high == non.low becomes non.low == non.high
-                    return in(1)._type instanceof SONTypeFloat ? new EQF(in(2),in(1)) : new EQ(in(2),in(1));
+                    return in(1)._type instanceof TypeFloat ? new EQF(in(2),in(1)) : new EQ(in(2),in(1));
             }
             // Equals X==0 becomes a !X
-            if( (in(2)._type == ZERO || in(2)._type == SONType.NIL) )
+            if( (in(2)._type == ZERO || in(2)._type == Type.NIL) )
                 return new NotNode(in(1));
         }
 
@@ -83,7 +83,7 @@ public Node idealize() {
     public static class EQ extends BoolNode {
         public EQ(Node lhs, Node rhs) { super(lhs,rhs); }
         public String op() { return "=="; }
-        SONTypeInteger doOp(SONTypeInteger i1, SONTypeInteger i2) {
+        TypeInteger doOp(TypeInteger i1, TypeInteger i2) {
             if( i1==i2 && i1.isConstant() ) return TRUE;
             if( i1._max < i2._min || i1._min > i2._max ) return FALSE;
             return BOOL;
@@ -95,7 +95,7 @@ SONTypeInteger doOp(SONTypeInteger i1, SONTypeInteger i2) {
     public static class NE extends BoolNode {
         public NE(Node lhs, Node rhs) { super(lhs,rhs); }
         public String op() { return "!="; }
-        SONTypeInteger doOp(SONTypeInteger i1, SONTypeInteger i2) {
+        TypeInteger doOp(TypeInteger i1, TypeInteger i2) {
             if( i1==i2 && i1.isConstant() ) return TRUE;
             if( i1._max < i2._min || i1._min > i2._max ) return FALSE;
             return BOOL;
@@ -108,7 +108,7 @@ public static class LT extends BoolNode {
         public LT(Node lhs, Node rhs) { super(lhs,rhs); }
         public String op() { return "<" ; }
         public String glabel() { return "&lt;"; }
-        SONTypeInteger doOp(SONTypeInteger i1, SONTypeInteger i2) {
+        TypeInteger doOp(TypeInteger i1, TypeInteger i2) {
             if( i1._max <  i2._min ) return TRUE;
             if( i1._min >= i2._max ) return FALSE;
             return BOOL;
@@ -120,7 +120,7 @@ public static class LE extends BoolNode {
         public LE(Node lhs, Node rhs) { super(lhs,rhs); }
         public String op() { return "<="; }
         public String glabel() { return "&lt;="; }
-        SONTypeInteger doOp(SONTypeInteger i1, SONTypeInteger i2) {
+        TypeInteger doOp(TypeInteger i1, TypeInteger i2) {
             if( i1._max <= i2._min ) return TRUE;
             if( i1._min >  i2._max ) return FALSE;
             return BOOL;
@@ -138,7 +138,7 @@ public static class ULT extends BoolNode {
         public ULT(Node lhs, Node rhs) { super(lhs,rhs); }
         public String op() { return "u<" ; }
         public String glabel() { return "u&lt;"; }
-        SONTypeInteger doOp(SONTypeInteger i1, SONTypeInteger i2) {
+        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;
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CFGNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CFGNode.java
index cda5c80..afd1622 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CFGNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CFGNode.java
@@ -140,8 +140,8 @@ int _bltWalk( int pre, FunNode fun, StopNode stop, BitSet post ) {
         for( Node use : _outputs ) {
             if( !(use instanceof CFGNode usecfg) ) continue;
             if( skip(usecfg) ) continue;
-            if( usecfg._type == SONType.XCONTROL ||       // Do not walk dead control
-                usecfg._type == SONTypeTuple.IF_NEITHER ) // Nor dead IFs
+            if( usecfg._type == Type.XCONTROL ||       // Do not walk dead control
+                usecfg._type == TypeTuple.IF_NEITHER ) // Nor dead IFs
                 continue;
             // Child visited but not post-visited?
             if( !post.get(usecfg._nid) ) {
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CProjNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CProjNode.java
index 9c18492..1aec013 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CProjNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CProjNode.java
@@ -1,8 +1,8 @@
 package com.compilerprogramming.ezlang.compiler.nodes;
 
 import com.compilerprogramming.ezlang.compiler.Compiler;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeTuple;
+import com.compilerprogramming.ezlang.compiler.sontypes.Type;
+import com.compilerprogramming.ezlang.compiler.sontypes.TypeTuple;
 import java.util.BitSet;
 
 public class CProjNode extends CFGNode {
@@ -29,17 +29,17 @@ public CProjNode(Node ctrl, int idx, String label) {
     public CFGNode ctrl() { return cfg(0); }
 
     @Override
-    public SONType compute() {
-        SONType t = ctrl()._type;
-        return t instanceof SONTypeTuple tt ? tt._types[_idx] : SONType.BOTTOM;
+    public Type compute() {
+        Type t = ctrl()._type;
+        return t instanceof TypeTuple tt ? tt._types[_idx] : Type.BOTTOM;
     }
 
     @Override
     public Node idealize() {
-        if( ctrl()._type instanceof SONTypeTuple tt ) {
-            if( tt._types[_idx]== SONType.XCONTROL )
+        if( ctrl()._type instanceof TypeTuple tt ) {
+            if( tt._types[_idx]== Type.XCONTROL )
                 return Compiler.XCTRL; // We are dead
-            if( ctrl() instanceof IfNode && tt._types[1-_idx]== SONType.XCONTROL ) // Only true for IfNodes
+            if( ctrl() instanceof IfNode && tt._types[1-_idx]== Type.XCONTROL ) // Only true for IfNodes
                 return ctrl().in(0);               // We become our input control
         }
 
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CallEndNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CallEndNode.java
index f2db2a9..79a1c03 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CallEndNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CallEndNode.java
@@ -16,9 +16,9 @@ public class CallEndNode extends CFGNode implements MultiNode {
 
     // When set true, this Call/CallEnd/Fun/Return is being trivially inlined
     private boolean _folding;
-    public final SONTypeRPC _rpc;
+    public final TypeRPC _rpc;
 
-    public CallEndNode(CallNode call) { super(new Node[]{call}); _rpc = SONTypeRPC.constant(_nid); }
+    public CallEndNode(CallNode call) { super(new Node[]{call}); _rpc = TypeRPC.constant(_nid); }
     public CallEndNode(CallEndNode cend) { super(cend); _rpc = cend._rpc; }
 
     @Override public String label() { return "CallEnd"; }
@@ -37,21 +37,21 @@ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
     }
 
     @Override
-    public SONType compute() {
+    public Type compute() {
         if( !(in(0) instanceof CallNode call) )
-            return SONTypeTuple.RET.dual();
-        SONType ret = SONType.BOTTOM;
-        SONTypeMem mem = SONTypeMem.BOT;
-        if( addDep(call.fptr())._type instanceof SONTypeFunPtr tfp ) {
+            return TypeTuple.RET.dual();
+        Type ret = Type.BOTTOM;
+        TypeMem mem = TypeMem.BOT;
+        if( addDep(call.fptr())._type instanceof TypeFunPtr tfp ) {
             ret = tfp.ret();
             // Here, if I can figure out I've found *all* callers, then I can meet
             // across the linked returns and join with the function return type.
             if( tfp.isConstant() && nIns()>1 ) {
                 assert nIns()==2;     // Linked exactly once for a constant
-                ret = ((SONTypeTuple)in(1)._type).ret(); // Return type
+                ret = ((TypeTuple)in(1)._type).ret(); // Return type
             }
         }
-        return SONTypeTuple.make(call._type, SONTypeMem.BOT,ret);
+        return TypeTuple.make(call._type, TypeMem.BOT,ret);
     }
 
     @Override
@@ -64,7 +64,7 @@ public Node idealize() {
             if( fptr.nOuts() == 1 && // Only user is this call
                 fptr instanceof ConstantNode && // We have an immediate call
                 // Function is being called, and its not-null
-                fptr._type instanceof SONTypeFunPtr tfp && tfp.notNull() &&
+                fptr._type instanceof TypeFunPtr tfp && tfp.notNull() &&
                 // Arguments are correct
                 call.err()==null ) {
                 ReturnNode ret = (ReturnNode)in(1);
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CallNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CallNode.java
index 5f88fd4..78c0632 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CallNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CallNode.java
@@ -1,11 +1,10 @@
 package com.compilerprogramming.ezlang.compiler.nodes;
 
 import com.compilerprogramming.ezlang.compiler.codegen.CodeGen;
-import com.compilerprogramming.ezlang.compiler.IterPeeps;
 import com.compilerprogramming.ezlang.compiler.Compiler;
 import com.compilerprogramming.ezlang.compiler.Utils;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFunPtr;
+import com.compilerprogramming.ezlang.compiler.sontypes.Type;
+import com.compilerprogramming.ezlang.compiler.sontypes.TypeFunPtr;
 import com.compilerprogramming.ezlang.exceptions.CompilerException;
 
 import java.util.BitSet;
@@ -31,7 +30,7 @@ public class CallNode extends CFGNode {
         return sb.append(")");
     }
     public String name() {
-        if( fptr()._type instanceof SONTypeFunPtr tfp && tfp.isConstant() )
+        if( fptr()._type instanceof TypeFunPtr tfp && tfp.isConstant() )
             return CodeGen.CODE.link(tfp)._name;
         return null;
     }
@@ -47,7 +46,7 @@ public String name() {
     // args from input 2 to last; last is function input
     public Node fptr() { return _inputs.last(); }
     // Error if not a TFP
-    public SONTypeFunPtr tfp() { return (SONTypeFunPtr)fptr()._type; }
+    public TypeFunPtr tfp() { return (TypeFunPtr)fptr()._type; }
 
     // Call is to an externally supplied code
     public boolean external() { return false; }
@@ -78,7 +77,7 @@ private CallEndNode _cend() {
 
 
     @Override
-    public SONType compute() {
+    public Type compute() {
         return ctrl()._type;
     }
 
@@ -91,15 +90,15 @@ public Node idealize() {
         // point to this Call, and all his Parms point to the call arguments;
         // also the CallEnd points to the Return.
         Node progress = null;
-        if( fptr()._type instanceof SONTypeFunPtr tfp && tfp.nargs() == nargs() ) {
+        if( fptr()._type instanceof TypeFunPtr tfp && tfp.nargs() == nargs() ) {
             // If fidxs is negative, then infinite unknown functions
             long fidxs = tfp.fidxs();
             if( fidxs > 0 ) {
                 // Wipe out the return which matching in the linker table
                 // Walk the (63 max) bits and link
-                for( ; fidxs!=0; fidxs = SONTypeFunPtr.nextFIDX(fidxs) ) {
+                for( ; fidxs!=0; fidxs = TypeFunPtr.nextFIDX(fidxs) ) {
                     int fidx = Long.numberOfTrailingZeros(fidxs);
-                    SONTypeFunPtr tfp0 = tfp.makeFrom(fidx);
+                    TypeFunPtr tfp0 = tfp.makeFrom(fidx);
                     FunNode fun = CodeGen.CODE.link(tfp0);
                     if( fun!=null && !fun._folding && !linked(fun) )
                         progress = link(fun);
@@ -149,7 +148,7 @@ public void unlink_all() {
 
     @Override
     public CompilerException err() {
-        if( !(fptr()._type instanceof SONTypeFunPtr tfp) )
+        if( !(fptr()._type instanceof TypeFunPtr tfp) )
             throw Utils.TODO();
         if( !tfp.notNull() )
             return Compiler.error( "Might be null calling "+tfp);
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CalleeSaveNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CalleeSaveNode.java
index a003903..bf6461b 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CalleeSaveNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CalleeSaveNode.java
@@ -1,11 +1,8 @@
 package com.compilerprogramming.ezlang.compiler.nodes;
 
-import com.compilerprogramming.ezlang.compiler.Utils;
 import com.compilerprogramming.ezlang.compiler.codegen.Encoding;
 import com.compilerprogramming.ezlang.compiler.codegen.RegMask;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
-import java.util.BitSet;
-import java.io.ByteArrayOutputStream;
+import com.compilerprogramming.ezlang.compiler.sontypes.Type;
 
 public class CalleeSaveNode extends ProjNode implements MachNode {
     final RegMask _mask;
@@ -21,6 +18,6 @@ public CalleeSaveNode(FunNode fun, int reg, String label) {
     @Override public RegMask outregmap() { return _mask; }
     @Override public void encoding( Encoding enc ) { }
 
-    @Override public SONType compute() { return SONType.BOTTOM; }
+    @Override public Type compute() { return Type.BOTTOM; }
     @Override public Node idealize() { return null; }
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CastNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CastNode.java
index 58de87f..7645f39 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CastNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CastNode.java
@@ -1,7 +1,7 @@
 package com.compilerprogramming.ezlang.compiler.nodes;
 
 import com.compilerprogramming.ezlang.compiler.Utils;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
+import com.compilerprogramming.ezlang.compiler.sontypes.Type;
 import com.compilerprogramming.ezlang.exceptions.CompilerException;
 
 import java.util.BitSet;
@@ -9,8 +9,8 @@
 // Upcast (join) the input to a t.  Used after guard test to lift an input.
 // Can also be used to make a type-assertion if ctrl is null.
 public class CastNode extends Node {
-    public SONType _t;
-    public CastNode(SONType t, Node ctrl, Node in) {
+    public Type _t;
+    public CastNode(Type t, Node ctrl, Node in) {
         super(ctrl, in);
         _t = t;
         setType(compute());
@@ -36,7 +36,7 @@ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
     }
 
     @Override
-    public SONType compute() {
+    public Type compute() {
         return in(1)._type.join(_t);
     }
 
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ConstantNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ConstantNode.java
index 4eb1798..407ab01 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ConstantNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ConstantNode.java
@@ -2,8 +2,8 @@
 
 import com.compilerprogramming.ezlang.compiler.codegen.CodeGen;
 import com.compilerprogramming.ezlang.compiler.SB;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFunPtr;
+import com.compilerprogramming.ezlang.compiler.sontypes.Type;
+import com.compilerprogramming.ezlang.compiler.sontypes.TypeFunPtr;
 import java.util.BitSet;
 
 /**
@@ -20,17 +20,17 @@
  */
 
 public class ConstantNode extends Node {
-    public final SONType _con;
-    public ConstantNode( SONType type ) {
+    public final Type _con;
+    public ConstantNode( Type type ) {
         super(new Node[]{CodeGen.CODE._start});
         _con = _type = type;
     }
-    public ConstantNode( Node con, SONType t ) { super(con);  _con = t;  }
+    public ConstantNode( Node con, Type t ) { super(con);  _con = t;  }
     public ConstantNode( ConstantNode con ) { this(con,con._type);  }
 
-    public static Node make( SONType type ) {
-        if( type== SONType. CONTROL ) return new CtrlNode();
-        if( type== SONType.XCONTROL ) return new XCtrlNode();
+    public static Node make( Type type ) {
+        if( type== Type. CONTROL ) return new CtrlNode();
+        if( type== Type.XCONTROL ) return new XCtrlNode();
         return new ConstantNode(type);
     }
 
@@ -42,7 +42,7 @@ public static Node make( SONType type ) {
 
     @Override
     public StringBuilder _print1(StringBuilder sb, BitSet visited) {
-        if( _con instanceof SONTypeFunPtr tfp && tfp.isConstant() ) {
+        if( _con instanceof TypeFunPtr tfp && tfp.isConstant() ) {
             FunNode fun = CodeGen.CODE.link(tfp);
             if( fun!=null && fun._name != null )
                 return sb.append("{ ").append(fun._name).append("}");
@@ -53,7 +53,7 @@ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
     @Override public boolean isConst() { return true; }
 
     @Override
-    public SONType compute() { return _con; }
+    public Type compute() { return _con; }
 
     @Override
     public Node idealize() { return null; }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CtrlNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CtrlNode.java
index 6450cdd..6131eef 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CtrlNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CtrlNode.java
@@ -1,7 +1,7 @@
 package com.compilerprogramming.ezlang.compiler.nodes;
 
 import com.compilerprogramming.ezlang.compiler.codegen.CodeGen;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
+import com.compilerprogramming.ezlang.compiler.sontypes.Type;
 import java.util.BitSet;
 
 public class CtrlNode extends CFGNode {
@@ -9,6 +9,6 @@ public class CtrlNode extends CFGNode {
     @Override public String label() { return "Ctrl"; }
     @Override public StringBuilder _print1(StringBuilder sb, BitSet visited) { return sb.append("Cctrl"); }
     @Override public boolean isConst() { return true; }
-    @Override  public SONType compute() { return SONType.CONTROL; }
+    @Override  public Type compute() { return Type.CONTROL; }
     @Override public Node idealize() { return null; }
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/DivFNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/DivFNode.java
index 0813fa8..bbfcccf 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/DivFNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/DivFNode.java
@@ -1,7 +1,7 @@
 package com.compilerprogramming.ezlang.compiler.nodes;
 
-import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFloat;
+import com.compilerprogramming.ezlang.compiler.sontypes.Type;
+import com.compilerprogramming.ezlang.compiler.sontypes.TypeFloat;
 
 import java.util.BitSet;
 
@@ -20,11 +20,11 @@ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
     }
 
     @Override
-    public SONType compute() {
-        if (in(1)._type instanceof SONTypeFloat i0 &&
-            in(2)._type instanceof SONTypeFloat i1) {
+    public Type compute() {
+        if (in(1)._type instanceof TypeFloat i0 &&
+            in(2)._type instanceof TypeFloat i1) {
             if (i0.isConstant() && i1.isConstant())
-                return SONTypeFloat.constant(i0.value()/i1.value());
+                return TypeFloat.constant(i0.value()/i1.value());
         }
         return in(1)._type.meet(in(2)._type);
     }
@@ -32,8 +32,8 @@ public SONType compute() {
     @Override
     public Node idealize() {
         // Div of constant
-        if( in(2)._type instanceof SONTypeFloat f && f.isConstant() )
-            return new MulFNode(in(1),new ConstantNode(SONTypeFloat.constant(1.0/f.value())).peephole());
+        if( in(2)._type instanceof TypeFloat f && f.isConstant() )
+            return new MulFNode(in(1),new ConstantNode(TypeFloat.constant(1.0/f.value())).peephole());
 
         return null;
     }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/DivNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/DivNode.java
index 9c51b6e..34d6eb2 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/DivNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/DivNode.java
@@ -1,7 +1,7 @@
 package com.compilerprogramming.ezlang.compiler.nodes;
 
-import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeInteger;
+import com.compilerprogramming.ezlang.compiler.sontypes.Type;
+import com.compilerprogramming.ezlang.compiler.sontypes.TypeInteger;
 import java.util.BitSet;
 
 public class DivNode extends Node {
@@ -19,24 +19,24 @@ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
     }
 
     @Override
-    public SONType compute() {
-        SONType t1 = in(1)._type, t2 = in(2)._type;
+    public Type compute() {
+        Type t1 = in(1)._type, t2 = in(2)._type;
         if( t1.isHigh() || t2.isHigh() )
-            return SONTypeInteger.TOP;
-        if( t1 instanceof SONTypeInteger i1 &&
-            t2 instanceof SONTypeInteger i2 ) {
+            return TypeInteger.TOP;
+        if( t1 instanceof TypeInteger i1 &&
+            t2 instanceof TypeInteger i2 ) {
             if (i1.isConstant() && i2.isConstant())
                 return i2.value() == 0
-                    ? SONTypeInteger.ZERO
-                    : SONTypeInteger.constant(i1.value()/i2.value());
+                    ? TypeInteger.ZERO
+                    : TypeInteger.constant(i1.value()/i2.value());
         }
-        return SONTypeInteger.BOT;
+        return TypeInteger.BOT;
     }
 
     @Override
     public Node idealize() {
         // Div of 1.
-        if( in(2)._type == SONTypeInteger.TRUE )
+        if( in(2)._type == TypeInteger.TRUE )
             return in(1);
         return null;
     }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/FRefNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/FRefNode.java
index 3b4a282..0b48d15 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/FRefNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/FRefNode.java
@@ -2,7 +2,7 @@
 
 import com.compilerprogramming.ezlang.compiler.Compiler;
 import com.compilerprogramming.ezlang.compiler.Var;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
+import com.compilerprogramming.ezlang.compiler.sontypes.Type;
 import com.compilerprogramming.ezlang.exceptions.CompilerException;
 
 import java.util.BitSet;
@@ -13,7 +13,7 @@
  *  peepholes to the Def.
  */
 public class FRefNode extends ConstantNode {
-    public static final SONType FREF_TYPE = SONType.BOTTOM;
+    public static final Type FREF_TYPE = Type.BOTTOM;
     public final Var _n;
     public FRefNode( Var n ) { super(FREF_TYPE); _n = n; }
 
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/FunNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/FunNode.java
index 7e12aaf..f7de008 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/FunNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/FunNode.java
@@ -4,9 +4,9 @@
 import com.compilerprogramming.ezlang.compiler.codegen.CodeGen;
 import com.compilerprogramming.ezlang.compiler.codegen.Encoding;
 import com.compilerprogramming.ezlang.compiler.codegen.RegMask;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFunPtr;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeTuple;
+import com.compilerprogramming.ezlang.compiler.sontypes.Type;
+import com.compilerprogramming.ezlang.compiler.sontypes.TypeFunPtr;
+import com.compilerprogramming.ezlang.compiler.sontypes.TypeTuple;
 
 import java.util.BitSet;
 import static com.compilerprogramming.ezlang.compiler.codegen.CodeGen.CODE;
@@ -16,19 +16,19 @@ public class FunNode extends RegionNode {
     // When set true, this Call/CallEnd/Fun/Return is being trivially inlined
     boolean _folding;
 
-    private SONTypeFunPtr _sig;    // Initial signature
+    private TypeFunPtr _sig;    // Initial signature
     private ReturnNode _ret;    // Return pointer
 
     public String _name;        // Debug name
 
-    public FunNode(SONTypeFunPtr sig, Node... nodes ) { super(nodes); _sig = sig; }
+    public FunNode(TypeFunPtr sig, Node... nodes ) { super(nodes); _sig = sig; }
     public FunNode( FunNode fun ) {
         super( fun );
         if( fun!=null ) {
             _sig = fun.sig();
             _name = fun._name;
         } else {
-            _sig = SONTypeFunPtr.BOT;
+            _sig = TypeFunPtr.BOT;
             _name = "";
         }
     }
@@ -60,8 +60,8 @@ public ParmNode rpc() {
     public ReturnNode ret() { assert _ret!=null; return _ret; }
 
     // Signature can improve over time
-    public SONTypeFunPtr sig() { return _sig; }
-    public void setSig( SONTypeFunPtr sig ) {
+    public TypeFunPtr sig() { return _sig; }
+    public void setSig( TypeFunPtr sig ) {
         assert sig.isa(_sig);
         if( _sig != sig ) {
             CODE.add(this);
@@ -70,9 +70,9 @@ public void setSig( SONTypeFunPtr sig ) {
     }
 
     @Override
-    public SONType compute() {
+    public Type compute() {
         // Only dead if no callers after SCCP
-        return SONType.CONTROL;
+        return Type.CONTROL;
     }
 
     @Override
@@ -87,7 +87,7 @@ public Node idealize() {
         }
 
         // Upgrade inferred or user-written return type to actual
-        if( _ret!=null && _ret._type instanceof SONTypeTuple tt && tt.ret() != _sig.ret() )
+        if( _ret!=null && _ret._type instanceof TypeTuple tt && tt.ret() != _sig.ret() )
 		    // FIXME Dibyendu
             //throw Utils.TODO();
             return null;
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/IfNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/IfNode.java
index 862c8b6..5cc6a2e 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/IfNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/IfNode.java
@@ -2,7 +2,6 @@
 
 import com.compilerprogramming.ezlang.compiler.Utils;
 import com.compilerprogramming.ezlang.compiler.codegen.CodeGen;
-import com.compilerprogramming.ezlang.compiler.IterPeeps;
 import com.compilerprogramming.ezlang.compiler.sontypes.*;
 import java.util.BitSet;
 
@@ -32,25 +31,25 @@ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
     @Override public CFGNode uctrl() { return null; }
 
     @Override
-    public SONType compute() {
+    public Type compute() {
         // If the If node is not reachable then neither is any following Proj
-        if (ctrl()._type != SONType.CONTROL && ctrl()._type != SONType.BOTTOM )
-            return SONTypeTuple.IF_NEITHER;
+        if (ctrl()._type != Type.CONTROL && ctrl()._type != Type.BOTTOM )
+            return TypeTuple.IF_NEITHER;
         Node pred = pred();
-        SONType t = pred._type;
+        Type t = pred._type;
         // High types mean NEITHER side is reachable.
         // Wait until the type falls to decide which way to go.
         if( t.isHigh() )
-            return SONTypeTuple.IF_NEITHER;
+            return TypeTuple.IF_NEITHER;
         // If constant is 0 then false branch is reachable
         // Else true branch is reachable
         if( t.isConstant() )
-            return (t== SONType.NIL || t== SONTypeInteger.ZERO || (t instanceof SONTypeFunPtr tfp && tfp._fidxs==0) ) ? SONTypeTuple.IF_FALSE : SONTypeTuple.IF_TRUE;
+            return (t== Type.NIL || t== TypeInteger.ZERO || (t instanceof TypeFunPtr tfp && tfp._fidxs==0) ) ? TypeTuple.IF_FALSE : TypeTuple.IF_TRUE;
         // If adding a zero makes a difference, the predicate must not have a zero/null
         if( !t.makeZero().isa(t) )
-            return SONTypeTuple.IF_TRUE;
+            return TypeTuple.IF_TRUE;
 
-        return SONTypeTuple.IF_BOTH;
+        return TypeTuple.IF_BOTH;
     }
 
     @Override
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/LoadNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/LoadNode.java
index 4607e8e..28842c2 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/LoadNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/LoadNode.java
@@ -18,7 +18,7 @@ public class LoadNode extends MemOpNode {
      * @param ptr   The ptr to the struct base from where we load a field
      * @param off   The offset inside the struct base
      */
-    public LoadNode(String name, int alias, SONType glb, Node mem, Node ptr, Node off) {
+    public LoadNode(String name, int alias, Type glb, Node mem, Node ptr, Node off) {
         super(name, alias, true, glb, mem, ptr, off);
     }
 
@@ -31,10 +31,10 @@ public LoadNode(String name, int alias, SONType glb, Node mem, Node ptr, Node of
     public StringBuilder _print1(StringBuilder sb, BitSet visited) { return sb.append(".").append(_name); }
 
     @Override
-    public SONType compute() {
-        if( mem()._type instanceof SONTypeMem mem ) {
+    public Type compute() {
+        if( mem()._type instanceof TypeMem mem ) {
             // Update declared forward ref to the actual
-            if( _declaredType.isFRef() && mem._t instanceof SONTypeMemPtr tmp && !tmp.isFRef() )
+            if( _declaredType.isFRef() && mem._t instanceof TypeMemPtr tmp && !tmp.isFRef() )
                 _declaredType = tmp;
             // No lifting if ptr might null-check
             if( err()==null )
@@ -118,7 +118,7 @@ public Node idealize() {
         //   if( pred ) ptr.x = e0;         val = pred ? e0
         //   else       ptr.x = e1;                    : e1;
         //   val = ptr.x;                   ptr.x = val;
-        if( mem() instanceof PhiNode memphi && memphi.region()._type == SONType.CONTROL && memphi.nIns()== 3 &&
+        if( mem() instanceof PhiNode memphi && memphi.region()._type == Type.CONTROL && memphi.nIns()== 3 &&
             // Offset can be hoisted
             off() instanceof ConstantNode &&
             // Pointer can be hoisted
@@ -139,8 +139,8 @@ public Node idealize() {
 
     // Load a flavored zero from a New
     private Node zero(NewNode nnn) {
-        SONTypeStruct ts = nnn._ptr._obj;
-        SONType zero = ts._fields[ts.findAlias(_alias)]._type.makeZero();
+        TypeStruct ts = nnn._ptr._obj;
+        Type zero = ts._fields[ts.findAlias(_alias)]._type.makeZero();
         return castRO(new ConstantNode(zero).peephole());
     }
 
@@ -179,7 +179,7 @@ private static boolean hoistPtr(Node ptr, PhiNode memphi ) {
     private boolean profit(PhiNode phi, int idx) {
         Node px = phi.in(idx);
         if( px==null ) return false;
-        if( px._type instanceof SONTypeMem mem && mem._t.isHighOrConst() ) return true;
+        if( px._type instanceof TypeMem mem && mem._t.isHighOrConst() ) return true;
         if( px instanceof StoreNode st1 && ptr()==st1.ptr() && off()==st1.off() ) return true;
         addDep(px);
         return false;
@@ -195,13 +195,13 @@ private Node castRO(Node rez) {
     // When a load bypasses a store, the store might truncate bits - and the
     // load will need to zero/sign-extend.
     private Node extend(Node val) {
-        if( !(_declaredType instanceof SONTypeInteger ti) ) return val;
+        if( !(_declaredType instanceof TypeInteger ti) ) return val;
         if( ti._min==0 )        // Unsigned
             return new AndNode(val,con(ti._max));
         // Signed extension
         int shift = Long.numberOfLeadingZeros(ti._max)-1;
         Node shf = con(shift);
-        if( shf._type== SONTypeInteger.ZERO )
+        if( shf._type== TypeInteger.ZERO )
             return val;
         Node shl = new ShlNode(val,shf.keep()).peephole();
         return new SarNode(shl,shf.unkeep());
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/LogicalNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/LogicalNode.java
index 1b93813..e7c3450 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/LogicalNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/LogicalNode.java
@@ -1,7 +1,7 @@
 package com.compilerprogramming.ezlang.compiler.nodes;
 
 import com.compilerprogramming.ezlang.compiler.Compiler;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeInteger;
+import com.compilerprogramming.ezlang.compiler.sontypes.TypeInteger;
 import com.compilerprogramming.ezlang.exceptions.CompilerException;
 
 import java.util.BitSet;
@@ -21,8 +21,8 @@ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
 
     @Override public CompilerException err() {
         if( in(1)._type.isHigh() || in(2)._type.isHigh() ) return null;
-        if( !(in(1)._type instanceof SONTypeInteger) ) return Compiler.error("Cannot '"+op()+"' " + in(1)._type);
-        if( !(in(2)._type instanceof SONTypeInteger) ) return Compiler.error("Cannot '"+op()+"' " + in(2)._type);
+        if( !(in(1)._type instanceof TypeInteger) ) return Compiler.error("Cannot '"+op()+"' " + in(1)._type);
+        if( !(in(2)._type instanceof TypeInteger) ) return Compiler.error("Cannot '"+op()+"' " + in(2)._type);
         return null;
     }
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/LoopNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/LoopNode.java
index a912ba8..87c1a03 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/LoopNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/LoopNode.java
@@ -1,8 +1,8 @@
 package com.compilerprogramming.ezlang.compiler.nodes;
 
 import com.compilerprogramming.ezlang.compiler.codegen.CodeGen;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeMem;
+import com.compilerprogramming.ezlang.compiler.sontypes.Type;
+import com.compilerprogramming.ezlang.compiler.sontypes.TypeMem;
 
 public class LoopNode extends RegionNode {
     public LoopNode( Node entry ) { super(null,entry,null); }
@@ -15,8 +15,8 @@ public class LoopNode extends RegionNode {
     public String label() { return "Loop"; }
 
     @Override
-    public SONType compute() {
-        if( inProgress() ) return SONType.CONTROL;
+    public Type compute() {
+        if( inProgress() ) return Type.CONTROL;
         return entry()._type;
     }
 
@@ -53,15 +53,15 @@ public StopNode forceExit( FunNode fun, StopNode stop ) {
 
         // Now fold control into the exit.  Might have 1 valid exit, or an
         // XCtrl or a bunch of prior NeverNode exits.
-        Node top = new ConstantNode(SONType.TOP).peephole();
+        Node top = new ConstantNode(Type.TOP).peephole();
         Node memout = new MemMergeNode(false);
         memout.addDef(f); // placeholder for control
         for( Node u : _outputs )
-            if( u instanceof PhiNode phi && phi._type.isa(SONTypeMem.BOT) )
+            if( u instanceof PhiNode phi && phi._type.isa(TypeMem.BOT) )
                 memout.addDef(phi);
 
         Node ctrl = ret.ctrl(), mem = ret.mem(), expr = ret.expr();
-        if( ctrl!=null && ctrl._type != SONType.XCONTROL ) {
+        if( ctrl!=null && ctrl._type != Type.XCONTROL ) {
             // Perfect aligned exit?
             if( !(ctrl instanceof RegionNode r &&
                   mem  instanceof PhiNode pmem && pmem.region()==r &&
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MachConcreteNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MachConcreteNode.java
index 82aa6e0..f83f429 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MachConcreteNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MachConcreteNode.java
@@ -1,6 +1,6 @@
 package com.compilerprogramming.ezlang.compiler.nodes;
 
-import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
+import com.compilerprogramming.ezlang.compiler.sontypes.Type;
 import com.compilerprogramming.ezlang.compiler.Utils;
 
 import java.util.BitSet;
@@ -13,7 +13,7 @@ public abstract class MachConcreteNode extends Node implements MachNode {
     public MachConcreteNode(Node[]nodes) { super(nodes); }
 
     @Override public String label() { return op(); }
-    @Override public SONType compute () { throw Utils.TODO(); }
+    @Override public Type compute () { throw Utils.TODO(); }
     @Override public Node idealize() { throw Utils.TODO(); }
 
     @Override public StringBuilder _print1(StringBuilder sb, BitSet visited) {
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MemMergeNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MemMergeNode.java
index 6a36e74..8de4ab8 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MemMergeNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MemMergeNode.java
@@ -18,7 +18,7 @@ public class MemMergeNode extends Node {
      */
     public final boolean _inProgress;
 
-    public MemMergeNode( boolean inProgress) { _type = SONTypeMem.BOT; _inProgress = inProgress; }
+    public MemMergeNode( boolean inProgress) { _type = TypeMem.BOT; _inProgress = inProgress; }
     public MemMergeNode(MemMergeNode mem) { super(mem); _inProgress = false; }
 
 
@@ -59,7 +59,7 @@ public Node merge() {
     }
 
 
-    @Override public SONType compute() { return SONTypeMem.BOT; }
+    @Override public Type compute() { return TypeMem.BOT; }
 
     @Override public Node idealize() {
         if( inProgress() ) return null;
@@ -106,7 +106,7 @@ Node _mem( int alias, Node st ) {
                 // Set real Phi in the loop head
                 // The phi takes its one input (no backedge yet) from a recursive
                 // lookup, which might have insert a Phi in every loop nest.
-                : loopmem.alias(alias, new PhiNode(Compiler.memName(alias), SONTypeMem.BOT,loop.ctrl(),loopmem._mem(alias,null),null).peephole() );
+                : loopmem.alias(alias, new PhiNode(Compiler.memName(alias), TypeMem.BOT,loop.ctrl(),loopmem._mem(alias,null),null).peephole() );
             alias(alias,old);
         }
         // Memory projections are made lazily; expand as needed
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MemOpNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MemOpNode.java
index 0bf695d..87c1c2e 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MemOpNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MemOpNode.java
@@ -2,8 +2,8 @@
 
 import com.compilerprogramming.ezlang.compiler.Compiler;
 import com.compilerprogramming.ezlang.compiler.Utils;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeMemPtr;
+import com.compilerprogramming.ezlang.compiler.sontypes.Type;
+import com.compilerprogramming.ezlang.compiler.sontypes.TypeMemPtr;
 import com.compilerprogramming.ezlang.exceptions.CompilerException;
 
 import java.lang.StringBuilder;
@@ -37,18 +37,18 @@ public abstract class MemOpNode extends Node {
 
     // Declared type; not final because it might be a forward-reference
     // which will be lazily improved when the reference is declared.
-    public SONType _declaredType;
+    public Type _declaredType;
 
     // A debug name, no semantic meaning
     public final String _name;
-    public MemOpNode(String name, int alias, boolean isLoad, SONType glb, Node mem, Node ptr, Node off) {
+    public MemOpNode(String name, int alias, boolean isLoad, Type glb, Node mem, Node ptr, Node off) {
         super(null, mem, ptr, off);
         _name  = name;
         _alias = alias;
         _declaredType = glb;
         _isLoad = isLoad;
     }
-    public MemOpNode(String name, int alias, boolean isLoad, SONType glb, Node mem, Node ptr, Node off, Node value) {
+    public MemOpNode(String name, int alias, boolean isLoad, Type glb, Node mem, Node ptr, Node off, Node value) {
         this(name, alias, isLoad, glb, mem, ptr, off);
         addDef(value);
     }
@@ -57,7 +57,7 @@ public MemOpNode( Node mach, MemOpNode mop ) {
         _name  = mop==null ? null : mop._name;
         _alias = mop==null ? 0    : mop._alias;
         _isLoad= mop==null ? true : mop._isLoad;
-        _declaredType = mop==null ? SONType.BOTTOM : mop._declaredType;
+        _declaredType = mop==null ? Type.BOTTOM : mop._declaredType;
         if( mop==null )
             throw Utils.TODO("Load or not");
     }
@@ -69,7 +69,7 @@ public MemOpNode( boolean isLoad ) {
         _alias        = 0;
 
         _isLoad       = isLoad;
-        _declaredType = SONType.BOTTOM;
+        _declaredType = Type.BOTTOM;
     }
 
     //
@@ -95,12 +95,12 @@ public boolean eq(Node n) {
 
     @Override
     public CompilerException err() {
-        SONType ptr = ptr()._type;
+        Type ptr = ptr()._type;
         // Already an error, but better error messages come from elsewhere
-        if( ptr == SONType.BOTTOM ) return null;
+        if( ptr == Type.BOTTOM ) return null;
         if( ptr.isHigh() ) return null; // Assume it will fall to not-null
         // Better be a not-nil TMP
-        if( ptr instanceof SONTypeMemPtr tmp && tmp.notNull() )
+        if( ptr instanceof TypeMemPtr tmp && tmp.notNull() )
             return null;
         return Compiler.error( "Might be null accessing '" + _name + "'");
     }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MinusFNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MinusFNode.java
index aa93f64..6a6a3f0 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MinusFNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MinusFNode.java
@@ -1,7 +1,7 @@
 package com.compilerprogramming.ezlang.compiler.nodes;
 
-import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFloat;
+import com.compilerprogramming.ezlang.compiler.sontypes.Type;
+import com.compilerprogramming.ezlang.compiler.sontypes.TypeFloat;
 
 import java.util.BitSet;
 
@@ -19,10 +19,10 @@ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
     }
 
     @Override
-    public SONType compute() {
-        if (in(1)._type instanceof SONTypeFloat i0)
-            return i0.isConstant() ? SONTypeFloat.constant(-i0.value()) : i0;
-        return SONTypeFloat.F64;
+    public Type compute() {
+        if (in(1)._type instanceof TypeFloat i0)
+            return i0.isConstant() ? TypeFloat.constant(-i0.value()) : i0;
+        return TypeFloat.F64;
     }
 
     @Override
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MinusNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MinusNode.java
index 6bb92e1..8b72814 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MinusNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MinusNode.java
@@ -1,7 +1,7 @@
 package com.compilerprogramming.ezlang.compiler.nodes;
 
-import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeInteger;
+import com.compilerprogramming.ezlang.compiler.sontypes.Type;
+import com.compilerprogramming.ezlang.compiler.sontypes.TypeInteger;
 
 import java.util.BitSet;
 
@@ -19,12 +19,12 @@ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
     }
 
     @Override
-    public SONType compute() {
-        if( in(1)._type.isHigh() ) return SONTypeInteger.TOP;
-        if( in(1)._type instanceof SONTypeInteger i0 &&
-            !(i0== SONTypeInteger.BOT || i0._min == Long.MIN_VALUE || i0._max == Long.MIN_VALUE) )
-            return SONTypeInteger.make(-i0._max,-i0._min);
-        return SONTypeInteger.BOT;
+    public Type compute() {
+        if( in(1)._type.isHigh() ) return TypeInteger.TOP;
+        if( in(1)._type instanceof TypeInteger i0 &&
+            !(i0== TypeInteger.BOT || i0._min == Long.MIN_VALUE || i0._max == Long.MIN_VALUE) )
+            return TypeInteger.make(-i0._max,-i0._min);
+        return TypeInteger.BOT;
     }
 
     @Override
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MulFNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MulFNode.java
index d0f0f37..5890f10 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MulFNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MulFNode.java
@@ -1,7 +1,7 @@
 package com.compilerprogramming.ezlang.compiler.nodes;
 
-import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFloat;
+import com.compilerprogramming.ezlang.compiler.sontypes.Type;
+import com.compilerprogramming.ezlang.compiler.sontypes.TypeFloat;
 
 import java.util.BitSet;
 
@@ -20,11 +20,11 @@ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
     }
 
     @Override
-    public SONType compute() {
-        if (in(1)._type instanceof SONTypeFloat i0 &&
-            in(2)._type instanceof SONTypeFloat i1) {
+    public Type compute() {
+        if (in(1)._type instanceof TypeFloat i0 &&
+            in(2)._type instanceof TypeFloat i1) {
             if (i0.isConstant() && i1.isConstant())
-                return SONTypeFloat.constant(i0.value()*i1.value());
+                return TypeFloat.constant(i0.value()*i1.value());
         }
         return in(1)._type.meet(in(2)._type);
     }
@@ -33,12 +33,12 @@ public SONType compute() {
     public Node idealize() {
         Node lhs = in(1);
         Node rhs = in(2);
-        SONType t1 = lhs._type;
-        SONType t2 = rhs._type;
+        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 SONTypeFloat i && i.value()==1 )
+        if ( t2.isConstant() && t2 instanceof TypeFloat i && i.value()==1 )
             return lhs;
 
         // Move constants to RHS: con*arg becomes arg*con
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MulNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MulNode.java
index e35b172..81f378c 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MulNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MulNode.java
@@ -1,8 +1,8 @@
 package com.compilerprogramming.ezlang.compiler.nodes;
 
 import com.compilerprogramming.ezlang.compiler.Compiler;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeInteger;
+import com.compilerprogramming.ezlang.compiler.sontypes.Type;
+import com.compilerprogramming.ezlang.compiler.sontypes.TypeInteger;
 import com.compilerprogramming.ezlang.exceptions.CompilerException;
 
 import java.util.BitSet;
@@ -22,33 +22,33 @@ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
     }
 
     @Override
-    public SONType compute() {
-        SONType t1 = in(1)._type, t2 = in(2)._type;
+    public Type compute() {
+        Type t1 = in(1)._type, t2 = in(2)._type;
         if( t1.isHigh() || t2.isHigh() )
-            return SONTypeInteger.TOP;
-        if( t1 instanceof SONTypeInteger i1 &&
-            t2 instanceof SONTypeInteger i2 ) {
-            if( i1== SONTypeInteger.ZERO || i2== SONTypeInteger.ZERO)
-                return SONTypeInteger.ZERO;
+            return TypeInteger.TOP;
+        if( t1 instanceof TypeInteger i1 &&
+            t2 instanceof TypeInteger i2 ) {
+            if( i1== TypeInteger.ZERO || i2== TypeInteger.ZERO)
+                return TypeInteger.ZERO;
             if (i1.isConstant() && i2.isConstant())
-                return SONTypeInteger.constant(i1.value()*i2.value());
+                return TypeInteger.constant(i1.value()*i2.value());
         }
-        return SONTypeInteger.BOT;
+        return TypeInteger.BOT;
     }
 
     @Override
     public Node idealize() {
         Node lhs = in(1);
         Node rhs = in(2);
-        SONType t1 = lhs._type;
-        SONType t2 = rhs._type;
+        Type t1 = lhs._type;
+        Type t2 = rhs._type;
 
         // Move constants to RHS: con*arg becomes arg*con
         if ( t1.isConstant() && !t2.isConstant() )
             return swap12();
 
         // Multiply by constant
-        if ( t2.isConstant() && t2 instanceof SONTypeInteger i ) {
+        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();
@@ -82,8 +82,8 @@ public Node idealize() {
     @Override Node copyF() { return new MulFNode(null,null); }
     @Override public CompilerException err() {
         if( in(1)._type.isHigh() || in(2)._type.isHigh() ) return null;
-        if( !(in(1)._type instanceof SONTypeInteger) ) return Compiler.error("Cannot '"+label()+"' " + in(1)._type.glb());
-        if( !(in(2)._type instanceof SONTypeInteger) ) return Compiler.error("Cannot '"+label()+"' " + in(2)._type.glb());
+        if( !(in(1)._type instanceof TypeInteger) ) return Compiler.error("Cannot '"+label()+"' " + in(1)._type.glb());
+        if( !(in(2)._type instanceof TypeInteger) ) return Compiler.error("Cannot '"+label()+"' " + in(2)._type.glb());
         return null;
     }
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/NeverNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/NeverNode.java
index 5e7f4e7..4d392ef 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/NeverNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/NeverNode.java
@@ -1,8 +1,7 @@
 package com.compilerprogramming.ezlang.compiler.nodes;
 
-import com.compilerprogramming.ezlang.compiler.Compiler;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeTuple;
+import com.compilerprogramming.ezlang.compiler.sontypes.Type;
+import com.compilerprogramming.ezlang.compiler.sontypes.TypeTuple;
 
 import java.util.BitSet;
 
@@ -15,7 +14,7 @@ public class NeverNode extends IfNode {
 
     @Override public StringBuilder _print1(StringBuilder sb, BitSet visited) { return sb.append("Never"); }
 
-    @Override public SONType compute() { return SONTypeTuple.IF_BOTH; }
+    @Override public Type compute() { return TypeTuple.IF_BOTH; }
 
     @Override public Node idealize() { return null; }
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/NewNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/NewNode.java
index 1d7ca33..daac5e8 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/NewNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/NewNode.java
@@ -16,21 +16,21 @@
  */
 public class NewNode extends Node implements MultiNode {
 
-    public final SONTypeMemPtr _ptr;
+    public final TypeMemPtr _ptr;
     public final int _len;
 
-    public NewNode(SONTypeMemPtr ptr, Node... nodes) {
+    public NewNode(TypeMemPtr ptr, Node... nodes) {
         super(nodes);
         assert !ptr.nullable();
         _ptr = ptr;
         _len = ptr._obj._fields.length;
         // Control in slot 0
-        assert nodes[0]._type== SONType.CONTROL || nodes[0]._type == SONType.XCONTROL;
+        assert nodes[0]._type== Type.CONTROL || nodes[0]._type == Type.XCONTROL;
         // Malloc-length in slot 1
-        assert nodes[1]._type instanceof SONTypeInteger || nodes[1]._type== SONType.NIL;
+        assert nodes[1]._type instanceof TypeInteger || nodes[1]._type== Type.NIL;
         for( int i=0; i<_len; i++ )
             // Memory slices for all fields.
-            assert nodes[2+i]._type.isa(SONTypeMem.BOT);
+            assert nodes[2+i]._type.isa(TypeMem.BOT);
     }
 
     public NewNode(NewNode nnn) { super(nnn); _ptr = nnn._ptr; _len = nnn._len; }
@@ -61,19 +61,19 @@ int findAlias(int alias) {
     public Node size() { return in(1); }
 
     @Override
-    public SONTypeTuple compute() {
+    public TypeTuple compute() {
         Field[] fs = _ptr._obj._fields;
-        SONType[] ts = new SONType[fs.length+2];
-        ts[0] = SONType.CONTROL;
+        Type[] ts = new Type[fs.length+2];
+        ts[0] = Type.CONTROL;
         ts[1] = _ptr;
         for( int i=0; i<fs.length; i++ ) {
-            SONType mt = in(i+2)._type;
-            SONTypeMem mem = mt== SONType.TOP ? SONTypeMem.TOP : (SONTypeMem)mt;
-            SONType tfld = mem._t.meet(mem._t.makeZero());
-            SONType tfld2 = tfld.join(fs[i]._type);
-            ts[i+2] = SONTypeMem.make(fs[i]._alias,tfld2);
+            Type mt = in(i+2)._type;
+            TypeMem mem = mt== Type.TOP ? TypeMem.TOP : (TypeMem)mt;
+            Type tfld = mem._t.meet(mem._t.makeZero());
+            Type tfld2 = tfld.join(fs[i]._type);
+            ts[i+2] = TypeMem.make(fs[i]._alias,tfld2);
         }
-        return SONTypeTuple.make(ts);
+        return TypeTuple.make(ts);
     }
 
     @Override
@@ -92,10 +92,10 @@ public SONTypeTuple compute() {
     private RegMask _retMask;
     private RegMask _kills;
     public void cacheRegs(CodeGen code) {
-        _arg2Reg  = code._mach.callArgMask(SONTypeFunPtr.CALLOC,2,0).firstReg();
-        _arg3Mask = code._mach.callArgMask(SONTypeFunPtr.CALLOC,3,0);
+        _arg2Reg  = code._mach.callArgMask(TypeFunPtr.CALLOC,2,0).firstReg();
+        _arg3Mask = code._mach.callArgMask(TypeFunPtr.CALLOC,3,0);
         // Return mask depends on TFP (either GPR or FPR)
-        _retMask = code._mach.retMask(SONTypeFunPtr.CALLOC);
+        _retMask = code._mach.retMask(TypeFunPtr.CALLOC);
         // Kill mask is all caller-saves, and any mirror stack slots for args
         // in registers.
         RegMaskRW kills = code._callerSave.copy();
@@ -104,7 +104,7 @@ public void cacheRegs(CodeGen code) {
         // Incoming function arg slots, all low numbered in the RA
         int fslot = cfg0().fun()._maxArgSlot;
         // Killed slots for this calls outgoing args
-        int xslot = code._mach.maxArgSlot(SONTypeFunPtr.CALLOC);
+        int xslot = code._mach.maxArgSlot(TypeFunPtr.CALLOC);
         _xslot = (maxReg+fslot)+xslot;
         for( int i=0; i<xslot; i++ )
             kills.set((maxReg+fslot)+i);
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/Node.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/Node.java
index 75f8085..53fe5ca 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/Node.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/Node.java
@@ -3,9 +3,9 @@
 import com.compilerprogramming.ezlang.compiler.*;
 import com.compilerprogramming.ezlang.compiler.codegen.CodeGen;
 import com.compilerprogramming.ezlang.compiler.print.IRPrinter;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFloat;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeInteger;
+import com.compilerprogramming.ezlang.compiler.sontypes.Type;
+import com.compilerprogramming.ezlang.compiler.sontypes.TypeFloat;
+import com.compilerprogramming.ezlang.compiler.sontypes.TypeInteger;
 import com.compilerprogramming.ezlang.exceptions.CompilerException;
 
 import java.util.*;
@@ -51,7 +51,7 @@ public abstract class Node implements Cloneable {
      * Current computed type for this Node.  This value changes as the graph
      * changes and more knowledge is gained about the program.
      */
-    public SONType _type;
+    public Type _type;
 
 
     Node(Node... inputs) {
@@ -71,7 +71,7 @@ public abstract class Node implements Cloneable {
         _nid = CODE.getUID(); // allocate unique dense ID
         _inputs  = new Ary<>(n==null ? new Node[0] : n._inputs.asAry());
         _outputs = new Ary<>(Node.class);
-        _type = n==null ? SONType.BOTTOM : n._type;
+        _type = n==null ? Type.BOTTOM : n._type;
         _deps = null;
         _hash = 0;
     }
@@ -386,9 +386,9 @@ public final Node peephole( ) {
 
     /**
      * Try to peephole at this node and return a better replacement Node if
-     * possible.  We compute a {@link SONType} and then check and replace:
+     * possible.  We compute a {@link Type} and then check and replace:
      * <ul>
-     * <li>if the Type {@link SONType#isConstant}, we replace with a {@link ConstantNode}</li>
+     * <li>if the Type {@link Type#isConstant}, we replace with a {@link ConstantNode}</li>
      * <li>in a future chapter we will look for a
      * <a href="https://en.wikipedia.org/wiki/Common_subexpression_elimination">Common Subexpression</a>
      * to eliminate.</li>
@@ -405,7 +405,7 @@ public final Node peephole( ) {
     public final Node peepholeOpt( ) {
         CODE.iterCnt();
         // Compute initial or improved Type
-        SONType old = setType(compute());
+        Type old = setType(compute());
 
         // Replace constant computations from non-constants with a constant
         // node.  If peeps are disabled, still allow high Phis to collapse;
@@ -473,12 +473,12 @@ private Node deadCodeElim(Node m) {
      * infinitely recurse until stack overflow.  Instead, compute typically
      * computes a new type from the {@link #_type} field of its inputs.
      */
-    public abstract SONType compute();
+    public abstract Type compute();
 
     // Set the type.  Assert monotonic progress.
     // If changing, add users to worklist.
-    public SONType setType(SONType type) {
-        SONType old = _type;
+    public Type setType(Type type) {
+        Type old = _type;
         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
@@ -634,13 +634,13 @@ public final Node widen() {
         Node flt = copyF();
         if( flt==null ) return this;
         for( int i=1; i<nIns(); i++ )
-            flt.setDef(i, in(i)._type instanceof SONTypeFloat ? in(i) : new ToFloatNode(in(i)).peephole());
+            flt.setDef(i, in(i)._type instanceof TypeFloat ? in(i) : new ToFloatNode(in(i)).peephole());
         kill();
         return flt;
     }
     private boolean hasFloatInput() {
         for( int i=1; i<nIns(); i++ )
-            if( in(i)._type instanceof SONTypeFloat)
+            if( in(i)._type instanceof TypeFloat)
                 return true;
         return false;
     }
@@ -695,7 +695,7 @@ public Node copy() {
     public CompilerException err() { return null; }
 
     // Common integer constants
-    public static ConstantNode con(long x) { return (ConstantNode)(new ConstantNode(SONTypeInteger.constant(x)).peephole()); }
+    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.
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/NotNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/NotNode.java
index 5649814..7f4dfbb 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/NotNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/NotNode.java
@@ -17,13 +17,13 @@ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
     }
 
     @Override
-    public SONTypeInteger compute() {
-        SONType t0 = in(1)._type;
-        if( t0.isHigh() )  return SONTypeInteger.BOOL.dual();
-        if( t0 == SONType.NIL || t0 == SONTypeInteger.ZERO ) return SONTypeInteger.TRUE;
-        if( t0 instanceof SONTypeNil tn && tn.notNull() ) return SONTypeInteger.FALSE;
-        if( t0 instanceof SONTypeInteger i && (i._min > 0 || i._max < 0) ) return SONTypeInteger.FALSE;
-        return SONTypeInteger.BOOL;
+    public TypeInteger compute() {
+        Type t0 = in(1)._type;
+        if( t0.isHigh() )  return TypeInteger.BOOL.dual();
+        if( t0 == Type.NIL || t0 == TypeInteger.ZERO ) return TypeInteger.TRUE;
+        if( t0 instanceof TypeNil tn && tn.notNull() ) return TypeInteger.FALSE;
+        if( t0 instanceof TypeInteger i && (i._min > 0 || i._max < 0) ) return TypeInteger.FALSE;
+        return TypeInteger.BOOL;
     }
 
     @Override
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/OrNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/OrNode.java
index fc76f23..444e2ca 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/OrNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/OrNode.java
@@ -1,8 +1,7 @@
 package com.compilerprogramming.ezlang.compiler.nodes;
 
-import com.compilerprogramming.ezlang.compiler.Compiler;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeInteger;
+import com.compilerprogramming.ezlang.compiler.sontypes.Type;
+import com.compilerprogramming.ezlang.compiler.sontypes.TypeInteger;
 
 public class OrNode extends LogicalNode {
     public OrNode(Node lhs, Node rhs) { super(lhs, rhs); }
@@ -13,28 +12,28 @@ public class OrNode extends LogicalNode {
     @Override public String glabel() { return "|"; }
 
     @Override
-    public SONType compute() {
-        SONType t1 = in(1)._type, t2 = in(2)._type;
+    public Type compute() {
+        Type t1 = in(1)._type, t2 = in(2)._type;
         if( t1.isHigh() || t2.isHigh() )
-            return SONTypeInteger.TOP;
-        if( t1 instanceof SONTypeInteger i0 &&
-            t2 instanceof SONTypeInteger i1 ) {
+            return TypeInteger.TOP;
+        if( t1 instanceof TypeInteger i0 &&
+            t2 instanceof TypeInteger i1 ) {
             if( i0.isConstant() && i1.isConstant() )
-                return SONTypeInteger.constant(i0.value()|i1.value());
+                return TypeInteger.constant(i0.value()|i1.value());
         }
-        return SONTypeInteger.BOT;
+        return TypeInteger.BOT;
     }
 
     @Override
     public Node idealize() {
         Node lhs = in(1);
         Node rhs = in(2);
-        SONType t1 = lhs._type;
-        SONType t2 = rhs._type;
+        Type t1 = lhs._type;
+        Type t2 = rhs._type;
 
         // Or of 0.  We do not check for (0|x) because this will already
         // canonicalize to (x|0)
-        if( t2.isConstant() && t2 instanceof SONTypeInteger i && i.value()==0 )
+        if( t2.isConstant() && t2 instanceof TypeInteger i && i.value()==0 )
             return lhs;
 
         // Move constants to RHS: con*arg becomes arg*con
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ParmNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ParmNode.java
index b755949..054d3a6 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ParmNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ParmNode.java
@@ -1,6 +1,6 @@
 package com.compilerprogramming.ezlang.compiler.nodes;
 
-import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
+import com.compilerprogramming.ezlang.compiler.sontypes.Type;
 import java.util.BitSet;
 
 public class ParmNode extends PhiNode {
@@ -8,7 +8,7 @@ public class ParmNode extends PhiNode {
     // Argument indices are mapped one-to-one on CallNode inputs
     public final int _idx;             // Argument index
 
-    public ParmNode(String label, int idx, SONType declaredType, Node... inputs) {
+    public ParmNode(String label, int idx, Type declaredType, Node... inputs) {
         super(label,declaredType,inputs);
         _idx = idx;
     }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/PhiNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/PhiNode.java
index 9ac7bb8..86f7818 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/PhiNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/PhiNode.java
@@ -11,10 +11,10 @@ public class PhiNode extends Node {
 
     // The Phi type we compute must stay within the domain of the Phi.
     // Example Int stays Int, Ptr stays Ptr, Control stays Control, Mem stays Mem.
-    final SONType _declaredType;
+    final Type _declaredType;
 
-    public PhiNode(String label, SONType declaredType, Node... inputs) { super(inputs); _label = label;  assert declaredType!=null; _declaredType = declaredType; }
-    public PhiNode(PhiNode phi, String label, SONType declaredType) { super(phi); _label = label; _type = _declaredType = 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; _type = _declaredType = declaredType; }
     public PhiNode(PhiNode phi) { super(phi); _label = phi._label; _declaredType = phi._declaredType;  }
 
     public PhiNode(RegionNode r, Node sample) {
@@ -45,21 +45,21 @@ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
     }
 
     public CFGNode region() { return (CFGNode)in(0); }
-    @Override public boolean isMem() { return _declaredType instanceof SONTypeMem; }
+    @Override public boolean isMem() { return _declaredType instanceof TypeMem; }
     @Override public boolean isPinned() { return true; }
 
     @Override
-    public SONType compute() {
+    public Type compute() {
         if( !(region() instanceof RegionNode r) )
-            return region()._type== SONType.XCONTROL ? (_type instanceof SONTypeMem ? SONTypeMem.TOP : SONType.TOP) : _type;
+            return region()._type== Type.XCONTROL ? (_type instanceof TypeMem ? TypeMem.TOP : Type.TOP) : _type;
         // During parsing Phis have to be computed type pessimistically.
         if( r.inProgress() ) return _declaredType;
         // Set type to local top of the starting type
-        SONType t = _declaredType.glb().dual();//Type.TOP;
+        Type t = _declaredType.glb().dual();//Type.TOP;
         for (int i = 1; i < nIns(); i++)
             // If the region's control input is live, add this as a dependency
             // to the control because we can be peeped should it become dead.
-            if( addDep(r.in(i))._type != SONType.XCONTROL )
+            if( addDep(r.in(i))._type != Type.XCONTROL )
                 t = t.meet(in(i)._type);
         return t;
     }
@@ -78,7 +78,7 @@ public Node idealize() {
 
         // No bother if region is going to fold dead paths soon
         for( int i=1; i<nIns(); i++ )
-            if( r.in(i)._type == SONType.XCONTROL )
+            if( r.in(i)._type == Type.XCONTROL )
                 return null;
 
         // Generic "pull down op"
@@ -165,13 +165,13 @@ private Node drop_same_op() {
      * If only single unique input, return it
      */
     private Node singleUniqueInput() {
-        if( region() instanceof LoopNode loop && loop.entry()._type == SONType.XCONTROL )
+        if( region() instanceof LoopNode loop && loop.entry()._type == Type.XCONTROL )
             return null;    // Dead entry loops just ignore and let the loop collapse
         Node live = null;
         for( int i=1; i<nIns(); i++ ) {
             // If the region's control input is live, add this as a dependency
             // to the control because we can be peeped should it become dead.
-            if( addDep(region().in(i))._type != SONType.XCONTROL && in(i) != this )
+            if( addDep(region().in(i))._type != Type.XCONTROL && in(i) != this )
                 if( live == null || live == in(i) ) live = in(i);
                 else return null;
         }
@@ -200,22 +200,22 @@ public boolean inProgress() {
 
     @Override
     public CompilerException err() {
-        if( _type != SONType.BOTTOM ) return null;
+        if( _type != Type.BOTTOM ) return null;
 
         // BOTTOM means we mixed e.g. int and ptr
         for( int i=1; i<nIns(); i++ )
             // Already an error, but better error messages come from elsewhere
-            if( in(i)._type == SONType.BOTTOM )
+            if( in(i)._type == Type.BOTTOM )
                 return null;
 
         // Gather a minimal set of types that "cover" all the rest
         boolean ti=false, tf=false, tp=false, tn=false;
         for( int i=1; i<nIns(); i++ ) {
-            SONType t = in(i)._type;
-            ti |= t instanceof SONTypeInteger x;
-            tf |= t instanceof SONTypeFloat x;
-            tp |= t instanceof SONTypeMemPtr x;
-            tn |= t== SONType.NIL;
+            Type t = in(i)._type;
+            ti |= t instanceof TypeInteger x;
+            tf |= t instanceof TypeFloat x;
+            tp |= t instanceof TypeMemPtr x;
+            tn |= t== Type.NIL;
         }
         return ReturnNode.mixerr(ti,tf,tp,tn);
     }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ProjNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ProjNode.java
index 212caed..6d68dea 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ProjNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ProjNode.java
@@ -1,8 +1,8 @@
 package com.compilerprogramming.ezlang.compiler.nodes;
 
-import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeMem;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeTuple;
+import com.compilerprogramming.ezlang.compiler.sontypes.Type;
+import com.compilerprogramming.ezlang.compiler.sontypes.TypeMem;
+import com.compilerprogramming.ezlang.compiler.sontypes.TypeTuple;
 
 import java.util.BitSet;
 
@@ -35,13 +35,13 @@ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
         return in(0) instanceof CFGNode cfg ? cfg : in(0).cfg0();
     }
 
-    @Override public boolean isMem() { return _type instanceof SONTypeMem; }
+    @Override public boolean isMem() { return _type instanceof TypeMem; }
     @Override public boolean isPinned() { return true; }
 
     @Override
-    public SONType compute() {
-        SONType t = in(0)._type;
-        return t instanceof SONTypeTuple tt ? tt._types[_idx] : SONType.BOTTOM;
+    public Type compute() {
+        Type t = in(0)._type;
+        return t instanceof TypeTuple tt ? tt._types[_idx] : Type.BOTTOM;
     }
 
     @Override public Node idealize() { return ((MultiNode)in(0)).pcopy(_idx); }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ReadOnlyNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ReadOnlyNode.java
index 90b6a96..6e63ca0 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ReadOnlyNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ReadOnlyNode.java
@@ -1,7 +1,7 @@
 package com.compilerprogramming.ezlang.compiler.nodes;
 
-import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeMemPtr;
+import com.compilerprogramming.ezlang.compiler.sontypes.Type;
+import com.compilerprogramming.ezlang.compiler.sontypes.TypeMemPtr;
 
 import java.util.BitSet;
 
@@ -17,13 +17,13 @@ public class ReadOnlyNode extends Node {
     public StringBuilder _print1(StringBuilder sb, BitSet visited) {
         return in(1)._print0(sb.append("(const)"),visited);
     }
-    @Override public SONType compute() {
-        SONType t = in(1)._type;
-        return t instanceof SONTypeMemPtr tmp ? tmp.makeRO() : t;
+    @Override public Type compute() {
+        Type t = in(1)._type;
+        return t instanceof TypeMemPtr tmp ? tmp.makeRO() : t;
     }
 
     @Override public Node idealize() {
-        if( in(1)._type instanceof SONTypeMemPtr tmp && tmp.isFinal() )
+        if( in(1)._type instanceof TypeMemPtr tmp && tmp.isFinal() )
             return in(1);
         return null;
     }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/RegionNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/RegionNode.java
index f193b2c..1631615 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/RegionNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/RegionNode.java
@@ -2,7 +2,7 @@
 
 import com.compilerprogramming.ezlang.compiler.Compiler;
 import com.compilerprogramming.ezlang.compiler.codegen.CodeGen;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
+import com.compilerprogramming.ezlang.compiler.sontypes.Type;
 import java.util.*;
 
 public class RegionNode extends CFGNode {
@@ -21,9 +21,9 @@ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
     @Override public boolean blockHead() { return true; }
 
     @Override
-    public SONType compute() {
-        if( inProgress() ) return SONType.CONTROL;
-        SONType t = SONType.XCONTROL;
+    public Type compute() {
+        if( inProgress() ) return Type.CONTROL;
+        Type t = Type.XCONTROL;
         for (int i = 1; i < nIns(); i++)
             t = t.meet(in(i)._type);
         return t;
@@ -106,7 +106,7 @@ Node deadPath() {
 
     private int findDeadInput() {
         for( int i=1; i<nIns(); i++ )
-            if( in(i)._type== SONType.XCONTROL )
+            if( in(i)._type== Type.XCONTROL )
                 return i;
         return 0;               // All inputs alive
     }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ReturnNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ReturnNode.java
index b63280a..056f5ce 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ReturnNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ReturnNode.java
@@ -51,10 +51,10 @@ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
     @Override public CFGNode uctrl() { return null; }
 
     @Override
-    public SONType compute() {
-        if( inProgress () ) return SONTypeTuple.RET; // In progress
-        if( _fun.isDead() ) return SONTypeTuple.RET.dual(); // Dead another way
-        return SONTypeTuple.make(ctrl()._type,mem()._type,expr()._type);
+    public Type compute() {
+        if( inProgress () ) return TypeTuple.RET; // In progress
+        if( _fun.isDead() ) return TypeTuple.RET.dual(); // Dead another way
+        return TypeTuple.make(ctrl()._type,mem()._type,expr()._type);
     }
 
     @Override public Node idealize() {
@@ -71,9 +71,9 @@ public SONType compute() {
 //            _fun.setSig(fcn.makeFrom(ret));
 
         // If dead (cant be reached; infinite loop), kill the exit values
-        if( ctrl()._type== SONType.XCONTROL &&
+        if( ctrl()._type== Type.XCONTROL &&
             !(mem() instanceof ConstantNode && expr() instanceof ConstantNode) ) {
-            Node top = new ConstantNode(SONType.TOP).peephole();
+            Node top = new ConstantNode(Type.TOP).peephole();
             setDef(1,top);
             setDef(2,top);
             return this;
@@ -87,7 +87,7 @@ public boolean inProgress() {
     }
 
     // Gather parse-time return types for error reporting
-    private SONType mt = SONType.TOP;
+    private Type mt = Type.TOP;
     private boolean ti=false, tf=false, tp=false, tn=false;
 
     // Add a return exit to the current parsing function
@@ -95,18 +95,18 @@ void addReturn( Node ctrl, Node rmem, Node expr ) {
         assert inProgress();
 
         // Gather parse-time return types for error reporting
-        SONType t = expr._type;
+        Type t = expr._type;
         mt = mt.meet(t);
-        ti |= t instanceof SONTypeInteger x;
-        tf |= t instanceof SONTypeFloat x;
-        tp |= t instanceof SONTypeMemPtr x;
-        tn |= t== SONType.NIL;
+        ti |= t instanceof TypeInteger x;
+        tf |= t instanceof TypeFloat x;
+        tp |= t instanceof TypeMemPtr x;
+        tn |= t== Type.NIL;
 
         // Merge path into the One True Return
         RegionNode r = (RegionNode)ctrl();
         // Assert that the Phis are in particular outputs; not reordered or shuffled
-        PhiNode mem = (PhiNode)r.out(0); assert mem._declaredType == SONTypeMem.BOT;
-        PhiNode rez = (PhiNode)r.out(1); assert rez._declaredType == SONType.BOTTOM;
+        PhiNode mem = (PhiNode)r.out(0); assert mem._declaredType == TypeMem.BOT;
+        PhiNode rez = (PhiNode)r.out(1); assert rez._declaredType == Type.BOTTOM;
         // Pop "inProgress" null off
         r  ._inputs.pop();
         mem._inputs.pop();
@@ -122,7 +122,7 @@ void addReturn( Node ctrl, Node rmem, Node expr ) {
     }
 
     @Override public CompilerException err() {
-        return expr()._type/*mt*/== SONType.BOTTOM ? mixerr(ti,tf,tp,tn) : null;
+        return expr()._type/*mt*/== Type.BOTTOM ? mixerr(ti,tf,tp,tn) : null;
     }
 
     static CompilerException mixerr( boolean ti, boolean tf, boolean tp, boolean tn) {
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/RoundF32Node.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/RoundF32Node.java
index aa9a698..b9ad962 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/RoundF32Node.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/RoundF32Node.java
@@ -1,7 +1,7 @@
 package com.compilerprogramming.ezlang.compiler.nodes;
 
-import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFloat;
+import com.compilerprogramming.ezlang.compiler.sontypes.Type;
+import com.compilerprogramming.ezlang.compiler.sontypes.TypeFloat;
 
 import java.util.BitSet;
 
@@ -18,19 +18,19 @@ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
     }
 
     @Override
-    public SONType compute() {
-        if (in(1)._type instanceof SONTypeFloat i0 && i0.isConstant() )
-            return SONTypeFloat.constant((float)i0.value());
+    public Type compute() {
+        if (in(1)._type instanceof TypeFloat i0 && i0.isConstant() )
+            return TypeFloat.constant((float)i0.value());
         return in(1)._type;
     }
 
     @Override
     public Node idealize() {
         Node lhs = in(1);
-        SONType t1 = lhs._type;
+        Type t1 = lhs._type;
 
         // RoundF32 of float
-        if( t1 instanceof SONTypeFloat tf && tf._sz==32 )
+        if( t1 instanceof TypeFloat tf && tf._sz==32 )
             return lhs;
 
         return null;
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/SarNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/SarNode.java
index 1a823dd..7192cfe 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/SarNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/SarNode.java
@@ -1,7 +1,7 @@
 package com.compilerprogramming.ezlang.compiler.nodes;
 
-import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeInteger;
+import com.compilerprogramming.ezlang.compiler.sontypes.Type;
+import com.compilerprogramming.ezlang.compiler.sontypes.TypeInteger;
 
 public class SarNode extends LogicalNode {
     public SarNode(Node lhs, Node rhs) { super(lhs, rhs); }
@@ -12,32 +12,32 @@ public class SarNode extends LogicalNode {
     @Override public String glabel() { return "&gt;&gt;"; }
 
     @Override
-    public SONType compute() {
-        SONType t1 = in(1)._type, t2 = in(2)._type;
+    public Type compute() {
+        Type t1 = in(1)._type, t2 = in(2)._type;
         if( t1.isHigh() || t2.isHigh() )
-            return SONTypeInteger.TOP;
-        if (t1 instanceof SONTypeInteger i0 &&
-            t2 instanceof SONTypeInteger i1) {
-            if( i0 == SONTypeInteger.ZERO )
-                return SONTypeInteger.ZERO;
+            return TypeInteger.TOP;
+        if (t1 instanceof TypeInteger i0 &&
+            t2 instanceof TypeInteger i1) {
+            if( i0 == TypeInteger.ZERO )
+                return TypeInteger.ZERO;
             if( i0.isConstant() && i1.isConstant() )
-                return SONTypeInteger.constant(i0.value()>>i1.value());
+                return TypeInteger.constant(i0.value()>>i1.value());
             if( i1.isConstant() ) {
                 int log = (int)i1.value();
-                return SONTypeInteger.make(-1L<<(63-log),(1L<<(63-log))-1);
+                return TypeInteger.make(-1L<<(63-log),(1L<<(63-log))-1);
             }
         }
-        return SONTypeInteger.BOT;
+        return TypeInteger.BOT;
     }
 
     @Override
     public Node idealize() {
         Node lhs = in(1);
         Node rhs = in(2);
-        SONType t2 = rhs._type;
+        Type t2 = rhs._type;
 
         // Sar of 0.
-        if( t2.isConstant() && t2 instanceof SONTypeInteger i && (i.value()&63)==0 )
+        if( t2.isConstant() && t2 instanceof TypeInteger i && (i.value()&63)==0 )
             return lhs;
 
         // TODO: x >> 3 >> (y ? 1 : 2) ==> x >> (y ? 4 : 5)
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ScopeNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ScopeNode.java
index c3109cc..bf3b53e 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ScopeNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ScopeNode.java
@@ -144,7 +144,7 @@ int find( String name ) {
     /**
      * Create a new variable name in the current scope
      */
-    public boolean define(String name, SONType declaredType, boolean xfinal, Node init) {
+    public boolean define(String name, Type declaredType, boolean xfinal, Node init) {
         assert _lexSize.isEmpty() || name.charAt(0)!='$' ; // Later scopes do not define memory
         if( _lexSize._len > 0 )
             for( int i=_vars.size()-1; i>=_lexSize.last(); i-- ) {
@@ -355,20 +355,20 @@ public void addGuards( Node ctrl, Node pred, boolean invert ) {
             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.
-        SONType tnz = pred._type.nonZero();
+        Type tnz = pred._type.nonZero();
         if( tnz!=null )
             _addGuard(tnz,ctrl,pred);
 
         // Compute the negative test type.
         if( pred instanceof NotNode not ) {
             Node npred = not.in(1);
-            SONType tzero = npred._type.makeZero();
+            Type tzero = npred._type.makeZero();
             _addGuard(tzero,ctrl,npred);
         }
     }
 
-    private void _addGuard(SONType guard, Node ctrl, Node pred) {
-        SONType tcast = guard.join(pred._type);
+    private void _addGuard(Type guard, Node ctrl, Node pred) {
+        Type tcast = guard.join(pred._type);
         if( tcast != pred._type && !tcast.isHigh() ) {
             Node cast = new CastNode(tcast,ctrl,pred.keep()).peephole().keep();
             _guards.add(pred);
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ShlNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ShlNode.java
index f8d59b5..8b9a66c 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ShlNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ShlNode.java
@@ -1,8 +1,8 @@
 package com.compilerprogramming.ezlang.compiler.nodes;
 
 import com.compilerprogramming.ezlang.compiler.Compiler;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeInteger;
+import com.compilerprogramming.ezlang.compiler.sontypes.Type;
+import com.compilerprogramming.ezlang.compiler.sontypes.TypeInteger;
 
 public class ShlNode extends LogicalNode {
     public ShlNode(Node lhs, Node rhs) { super(lhs, rhs); }
@@ -13,18 +13,18 @@ public class ShlNode extends LogicalNode {
     @Override public String glabel() { return "&lt;&lt;"; }
 
     @Override
-    public SONType compute() {
-        SONType t1 = in(1)._type, t2 = in(2)._type;
+    public Type compute() {
+        Type t1 = in(1)._type, t2 = in(2)._type;
         if( t1.isHigh() || t2.isHigh() )
-            return SONTypeInteger.TOP;
-        if (t1 instanceof SONTypeInteger i0 &&
-            t2 instanceof SONTypeInteger i1 ) {
-            if( i0 == SONTypeInteger.ZERO )
-                return SONTypeInteger.ZERO;
+            return TypeInteger.TOP;
+        if (t1 instanceof TypeInteger i0 &&
+            t2 instanceof TypeInteger i1 ) {
+            if( i0 == TypeInteger.ZERO )
+                return TypeInteger.ZERO;
             if( i0.isConstant() && i1.isConstant() )
-                return SONTypeInteger.constant(i0.value()<<i1.value());
+                return TypeInteger.constant(i0.value()<<i1.value());
         }
-        return SONTypeInteger.BOT;
+        return TypeInteger.BOT;
     }
 
     @Override
@@ -32,12 +32,12 @@ public Node idealize() {
         Node lhs = in(1);
         Node rhs = in(2);
 
-        if( rhs._type instanceof SONTypeInteger shl && shl.isConstant() ) {
+        if( rhs._type instanceof TypeInteger shl && shl.isConstant() ) {
             // Shl of 0.
             if( (shl.value()&63)==0 )
                 return lhs;
             // (x + c) << i  =>  (x << i) + (c << i)
-            if( lhs instanceof AddNode add && addDep(add).in(2)._type instanceof SONTypeInteger c && c.isConstant() ) {
+            if( lhs instanceof AddNode add && addDep(add).in(2)._type instanceof TypeInteger c && c.isConstant() ) {
                 long sum = c.value() << shl.value();
                 if( Integer.MIN_VALUE <= sum  && sum <= Integer.MAX_VALUE )
                     return new AddNode(new ShlNode(add.in(1),rhs).peephole(), Compiler.con(sum) );
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ShrNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ShrNode.java
index c4b2a9b..f50ac65 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ShrNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ShrNode.java
@@ -1,8 +1,7 @@
 package com.compilerprogramming.ezlang.compiler.nodes;
 
-import com.compilerprogramming.ezlang.compiler.Compiler;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeInteger;
+import com.compilerprogramming.ezlang.compiler.sontypes.Type;
+import com.compilerprogramming.ezlang.compiler.sontypes.TypeInteger;
 
 public class ShrNode extends LogicalNode {
     public ShrNode(Node lhs, Node rhs) { super(lhs, rhs); }
@@ -13,28 +12,28 @@ public class ShrNode extends LogicalNode {
     @Override public String glabel() { return "&gt;&gt;&gt;"; }
 
     @Override
-    public SONType compute() {
-        SONType t1 = in(1)._type, t2 = in(2)._type;
+    public Type compute() {
+        Type t1 = in(1)._type, t2 = in(2)._type;
         if( t1.isHigh() || t2.isHigh() )
-            return SONTypeInteger.TOP;
-        if (t1 instanceof SONTypeInteger i0 &&
-            t2 instanceof SONTypeInteger i1 ) {
-            if( i0 == SONTypeInteger.ZERO )
-                return SONTypeInteger.ZERO;
+            return TypeInteger.TOP;
+        if (t1 instanceof TypeInteger i0 &&
+            t2 instanceof TypeInteger i1 ) {
+            if( i0 == TypeInteger.ZERO )
+                return TypeInteger.ZERO;
             if( i0.isConstant() && i1.isConstant() )
-                return SONTypeInteger.constant(i0.value()>>>i1.value());
+                return TypeInteger.constant(i0.value()>>>i1.value());
         }
-        return SONTypeInteger.BOT;
+        return TypeInteger.BOT;
     }
 
     @Override
     public Node idealize() {
         Node lhs = in(1);
         Node rhs = in(2);
-        SONType t2 = rhs._type;
+        Type t2 = rhs._type;
 
         // Shr of 0.
-        if( t2.isConstant() && t2 instanceof SONTypeInteger i && (i.value()&63)==0 )
+        if( t2.isConstant() && t2 instanceof TypeInteger i && (i.value()&63)==0 )
             return lhs;
 
         // TODO: x >>> 3 >>> (y ? 1 : 2) ==> x >>> (y ? 4 : 5)
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/SplitNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/SplitNode.java
index 0040146..f233e5f 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/SplitNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/SplitNode.java
@@ -2,7 +2,7 @@
 
 import com.compilerprogramming.ezlang.compiler.SB;
 import com.compilerprogramming.ezlang.compiler.codegen.CodeGen;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
+import com.compilerprogramming.ezlang.compiler.sontypes.Type;
 import java.util.BitSet;
 
 public abstract class SplitNode extends MachConcreteNode {
@@ -16,7 +16,7 @@ public abstract class SplitNode extends MachConcreteNode {
         else in(1)._print0(sb,visited);
         return sb.append(")");
     }
-    @Override public SONType compute() { return in(0)._type; }
+    @Override public Type compute() { return in(0)._type; }
     @Override public Node idealize() { return null; }
     @Override public void asm(CodeGen code, SB sb) {
         sb.p(code.reg(this)).p(" = ").p(code.reg(in(1)));
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StartNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StartNode.java
index 5908301..ba18e35 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StartNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StartNode.java
@@ -1,7 +1,6 @@
 package com.compilerprogramming.ezlang.compiler.nodes;
 
 import com.compilerprogramming.ezlang.compiler.codegen.CodeGen;
-import com.compilerprogramming.ezlang.compiler.Compiler;
 import com.compilerprogramming.ezlang.compiler.sontypes.*;
 
 import java.util.BitSet;
@@ -17,9 +16,9 @@
  */
 public class StartNode extends LoopNode implements MultiNode {
 
-    final SONType _arg;
+    final Type _arg;
 
-    public StartNode(SONType arg) { super((Node)null); _arg = arg; _type = compute(); }
+    public StartNode(Type arg) { super((Node)null); _arg = arg; _type = compute(); }
     public StartNode(StartNode start) { super(start); _arg = start==null ? null : start._arg; }
 
     @Override public String label() { return "Start"; }
@@ -44,8 +43,8 @@ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
     }
 
 
-    @Override public SONTypeTuple compute() {
-        return SONTypeTuple.make(SONType.CONTROL, SONTypeMem.TOP,_arg);
+    @Override public TypeTuple compute() {
+        return TypeTuple.make(Type.CONTROL, TypeMem.TOP,_arg);
     }
 
     @Override public Node idealize() { return null; }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StopNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StopNode.java
index f0fbdfa..5f19131 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StopNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StopNode.java
@@ -1,6 +1,6 @@
 package com.compilerprogramming.ezlang.compiler.nodes;
 
-import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
+import com.compilerprogramming.ezlang.compiler.sontypes.Type;
 import java.util.BitSet;
 
 public class StopNode extends CFGNode {
@@ -40,8 +40,8 @@ public ReturnNode ret() {
     }
 
     @Override
-    public SONType compute() {
-        return SONType.BOTTOM;
+    public Type compute() {
+        return Type.BOTTOM;
     }
 
     @Override
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StoreNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StoreNode.java
index a37af4f..fc3d9a9 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StoreNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StoreNode.java
@@ -21,7 +21,7 @@ public class StoreNode extends MemOpNode {
      * @param off   The offset inside the struct base
      * @param value Value to be stored
      */
-    public StoreNode(String name, int alias, SONType glb, Node mem, Node ptr, Node off, Node value, boolean init) {
+    public StoreNode(String name, int alias, Type glb, Node mem, Node ptr, Node off, Node value, boolean init) {
         super(name, alias, false, glb, mem, ptr, off, value);
         _init = init;
     }
@@ -40,20 +40,20 @@ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
     }
 
     @Override
-    public SONType compute() {
-        SONType val = val()._type;
-        SONTypeMem mem = (SONTypeMem)mem()._type; // Invariant
-        if( mem == SONTypeMem.TOP ) return SONTypeMem.TOP;
-        SONType t = SONType.BOTTOM;               // No idea on field contents
+    public Type compute() {
+        Type val = val()._type;
+        TypeMem mem = (TypeMem)mem()._type; // Invariant
+        if( mem == TypeMem.TOP ) return TypeMem.TOP;
+        Type t = Type.BOTTOM;               // No idea on field contents
         // Same alias, lift val to the declared type and then meet into other fields
         if( mem._alias == _alias ) {
             // Update declared forward ref to the actual
-            if( _declaredType.isFRef() && val instanceof SONTypeMemPtr tmp && !tmp.isFRef() )
+            if( _declaredType.isFRef() && val instanceof TypeMemPtr tmp && !tmp.isFRef() )
                 _declaredType = tmp;
             val = val.join(_declaredType);
             t = val.meet(mem._t);
         }
-        return SONTypeMem.make(_alias,t);
+        return TypeMem.make(_alias,t);
     }
 
     @Override
@@ -64,7 +64,7 @@ public Node idealize() {
         if( mem() instanceof StoreNode st &&
             ptr()==st.ptr() &&  // Must check same object
             off()==st.off() &&  // And same offset (could be "same alias" but this handles arrays to same index)
-            ptr()._type instanceof SONTypeMemPtr && // No bother if weird dead pointers
+            ptr()._type instanceof TypeMemPtr && // No bother if weird dead pointers
             // Must have exactly one use of "this" or you get weird
             // non-serializable memory effects in the worse case.
             checkOnlyUse(st) ) {
@@ -77,7 +77,7 @@ public Node idealize() {
         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 = ((SONTypeInteger)and.in(2)._type).value();
+                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 )
@@ -89,7 +89,7 @@ public Node idealize() {
         // Store of zero after alloc
         if( mem() instanceof ProjNode prj && prj.in(0) instanceof NewNode &&
             prj.in(0)==ptr().in(0) &&  // Same NewNode memory & pointer
-            (val()._type==SONTypeInteger.ZERO || val()._type==SONType.NIL ) )
+            (val()._type== TypeInteger.ZERO || val()._type== Type.NIL ) )
             return mem();
 
         return null;
@@ -110,10 +110,10 @@ private boolean checkOnlyUse(Node mem) {
     public CompilerException err() {
         CompilerException err = super.err();
         if( err != null ) return err;
-        SONTypeMemPtr tmp = (SONTypeMemPtr)ptr()._type;
+        TypeMemPtr tmp = (TypeMemPtr)ptr()._type;
         if( tmp._obj.field(_name)._final && !_init )
             return Compiler.error("Cannot modify final field '"+_name+"'");
-        SONType t = val()._type;
+        Type t = val()._type;
         //return _init || t.isa(_declaredType) ? null : Parser.error("Cannot store "+t+" into field "+_declaredType+" "+_name,_loc);
         return null;
     }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StructNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StructNode.java
index 71f3bb2..82746b2 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StructNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StructNode.java
@@ -9,7 +9,7 @@
  */
 public class StructNode extends Node {
 
-    public SONTypeStruct _ts;
+    public TypeStruct _ts;
 
     @Override public String label() { return _ts==null ? "STRUCT?" : _ts.str(); }
 
@@ -19,7 +19,7 @@ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
         sb.append(_ts._name).append(" {");
         for( int i=0; i<nIns(); i++ ) {
             sb.append(_ts._fields[i]._fname).append(":");
-            sb.append((in(i)==null ? SONType.BOTTOM : in(i)._type).print(new SB()));
+            sb.append((in(i)==null ? Type.BOTTOM : in(i)._type).print(new SB()));
             sb.append("; ");
         }
         sb.setLength(sb.length()-2);
@@ -27,12 +27,12 @@ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
     }
 
     @Override
-    public SONTypeStruct compute() {
-        if( _ts==null ) return SONTypeStruct.BOT;
+    public TypeStruct compute() {
+        if( _ts==null ) return TypeStruct.BOT;
         Field[] fs = new Field[_ts._fields.length];
         for( int i=0; i<fs.length; i++ )
-            fs[i] = _ts._fields[i].makeFrom(in(i)==null ? SONType.TOP : in(i)._type);
-        return SONTypeStruct.make(_ts._name,fs);
+            fs[i] = _ts._fields[i].makeFrom(in(i)==null ? Type.TOP : in(i)._type);
+        return TypeStruct.make(_ts._name,fs);
     }
 
     @Override
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/SubFNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/SubFNode.java
index 371fe5b..0e50c75 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/SubFNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/SubFNode.java
@@ -1,7 +1,7 @@
 package com.compilerprogramming.ezlang.compiler.nodes;
 
-import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFloat;
+import com.compilerprogramming.ezlang.compiler.sontypes.Type;
+import com.compilerprogramming.ezlang.compiler.sontypes.TypeFloat;
 
 import java.util.BitSet;
 
@@ -20,11 +20,11 @@ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
     }
 
     @Override
-    public SONType compute() {
-        if (in(1)._type instanceof SONTypeFloat i0 &&
-            in(2)._type instanceof SONTypeFloat i1) {
+    public Type compute() {
+        if (in(1)._type instanceof TypeFloat i0 &&
+            in(2)._type instanceof TypeFloat i1) {
             if (i0.isConstant() && i1.isConstant())
-                return SONTypeFloat.constant(i0.value()-i1.value());
+                return TypeFloat.constant(i0.value()-i1.value());
         }
         return in(1)._type.meet(in(2)._type);
     }
@@ -32,10 +32,10 @@ public SONType compute() {
     @Override
     public Node idealize() {
         Node lhs = in(1);
-        SONType t2 = in(2)._type;
+        Type t2 = in(2)._type;
 
         // Sub of 0.
-        if ( t2.isConstant() && t2 instanceof SONTypeFloat i && i.value()==0 )
+        if ( t2.isConstant() && t2 instanceof TypeFloat i && i.value()==0 )
             return lhs;
 
         return null;
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/SubNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/SubNode.java
index e779260..151de7f 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/SubNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/SubNode.java
@@ -1,7 +1,7 @@
 package com.compilerprogramming.ezlang.compiler.nodes;
 
-import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeInteger;
+import com.compilerprogramming.ezlang.compiler.sontypes.Type;
+import com.compilerprogramming.ezlang.compiler.sontypes.TypeInteger;
 import java.util.BitSet;
 
 public class SubNode extends Node {
@@ -19,25 +19,25 @@ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
     }
 
     @Override
-    public SONType compute() {
-        SONType t1 = in(1)._type, t2 = in(2)._type;
+    public Type compute() {
+        Type t1 = in(1)._type, t2 = in(2)._type;
         if( t1.isHigh() || t2.isHigh() )
-            return SONTypeInteger.TOP;
+            return TypeInteger.TOP;
         // Sub of same is 0
         if( in(1)==in(2) )
-            return SONTypeInteger.ZERO;
-        if( t1 instanceof SONTypeInteger i1 &&
-            t2 instanceof SONTypeInteger i2 ) {
+            return TypeInteger.ZERO;
+        if( t1 instanceof TypeInteger i1 &&
+            t2 instanceof TypeInteger i2 ) {
             if (i1.isConstant() && i2.isConstant())
-                return SONTypeInteger.constant(i1.value()-i2.value());
+                return TypeInteger.constant(i1.value()-i2.value());
             // Fold ranges like {2-3} - {0-1} into {1-3}.
             if( !AddNode.overflow(i1._min,-i2._max) &&
                 !AddNode.overflow(i1._max,-i2._min) &&
                 i2._min != Long.MIN_VALUE  )
-                return SONTypeInteger.make(i1._min-i2._max,i1._max-i2._min);
+                return TypeInteger.make(i1._min-i2._max,i1._max-i2._min);
         }
 
-        return SONTypeInteger.BOT;
+        return TypeInteger.BOT;
     }
 
     @Override
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ToFloatNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ToFloatNode.java
index 439f25f..d61ad7e 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ToFloatNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ToFloatNode.java
@@ -1,8 +1,8 @@
 package com.compilerprogramming.ezlang.compiler.nodes;
 
-import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFloat;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeInteger;
+import com.compilerprogramming.ezlang.compiler.sontypes.Type;
+import com.compilerprogramming.ezlang.compiler.sontypes.TypeFloat;
+import com.compilerprogramming.ezlang.compiler.sontypes.TypeInteger;
 
 import java.util.BitSet;
 
@@ -19,14 +19,14 @@ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
     }
 
     @Override
-    public SONType compute() {
-        if( in(1)._type == SONType.NIL ) return SONTypeFloat.FZERO;
-        if (in(1)._type instanceof SONTypeInteger i0 ) {
-            if( i0.isHigh() ) return SONTypeFloat.F64.dual();
+    public Type compute() {
+        if( in(1)._type == Type.NIL ) return TypeFloat.FZERO;
+        if (in(1)._type instanceof TypeInteger i0 ) {
+            if( i0.isHigh() ) return TypeFloat.F64.dual();
             if( i0.isConstant() )
-                return SONTypeFloat.constant(i0.value());
+                return TypeFloat.constant(i0.value());
         }
-        return SONTypeFloat.F64;
+        return TypeFloat.F64;
     }
 
     @Override public Node idealize() { return null; }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/XCtrlNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/XCtrlNode.java
index 94fd704..7afb780 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/XCtrlNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/XCtrlNode.java
@@ -1,7 +1,7 @@
 package com.compilerprogramming.ezlang.compiler.nodes;
 
 import com.compilerprogramming.ezlang.compiler.codegen.CodeGen;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
+import com.compilerprogramming.ezlang.compiler.sontypes.Type;
 import java.util.BitSet;
 
 public class XCtrlNode extends CFGNode {
@@ -9,6 +9,6 @@ public class XCtrlNode extends CFGNode {
     @Override public String label() { return "Xctrl"; }
     @Override public StringBuilder _print1(StringBuilder sb, BitSet visited) { return sb.append("Xctrl"); }
     @Override public boolean isConst() { return true; }
-    @Override  public SONType compute() { return SONType.XCONTROL; }
+    @Override  public Type compute() { return Type.XCONTROL; }
     @Override public Node idealize() { return null; }
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/XorNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/XorNode.java
index 1840851..3595b56 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/XorNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/XorNode.java
@@ -1,7 +1,7 @@
 package com.compilerprogramming.ezlang.compiler.nodes;
 
-import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeInteger;
+import com.compilerprogramming.ezlang.compiler.sontypes.Type;
+import com.compilerprogramming.ezlang.compiler.sontypes.TypeInteger;
 
 public class XorNode extends LogicalNode {
     public XorNode(Node lhs, Node rhs) { super(lhs, rhs); }
@@ -12,28 +12,28 @@ public class XorNode extends LogicalNode {
     @Override public String glabel() { return "^"; }
 
     @Override
-    public SONType compute() {
-        SONType t1 = in(1)._type, t2 = in(2)._type;
+    public Type compute() {
+        Type t1 = in(1)._type, t2 = in(2)._type;
         if( t1.isHigh() || t2.isHigh() )
-            return SONTypeInteger.TOP;
-        if( t1 instanceof SONTypeInteger i0 &&
-            t2 instanceof SONTypeInteger i1 ) {
+            return TypeInteger.TOP;
+        if( t1 instanceof TypeInteger i0 &&
+            t2 instanceof TypeInteger i1 ) {
             if( i0.isConstant() && i1.isConstant() )
-                return SONTypeInteger.constant(i0.value()^i1.value());
+                return TypeInteger.constant(i0.value()^i1.value());
         }
-        return SONTypeInteger.BOT;
+        return TypeInteger.BOT;
     }
 
     @Override
     public Node idealize() {
         Node lhs = in(1);
         Node rhs = in(2);
-        SONType t1 = lhs._type;
-        SONType t2 = rhs._type;
+        Type t1 = lhs._type;
+        Type t2 = rhs._type;
 
         // Xor of 0.  We do not check for (0^x) because this will already
         // canonicalize to (x^0)
-        if( t2.isConstant() && t2 instanceof SONTypeInteger i && i.value()==0 )
+        if( t2.isConstant() && t2 instanceof TypeInteger i && i.value()==0 )
             return lhs;
 
         // Move constants to RHS: con*arg becomes arg*con
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CallARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CallARM.java
index f85e62d..86b53ef 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CallARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CallARM.java
@@ -3,13 +3,13 @@
 import com.compilerprogramming.ezlang.compiler.*;
 import com.compilerprogramming.ezlang.compiler.codegen.*;
 import com.compilerprogramming.ezlang.compiler.nodes.*;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFunPtr;
+import com.compilerprogramming.ezlang.compiler.sontypes.TypeFunPtr;
 
 public class CallARM extends CallNode implements MachNode, RIPRelSize {
-    final SONTypeFunPtr _tfp;
+    final TypeFunPtr _tfp;
     final String _name;
 
-    CallARM(CallNode call, SONTypeFunPtr tfp) {
+    CallARM(CallNode call, TypeFunPtr tfp) {
         super(call);
         _inputs.pop(); // Pop constant target
         assert tfp.isConstant();
@@ -20,7 +20,7 @@ public class CallARM extends CallNode implements MachNode, RIPRelSize {
     @Override public String op() { return "call"; }
     @Override public String label() { return op(); }
     @Override public String name() { return _name; }
-    @Override public SONTypeFunPtr tfp() { return _tfp; }
+    @Override public TypeFunPtr tfp() { return _tfp; }
     @Override public RegMask regmap(int i) { return arm.callInMask(_tfp,i,fun()._maxArgSlot); }
     @Override public RegMask outregmap() { return null; }
 
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/FloatARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/FloatARM.java
index db04638..5d1b14a 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/FloatARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/FloatARM.java
@@ -1,11 +1,10 @@
 package com.compilerprogramming.ezlang.compiler.nodes.cpus.arm;
 
 import com.compilerprogramming.ezlang.compiler.SB;
-import com.compilerprogramming.ezlang.compiler.Utils;
 import com.compilerprogramming.ezlang.compiler.codegen.*;
 import com.compilerprogramming.ezlang.compiler.nodes.ConstantNode;
 import com.compilerprogramming.ezlang.compiler.nodes.MachNode;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFloat;
+import com.compilerprogramming.ezlang.compiler.sontypes.TypeFloat;
 
 //FMOV (scalar, immediate)
 //Floating-point move immediate.
@@ -20,7 +19,7 @@ public class FloatARM extends ConstantNode implements MachNode, RIPRelSize {
     @Override public void encoding( Encoding enc ) {
         enc.largeConstant(this,_con,0,-1/*TODO: ARM-style ELF patching*/);
         short dst = (short)(enc.reg(this) - arm.D_OFFSET);
-        double d = ((SONTypeFloat)_con).value();
+        double d = ((TypeFloat)_con).value();
         long x = Double.doubleToRawLongBits(d);
         enc.add4(arm.load_pc(arm.OPF_ARM, 0, dst));
     }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/IntARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/IntARM.java
index 9e00093..4190ce4 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/IntARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/IntARM.java
@@ -4,8 +4,8 @@
 import com.compilerprogramming.ezlang.compiler.codegen.*;
 import com.compilerprogramming.ezlang.compiler.nodes.ConstantNode;
 import com.compilerprogramming.ezlang.compiler.nodes.MachNode;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeInteger;
+import com.compilerprogramming.ezlang.compiler.sontypes.Type;
+import com.compilerprogramming.ezlang.compiler.sontypes.TypeInteger;
 
 // Integer constants
 public class IntARM extends ConstantNode implements MachNode {
@@ -20,7 +20,7 @@ public class IntARM extends ConstantNode implements MachNode {
 
     @Override public void encoding( Encoding enc ) {
         short self = enc.reg(this);
-        long x = _con==SONType.NIL ? 0 : ((SONTypeInteger)_con).value();
+        long x = _con== Type.NIL ? 0 : ((TypeInteger)_con).value();
         int nb0 = 0;
         int nb1 = 0;
         // Count number of 0000 and FFFF blocks
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LoadARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LoadARM.java
index eff01e0..66bf11b 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LoadARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LoadARM.java
@@ -4,7 +4,7 @@
 import com.compilerprogramming.ezlang.compiler.codegen.*;
 import com.compilerprogramming.ezlang.compiler.nodes.LoadNode;
 import com.compilerprogramming.ezlang.compiler.nodes.Node;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFloat;
+import com.compilerprogramming.ezlang.compiler.sontypes.TypeFloat;
 
 // Load memory addressing on ARM
 // Support imm, reg(direct), or reg+off(indirect) addressing
@@ -21,8 +21,8 @@ public class LoadARM extends MemOpARM {
 
     private static final int[] OP_LOADS  = new int[]{ arm.OP_LOAD_IMM_8,  arm.OP_LOAD_IMM_16,  arm.OP_LOAD_IMM_32,  arm.OP_LOAD_IMM_64, };
     private int imm_op() {
-        return _declaredType == SONTypeFloat.F32 ? arm.OPF_LOAD_IMM_32
-            :  _declaredType == SONTypeFloat.F64 ? arm.OPF_LOAD_IMM_64
+        return _declaredType == TypeFloat.F32 ? arm.OPF_LOAD_IMM_32
+            :  _declaredType == TypeFloat.F64 ? arm.OPF_LOAD_IMM_64
             :  OP_LOADS[_declaredType.log_size()];
     }
 
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/MemOpARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/MemOpARM.java
index fcb5986..d83916e 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/MemOpARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/MemOpARM.java
@@ -15,7 +15,7 @@ public abstract class MemOpARM extends MemOpNode implements MachNode {
     final char _sz = (char)('0'+(1<<_declaredType.log_size()));
     MemOpARM(MemOpNode mop, Node ptr, Node idx, int off, int imm) {
         super(mop,mop);
-        assert ptr._type instanceof SONTypeMemPtr && !(ptr instanceof AddNode);
+        assert ptr._type instanceof TypeMemPtr && !(ptr instanceof AddNode);
         assert ptr() == ptr;
         _inputs.setX(2, ptr);
         _inputs.setX(3, idx);
@@ -34,7 +34,7 @@ public abstract class MemOpARM extends MemOpNode implements MachNode {
 
     @Override public  StringBuilder _printMach(StringBuilder sb, BitSet visited) { return sb.append(".").append(_name); }
 
-    @Override public SONType compute() { throw Utils.TODO(); }
+    @Override public Type compute() { throw Utils.TODO(); }
     @Override public Node idealize() { throw Utils.TODO(); }
 
     int size() { return 1<<_declaredType.log_size(); }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/NegARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/NegARM.java
index fa76b32..02d6ef0 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/NegARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/NegARM.java
@@ -4,7 +4,6 @@
 import com.compilerprogramming.ezlang.compiler.codegen.*;
 import com.compilerprogramming.ezlang.compiler.nodes.*;
 import com.compilerprogramming.ezlang.compiler.nodes.MachConcreteNode;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeInteger;
 
 public class NegARM extends MachConcreteNode implements MachNode {
     NegARM(Node sub) {
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/StoreARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/StoreARM.java
index 283263b..ac0909e 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/StoreARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/StoreARM.java
@@ -4,7 +4,7 @@
 import com.compilerprogramming.ezlang.compiler.codegen.*;
 import com.compilerprogramming.ezlang.compiler.nodes.StoreNode;
 import com.compilerprogramming.ezlang.compiler.nodes.Node;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFloat;
+import com.compilerprogramming.ezlang.compiler.sontypes.TypeFloat;
 
 // Store memory addressing on ARM
 // Support imm, reg(direct), or reg+off(indirect) addressing
@@ -25,8 +25,8 @@ public class StoreARM extends MemOpARM {
 
     private static final int[] OP_STORES = new int[]{ arm.OP_STORE_IMM_8, arm.OP_STORE_IMM_16, arm.OP_STORE_IMM_32, arm.OP_STORE_IMM_64, };
     private int imm_op() {
-        return _declaredType == SONTypeFloat.F32 ? arm.OPF_STORE_IMM_32
-            :  _declaredType == SONTypeFloat.F64 ? arm.OPF_STORE_IMM_64
+        return _declaredType == TypeFloat.F32 ? arm.OPF_STORE_IMM_32
+            :  _declaredType == TypeFloat.F64 ? arm.OPF_STORE_IMM_64
             :  OP_STORES[_declaredType.log_size()];
     }
 
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SubIARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SubIARM.java
index 2102c14..d73969b 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SubIARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SubIARM.java
@@ -5,7 +5,6 @@
 
 import com.compilerprogramming.ezlang.compiler.*;
 import com.compilerprogramming.ezlang.compiler.nodes.*;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeInteger;
 
 public class SubIARM extends MachConcreteNode implements MachNode {
     final int _imm;
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/TFPARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/TFPARM.java
index 757c809..eda874e 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/TFPARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/TFPARM.java
@@ -4,8 +4,6 @@
 import com.compilerprogramming.ezlang.compiler.codegen.*;
 import com.compilerprogramming.ezlang.compiler.nodes.ConstantNode;
 import com.compilerprogramming.ezlang.compiler.nodes.MachNode;
-import com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv.riscv;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFunPtr;
 
 public class TFPARM extends ConstantNode implements MachNode, RIPRelSize {
     TFPARM( ConstantNode con ) { super(con); }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/UJmpARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/UJmpARM.java
index 6405475..fa1dfab 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/UJmpARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/UJmpARM.java
@@ -3,7 +3,7 @@
 import com.compilerprogramming.ezlang.compiler.*;
 import com.compilerprogramming.ezlang.compiler.codegen.*;
 import com.compilerprogramming.ezlang.compiler.nodes.*;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
+import com.compilerprogramming.ezlang.compiler.sontypes.Type;
 import java.util.BitSet;
 
 // unconditional jump
@@ -16,7 +16,7 @@ public class UJmpARM extends CFGNode implements MachNode, RIPRelSize {
     }
     @Override public RegMask regmap(int i) {return null; }
     @Override public RegMask outregmap() { return null; }
-    @Override public SONType compute() { throw Utils.TODO(); }
+    @Override public Type compute() { throw Utils.TODO(); }
     @Override public Node idealize() { throw Utils.TODO(); }
     @Override public void encoding( Encoding enc ) {
         enc.jump(this,uctrl());
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/arm.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/arm.java
index e9b6644..e56b593 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/arm.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/arm.java
@@ -252,12 +252,12 @@ public enum COND {
     }
 
     // True if signed 9-bit immediate
-    private static boolean imm9(SONTypeInteger ti) {
+    private static boolean imm9(TypeInteger ti) {
         // 55 = 64-9
         return ti.isConstant() && ((ti.value()<<55)>>55) == ti.value();
     }
     // True if signed 12-bit immediate
-    private static boolean imm12(SONTypeInteger ti) {
+    private static boolean imm12(TypeInteger ti) {
         // 52 = 64-12
         return ti.isConstant() && ((ti.value()<<52)>>52) == ti.value();
     }
@@ -265,7 +265,7 @@ private static boolean imm12(SONTypeInteger ti) {
 
     // Can we encode this in ARM's 12-bit LOGICAL immediate form?
     // Some combination of shifted bit-masks.
-    private static int imm12Logical(SONTypeInteger ti) {
+    private static int imm12Logical(TypeInteger ti) {
         if( !ti.isConstant() ) return -1;
         if( !ti.isConstant() ) return -1;
         long val = ti.value();
@@ -563,17 +563,17 @@ public static int b_calloc(int opcode, int delta) {
         return (opcode << 26) | delta;
     }
 
-    @Override public RegMask callArgMask(SONTypeFunPtr tfp, int idx, int maxArgSlot ) { return callInMask(tfp,idx,maxArgSlot); }
-    static RegMask callInMask(SONTypeFunPtr tfp, int idx, int maxArgSlot ) {
+    @Override public RegMask callArgMask(TypeFunPtr tfp, int idx, int maxArgSlot ) { return callInMask(tfp,idx,maxArgSlot); }
+    static RegMask callInMask(TypeFunPtr tfp, int idx, int maxArgSlot ) {
         if( idx==0 ) return CodeGen.CODE._rpcMask;
         if( idx==1 ) return null;
         // Count floats in signature up to index
         int fcnt=0;
         for( int i=2; i<idx; i++ )
-            if( tfp.arg(i-2) instanceof SONTypeFloat)
+            if( tfp.arg(i-2) instanceof TypeFloat)
                 fcnt++;
         // Floats up to XMMS in XMM registers
-        if( tfp.arg(idx-2) instanceof SONTypeFloat ) {
+        if( tfp.arg(idx-2) instanceof TypeFloat) {
             if( fcnt < XMMS.length )
                 return XMMS[fcnt];
         } else {
@@ -608,10 +608,10 @@ public static long decodeImm12(int imm12) {
     }
 
     // Return the max stack slot used by this signature, or 0
-    @Override public short maxArgSlot( SONTypeFunPtr tfp ) {
+    @Override public short maxArgSlot( TypeFunPtr tfp ) {
         int icnt=0, fcnt=0;     // Count of ints, floats
         for( int i=0; i<tfp.nargs(); i++ ) {
-            if( tfp.arg(i) instanceof SONTypeFloat ) fcnt++;
+            if( tfp.arg(i) instanceof TypeFloat) fcnt++;
             else icnt++;
         }
         int nstk = Math.max(icnt-8,0)+Math.max(fcnt-8,0);
@@ -628,8 +628,8 @@ public static long decodeImm12(int imm12) {
     static final long CALLER_SAVE = ~CALLEE_SAVE & ~(1L<<RSP);
     @Override public long callerSave() { return CALLER_SAVE; }
     @Override public long neverSave() { return 1L<<RSP; }
-    @Override public RegMask retMask( SONTypeFunPtr tfp ) {
-        return tfp.ret() instanceof SONTypeFloat ? D0_MASK : X0_MASK;
+    @Override public RegMask retMask( TypeFunPtr tfp ) {
+        return tfp.ret() instanceof TypeFloat ? D0_MASK : X0_MASK;
     }
     @Override public int rpc() { return X30; }
 
@@ -694,7 +694,7 @@ private Node cmp(BoolNode bool){
     private Node _cmp(BoolNode bool) {
         if( bool.isFloat() )
             return new CmpFARM(bool);
-        return bool.in(2) instanceof ConstantNode con && con._con instanceof SONTypeInteger ti
+        return bool.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti
                 ? new CmpIARM(bool, (int)ti.value())
                 : new CmpARM(bool);
     }
@@ -710,18 +710,18 @@ private Node jmp(IfNode iff) {
         String op = "!=";
         if( iff.in(1) instanceof BoolNode bool ) op = bool.op();
         else if( iff.in(1)==null ) op = "=="; // Never-node cutout
-        else iff.setDef(1, new BoolNode.NE(iff.in(1), new ConstantNode(SONTypeInteger.ZERO)));
+        else iff.setDef(1, new BoolNode.NE(iff.in(1), new ConstantNode(TypeInteger.ZERO)));
         return new BranchARM(iff, op);
     }
 
     private Node add(AddNode add) {
-        return add.in(2) instanceof ConstantNode off && off._con instanceof SONTypeInteger ti && imm12(ti)
+        return add.in(2) instanceof ConstantNode off && off._con instanceof TypeInteger ti && imm12(ti)
                 ? new AddIARM(add, (int)ti.value())
                 : new AddARM(add);
     }
 
     private Node sub(SubNode sub) {
-        return sub.in(2) instanceof ConstantNode con && con._con instanceof SONTypeInteger ti && imm12(ti)
+        return sub.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti)
                 ? new SubIARM(sub, (int)(ti.value()))
                 : new SubARM(sub);
     }
@@ -729,57 +729,57 @@ private Node sub(SubNode sub) {
     private Node con( ConstantNode con ) {
         if( !con._con.isConstant() ) return new ConstantNode( con ); // Default unknown caller inputs
         return switch( con._con ) {
-            case SONTypeInteger ti  -> new IntARM(con);
-            case SONTypeFloat   tf  -> new FloatARM(con);
-            case SONTypeFunPtr  tfp -> new TFPARM(con);
-            case SONTypeMemPtr tmp -> new ConstantNode(con);
-            case SONTypeNil tn  -> throw Utils.TODO();
+            case TypeInteger ti  -> new IntARM(con);
+            case TypeFloat tf  -> new FloatARM(con);
+            case TypeFunPtr tfp -> new TFPARM(con);
+            case TypeMemPtr tmp -> new ConstantNode(con);
+            case TypeNil tn  -> throw Utils.TODO();
             // TOP, BOTTOM, XCtrl, Ctrl, etc.  Never any executable code.
-            case SONType t -> t==SONType.NIL ? new IntARM(con) : new ConstantNode(con);
+            case Type t -> t== Type.NIL ? new IntARM(con) : new ConstantNode(con);
         };
     }
 
     private Node call(CallNode call){
-        return call.fptr() instanceof ConstantNode con && con._con instanceof SONTypeFunPtr tfp
+        return call.fptr() instanceof ConstantNode con && con._con instanceof TypeFunPtr tfp
                 ? new CallARM(call, tfp)
                 : new CallRRARM(call);
     }
 
     private Node or(OrNode or) {
         int imm12;
-        return or.in(2) instanceof ConstantNode off && off._con instanceof SONTypeInteger ti && (imm12 = imm12Logical(ti)) != -1
+        return or.in(2) instanceof ConstantNode off && off._con instanceof TypeInteger ti && (imm12 = imm12Logical(ti)) != -1
                 ? new OrIARM(or, imm12)
                 : new OrARM(or);
     }
 
     private Node xor(XorNode xor) {
         int imm12;
-        return xor.in(2) instanceof ConstantNode off && off._con instanceof SONTypeInteger ti && (imm12 = imm12Logical(ti)) != -1
+        return xor.in(2) instanceof ConstantNode off && off._con instanceof TypeInteger ti && (imm12 = imm12Logical(ti)) != -1
                 ? new XorIARM(xor, imm12)
                 : new XorARM(xor);
     }
 
     private Node and(AndNode and) {
         int imm12;
-        return and.in(2) instanceof ConstantNode off && off._con instanceof SONTypeInteger ti && (imm12 = imm12Logical(ti)) != -1
+        return and.in(2) instanceof ConstantNode off && off._con instanceof TypeInteger ti && (imm12 = imm12Logical(ti)) != -1
                 ? new AndIARM(and, imm12)
                 : new AndARM(and);
     }
 
     private Node asr(SarNode asr) {
-        return asr.in(2) instanceof ConstantNode off && off._con instanceof SONTypeInteger ti && ti.value() >= 0 && ti.value() < 63
+        return asr.in(2) instanceof ConstantNode off && off._con instanceof TypeInteger ti && ti.value() >= 0 && ti.value() < 63
                 ? new AsrIARM(asr, (int)ti.value())
                 : new AsrARM(asr);
     }
 
     private Node lsl(ShlNode lsl) {
-        return lsl.in(2)  instanceof ConstantNode off && off._con instanceof SONTypeInteger ti && ti.value() >= 0 && ti.value() < 63
+        return lsl.in(2)  instanceof ConstantNode off && off._con instanceof TypeInteger ti && ti.value() >= 0 && ti.value() < 63
                 ? new LslIARM(lsl, (int)ti.value())
                 : new LslARM(lsl);
     }
 
     private Node lsr(ShrNode lsr) {
-        return lsr.in(2)  instanceof ConstantNode off && off._con instanceof SONTypeInteger ti && ti.value() >= 0 && ti.value() < 63
+        return lsr.in(2)  instanceof ConstantNode off && off._con instanceof TypeInteger ti && ti.value() >= 0 && ti.value() < 63
                 ? new LsrIARM(lsr, (int)ti.value())
                 : new LsrARM(lsr);
     }
@@ -800,8 +800,8 @@ private <N extends MemOpNode> N address(N mop ) {
         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 SONTypeMemPtr; // Base ptr always, not some derived
-        if( mop.off() instanceof ConstantNode con && con._con instanceof SONTypeInteger ti && imm9(ti) ) {
+        assert !(base instanceof AddNode) && base._type instanceof TypeMemPtr; // Base ptr always, not some derived
+        if( mop.off() instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm9(ti) ) {
             off = (int)ti.value();
             assert off == ti.value(); // In 32-bit range
         } else {
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/AUIPC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/AUIPC.java
index 8691f78..6dd5f28 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/AUIPC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/AUIPC.java
@@ -4,11 +4,11 @@
 import com.compilerprogramming.ezlang.compiler.codegen.*;
 import com.compilerprogramming.ezlang.compiler.nodes.ConstantNode;
 import com.compilerprogramming.ezlang.compiler.nodes.MachNode;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
+import com.compilerprogramming.ezlang.compiler.sontypes.Type;
 
 // Add upper 20bits to PC.  Immediate comes from the relocation info.
 public class AUIPC extends ConstantNode implements MachNode, RIPRelSize {
-    AUIPC( SONType tfp ) { super(tfp); }
+    AUIPC( Type tfp ) { super(tfp); }
     @Override public RegMask regmap(int i) { return null; }
     @Override public RegMask outregmap() { return riscv.WMASK; }
     @Override public boolean isClone() { return true; }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CallRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CallRISC.java
index 457ca77..a552e4c 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CallRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CallRISC.java
@@ -3,12 +3,12 @@
 import com.compilerprogramming.ezlang.compiler.*;
 import com.compilerprogramming.ezlang.compiler.codegen.*;
 import com.compilerprogramming.ezlang.compiler.nodes.*;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFunPtr;
+import com.compilerprogramming.ezlang.compiler.sontypes.TypeFunPtr;
 
 public class CallRISC extends CallNode implements MachNode, RIPRelSize {
-    final SONTypeFunPtr _tfp;
+    final TypeFunPtr _tfp;
     final String _name;
-    CallRISC( CallNode call, SONTypeFunPtr tfp ) {
+    CallRISC( CallNode call, TypeFunPtr tfp ) {
         super(call);
         assert tfp.isConstant();
         _inputs.pop(); // Pop constant target
@@ -23,7 +23,7 @@ public class CallRISC extends CallNode implements MachNode, RIPRelSize {
     }
     @Override public RegMask outregmap() { return null; }
     @Override public String name() { return _name; }
-    @Override public SONTypeFunPtr tfp() { return _tfp; }
+    @Override public TypeFunPtr tfp() { return _tfp; }
 
     @Override public void encoding( Encoding enc ) {
         // Short form +/-4K:  beq r0,r0,imm12
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/FltRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/FltRISC.java
index f9dd6e6..31d0f1b 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/FltRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/FltRISC.java
@@ -1,11 +1,9 @@
 package com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv;
 
 import com.compilerprogramming.ezlang.compiler.SB;
-import com.compilerprogramming.ezlang.compiler.Utils;
 import com.compilerprogramming.ezlang.compiler.codegen.*;
 import com.compilerprogramming.ezlang.compiler.nodes.ConstantNode;
 import com.compilerprogramming.ezlang.compiler.nodes.MachNode;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFloat;
 
 public class FltRISC extends ConstantNode implements MachNode, RIPRelSize {
     FltRISC(ConstantNode con) { super(con); }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/ImmRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/ImmRISC.java
index 150a8b9..814b984 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/ImmRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/ImmRISC.java
@@ -3,7 +3,7 @@
 import com.compilerprogramming.ezlang.compiler.*;
 import com.compilerprogramming.ezlang.compiler.codegen.*;
 import com.compilerprogramming.ezlang.compiler.nodes.*;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeInteger;
+import com.compilerprogramming.ezlang.compiler.sontypes.TypeInteger;
 import java.util.BitSet;
 
 abstract public class ImmRISC extends MachConcreteNode implements MachNode {
@@ -49,6 +49,6 @@ abstract public class ImmRISC extends MachConcreteNode implements MachNode {
         int imm12 = (_imm12<<20)>>20; // Sign extend 12 bits
         sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" ").p(glabel()).p(" #").p(imm12);
         if( in(1) instanceof LUI lui )
-            sb.p(" // #").hex4((int)(((SONTypeInteger)lui._con).value()) + imm12);
+            sb.p(" // #").hex4((int)(((TypeInteger)lui._con).value()) + imm12);
     }
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/IntRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/IntRISC.java
index d01a11d..905800f 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/IntRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/IntRISC.java
@@ -4,8 +4,8 @@
 import com.compilerprogramming.ezlang.compiler.codegen.*;
 import com.compilerprogramming.ezlang.compiler.nodes.ConstantNode;
 import com.compilerprogramming.ezlang.compiler.nodes.MachNode;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeInteger;
+import com.compilerprogramming.ezlang.compiler.sontypes.Type;
+import com.compilerprogramming.ezlang.compiler.sontypes.TypeInteger;
 
 // 12-bit integer constant.  Larger constants are made up in the instruction
 // selection by adding with a LUI.
@@ -18,7 +18,7 @@ public class IntRISC extends ConstantNode implements MachNode {
     @Override public IntRISC copy() { return new IntRISC(this); }
     @Override public void encoding( Encoding enc ) {
         short dst  = enc.reg(this);
-        int val = _con==SONType.NIL ? 0 : (int)(((SONTypeInteger)_con).value() & 0xFFF);
+        int val = _con== Type.NIL ? 0 : (int)(((TypeInteger)_con).value() & 0xFFF);
         // Explicit truncation of larger immediates; this will sign-extend on
         // load and this is handled during instruction selection.
         enc.add4(riscv.i_type(riscv.OP_IMM, dst, 0, riscv.ZERO, val));
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/LUI.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/LUI.java
index e63e001..a804ba8 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/LUI.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/LUI.java
@@ -4,21 +4,21 @@
 import com.compilerprogramming.ezlang.compiler.codegen.*;
 import com.compilerprogramming.ezlang.compiler.nodes.ConstantNode;
 import com.compilerprogramming.ezlang.compiler.nodes.MachNode;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeInteger;
+import com.compilerprogramming.ezlang.compiler.sontypes.TypeInteger;
 
 // Load upper 20bits.
 public class LUI extends ConstantNode implements MachNode {
     public LUI( int imm20 ) {
-        super(SONTypeInteger.constant(imm20));
-        assert riscv.imm20Exact((SONTypeInteger)_con);
+        super(TypeInteger.constant(imm20));
+        assert riscv.imm20Exact((TypeInteger)_con);
     }
     @Override public RegMask regmap(int i) { return null; }
     @Override public RegMask outregmap() { return riscv.WMASK; }
     @Override public boolean isClone() { return true; }
-    @Override public LUI copy() { return new LUI((int)((SONTypeInteger)_con).value()); }
+    @Override public LUI copy() { return new LUI((int)((TypeInteger)_con).value()); }
     @Override public String op() { return "lui"; }
     @Override public void encoding( Encoding enc ) {
-        long x = ((SONTypeInteger)_con).value();
+        long x = ((TypeInteger)_con).value();
         int imm20 = (int)(x>>12) & 0xFFFFF;
         short dst = enc.reg(this);
         int lui = riscv.u_type(riscv.OP_LUI, dst, imm20);
@@ -27,6 +27,6 @@ public LUI( int imm20 ) {
 
     @Override public void asm(CodeGen code, SB sb) {
         String reg = code.reg(this);
-        sb.p(reg).p(" = #").hex4((int)(((SONTypeInteger)_con).value()));
+        sb.p(reg).p(" = #").hex4((int)(((TypeInteger)_con).value()));
     }
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/MemOpRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/MemOpRISC.java
index 90b6a49..f8d1ddf 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/MemOpRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/MemOpRISC.java
@@ -12,7 +12,7 @@ public abstract class MemOpRISC extends MemOpNode implements MachNode {
     final char _sz;
     MemOpRISC(MemOpNode mop, Node base, int off, Node val) {
         super(mop,mop);
-        assert base._type instanceof SONTypeMemPtr;
+        assert base._type instanceof TypeMemPtr;
         _inputs.setX(2, base ); // Base can be an Add, is no longer raw object base
         _inputs.setX(3, null);  // Never an index
         _inputs.setX(4, val );
@@ -25,7 +25,7 @@ public abstract class MemOpRISC extends MemOpNode implements MachNode {
 
     @Override public StringBuilder _printMach(StringBuilder sb, BitSet visited) { return sb.append(".").append(_name); }
 
-    @Override public SONType compute() { throw Utils.TODO(); }
+    @Override public Type compute() { throw Utils.TODO(); }
     @Override public Node idealize() { throw Utils.TODO(); }
 
     // func3 is based on load/store size and extend
@@ -33,26 +33,26 @@ int func3() {
         int func3 = -1;
         // no unsigned flavour for store, so both signed and unsigned trigger the same
         if(this instanceof StoreRISC) {
-            if( _declaredType == SONTypeInteger. I8 || _declaredType == SONTypeInteger.U8  || _declaredType == SONTypeInteger.BOOL) func3=0; //   SB
-            if( _declaredType == SONTypeInteger.I16 || _declaredType == SONTypeInteger.U16 ) func3=1; // SH
-            if( _declaredType == SONTypeInteger.I32 || _declaredType == SONTypeInteger.U32 || _declaredType instanceof SONTypeMemPtr) func3=2; //  SW
-            if( _declaredType == SONTypeInteger.BOT ) func3=3; //   SD
+            if( _declaredType == TypeInteger. I8 || _declaredType == TypeInteger.U8  || _declaredType == TypeInteger.BOOL) func3=0; //   SB
+            if( _declaredType == TypeInteger.I16 || _declaredType == TypeInteger.U16 ) func3=1; // SH
+            if( _declaredType == TypeInteger.I32 || _declaredType == TypeInteger.U32 || _declaredType instanceof TypeMemPtr) func3=2; //  SW
+            if( _declaredType == TypeInteger.BOT ) func3=3; //   SD
             if( func3 == -1 ) throw Utils.TODO();
             return func3;
         }
-        if( _declaredType == SONTypeInteger. I8 ) func3=0; // LB
-        if( _declaredType == SONTypeInteger.I16 ) func3=1; // LH
-        if( _declaredType == SONTypeInteger.I32 ) func3=2; // LW
-        if( _declaredType == SONTypeInteger.BOT ) func3=3; // LD
-        if( _declaredType == SONTypeInteger. U8 ) func3=4; // LBU
-        if( _declaredType == SONTypeInteger.BOOL) func3=4; // LBU
-        if( _declaredType == SONTypeInteger.U16 ) func3=5; // LHU
-        if( _declaredType == SONTypeInteger.U32 ) func3=6; // LWU
+        if( _declaredType == TypeInteger. I8 ) func3=0; // LB
+        if( _declaredType == TypeInteger.I16 ) func3=1; // LH
+        if( _declaredType == TypeInteger.I32 ) func3=2; // LW
+        if( _declaredType == TypeInteger.BOT ) func3=3; // LD
+        if( _declaredType == TypeInteger. U8 ) func3=4; // LBU
+        if( _declaredType == TypeInteger.BOOL) func3=4; // LBU
+        if( _declaredType == TypeInteger.U16 ) func3=5; // LHU
+        if( _declaredType == TypeInteger.U32 ) func3=6; // LWU
 
         // float
-        if(_declaredType == SONTypeFloat.F32) func3 = 2; // fLW   fSW
-        if(_declaredType == SONTypeFloat.F64) func3 = 3; // fLD   fSD
-        if( _declaredType instanceof SONTypeMemPtr ) func3=3; // 8 byte pointers
+        if(_declaredType == TypeFloat.F32) func3 = 2; // fLW   fSW
+        if(_declaredType == TypeFloat.F64) func3 = 3; // fLD   fSD
+        if( _declaredType instanceof TypeMemPtr) func3=3; // 8 byte pointers
         if( func3 == -1 ) throw Utils.TODO();
         return func3;
     }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/TFPRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/TFPRISC.java
index d04275d..fcdd904 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/TFPRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/TFPRISC.java
@@ -4,7 +4,7 @@
 import com.compilerprogramming.ezlang.compiler.codegen.*;
 import com.compilerprogramming.ezlang.compiler.nodes.ConstantNode;
 import com.compilerprogramming.ezlang.compiler.nodes.MachNode;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFunPtr;
+import com.compilerprogramming.ezlang.compiler.sontypes.TypeFunPtr;
 
 public class TFPRISC extends ConstantNode implements MachNode, RIPRelSize {
     TFPRISC(ConstantNode con) { super(con); }
@@ -17,7 +17,7 @@ public class TFPRISC extends ConstantNode implements MachNode, RIPRelSize {
         enc.relo(this);
         // TODO: 1 op encoding, plus a TODO if it does not fit
         short dst = enc.reg(this);
-        SONTypeFunPtr tfp = (SONTypeFunPtr)_con;
+        TypeFunPtr tfp = (TypeFunPtr)_con;
         // auipc  t0,0
         int auipc = riscv.u_type(riscv.OP_AUIPC, dst, 0);
         // addi   t1,t0 + #0
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/UJmpRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/UJmpRISC.java
index 7ddb3aa..f85809f 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/UJmpRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/UJmpRISC.java
@@ -3,7 +3,7 @@
 import com.compilerprogramming.ezlang.compiler.*;
 import com.compilerprogramming.ezlang.compiler.codegen.*;
 import com.compilerprogramming.ezlang.compiler.nodes.*;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
+import com.compilerprogramming.ezlang.compiler.sontypes.Type;
 import java.util.BitSet;
 
 // unconditional jump
@@ -13,7 +13,7 @@ public class UJmpRISC extends CFGNode implements MachNode, RIPRelSize {
     @Override public StringBuilder _print1( StringBuilder sb, BitSet visited ) { return sb.append("jmp "); }
     @Override public RegMask regmap(int i) {return null; }
     @Override public RegMask outregmap() { return null; }
-    @Override public SONType compute() { throw Utils.TODO(); }
+    @Override public Type compute() { throw Utils.TODO(); }
     @Override public Node idealize() { throw Utils.TODO(); }
     @Override public void encoding( Encoding enc ) {
         // Short form +/-4K:  beq r0,r0,imm12
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/riscv.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/riscv.java
index b051fef..854112f 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/riscv.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/riscv.java
@@ -253,17 +253,17 @@ static public int fsetop(String op) {
         };
     }
 
-    @Override public RegMask callArgMask( SONTypeFunPtr tfp, int idx, int maxArgSlot ) { return callInMask(tfp,idx,maxArgSlot); }
-    static RegMask callInMask( SONTypeFunPtr tfp, int idx, int maxArgSlot ) {
+    @Override public RegMask callArgMask(TypeFunPtr tfp, int idx, int maxArgSlot ) { return callInMask(tfp,idx,maxArgSlot); }
+    static RegMask callInMask(TypeFunPtr tfp, int idx, int maxArgSlot ) {
         if( idx==0 ) return RPC_MASK;
         if( idx==1 ) return null;
         // Count floats in signature up to index
         int fcnt=0;
         for( int i=2; i<idx; i++ )
-            if( tfp.arg(i-2) instanceof SONTypeFloat )
+            if( tfp.arg(i-2) instanceof TypeFloat)
                 fcnt++;
         // Floats up to XMMS in XMM registers
-        if( tfp.arg(idx-2) instanceof SONTypeFloat ) {
+        if( tfp.arg(idx-2) instanceof TypeFloat) {
             if( fcnt < XMMS.length )
                 return XMMS[fcnt];
         } else {
@@ -276,10 +276,10 @@ static RegMask callInMask( SONTypeFunPtr tfp, int idx, int maxArgSlot ) {
         return new RegMask(MAX_REG + 1 + (idx - 2));
     }
 
-    @Override public short maxArgSlot( SONTypeFunPtr tfp ) {
+    @Override public short maxArgSlot( TypeFunPtr tfp ) {
         int icnt=0, fcnt=0;     // Count of ints, floats
         for( int i=0; i<tfp.nargs(); i++ ) {
-            if( tfp.arg(i) instanceof SONTypeFloat ) fcnt++;
+            if( tfp.arg(i) instanceof TypeFloat) fcnt++;
             else icnt++;
         }
         int nstk = Math.max(icnt-8,0)+Math.max(fcnt-8,0);
@@ -296,8 +296,8 @@ static RegMask callInMask( SONTypeFunPtr tfp, int idx, int maxArgSlot ) {
 
     @Override public long callerSave() { return CALLER_SAVE; }
     @Override public long  neverSave() { return (1L<<RSP) | (1L<<ZERO); }
-    @Override public RegMask retMask( SONTypeFunPtr tfp ) {
-        return tfp.ret() instanceof SONTypeFloat ? FA0_MASK : A0_MASK;
+    @Override public RegMask retMask( TypeFunPtr tfp ) {
+        return tfp.ret() instanceof TypeFloat ? FA0_MASK : A0_MASK;
     }
     @Override public int rpc() { return RPC; }
 
@@ -309,12 +309,12 @@ static RegMask callInMask( SONTypeFunPtr tfp, int idx, int maxArgSlot ) {
     @Override  public SplitNode split(String kind, byte round, LRG lrg) { return new SplitRISC(kind,round);  }
 
     // True if signed 12-bit immediate
-    public static boolean imm12(SONTypeInteger ti) {
+    public static boolean imm12(TypeInteger ti) {
         // 52 = 64-12
         return ti.isConstant() && ((ti.value()<<52)>>52) == ti.value();
     }
     // True if HIGH 20-bit signed immediate, with all zeros low.
-    public static boolean imm20Exact(SONTypeInteger ti) {
+    public static boolean imm20Exact(TypeInteger ti) {
         // shift left 32 to clear out the upper 32 bits.
         // shift right SIGNED to sign-extend upper 32 bits; then shift 12 more to clear out lower 12 bits.
         // shift left 12 to re-center the bits.
@@ -371,13 +371,13 @@ private Node addf(AddFNode addf) {
     }
 
     private Node add(AddNode add) {
-        if( add.in(2) instanceof ConstantNode off2 && off2._con instanceof SONTypeInteger ti && imm12(ti) )
+        if( add.in(2) instanceof ConstantNode off2 && off2._con instanceof TypeInteger ti && imm12(ti) )
             return new AddIRISC(add, (int)ti.value(),true);
         return new AddRISC(add);
     }
 
     private Node and(AndNode and) {
-        if( and.in(2) instanceof ConstantNode con && con._con instanceof SONTypeInteger ti ) {
+        if( and.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti ) {
             if( imm12(ti) )
                 return new AndIRISC(and, (int)ti.value());
             // Could be any size low bit mask
@@ -388,7 +388,7 @@ private Node and(AndNode and) {
     }
 
     private Node call(CallNode call) {
-        return call.fptr() instanceof ConstantNode con && con._con instanceof SONTypeFunPtr tfp
+        return call.fptr() instanceof ConstantNode con && con._con instanceof TypeFunPtr tfp
             ? new CallRISC(call, tfp)
             : new CallRRISC(call);
     }
@@ -416,7 +416,7 @@ private Node cmp(BoolNode bool) {
         // we can remove the double XOR in the encodings.
 
         return switch( bool.op() ) {
-        case "<" -> bool.in(2) instanceof ConstantNode con && con._con instanceof SONTypeInteger ti && imm12(ti)
+        case "<" -> bool.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti)
             ? new SetIRISC(bool, (int)ti.value(),false)
             : new  SetRISC(bool, false);
         // x <= y - flip and negate; !(y < x); `slt tmp=y,x; xori dst=tmp,#1`
@@ -430,7 +430,7 @@ private Node cmp(BoolNode bool) {
     private Node con( ConstantNode con ) {
         if( !con._con.isConstant() ) return new ConstantNode( con ); // Default unknown caller inputs
         return switch( con._con ) {
-        case SONTypeInteger ti -> {
+        case TypeInteger ti -> {
             if( imm12(ti) ) yield new IntRISC(con);
             long x = ti.value();
             if( imm20Exact(ti) ) yield new LUI((int)x);
@@ -446,12 +446,12 @@ private Node con( ConstantNode con ) {
             throw Utils.TODO();
         }
         // Load from constant pool
-        case SONTypeFloat   tf  -> new FltRISC(con);
-        case SONTypeFunPtr  tfp -> new TFPRISC(con);
-        case SONTypeMemPtr  tmp -> throw Utils.TODO();
-        case SONTypeNil     tn  -> throw Utils.TODO();
+        case TypeFloat tf  -> new FltRISC(con);
+        case TypeFunPtr tfp -> new TFPRISC(con);
+        case TypeMemPtr tmp -> throw Utils.TODO();
+        case TypeNil tn  -> throw Utils.TODO();
         // TOP, BOTTOM, XCtrl, Ctrl, etc.  Never any executable code.
-        case SONType t -> t==SONType.NIL ? new IntRISC(con) : new ConstantNode(con);
+        case Type t -> t== Type.NIL ? new IntRISC(con) : new ConstantNode(con);
         };
     }
 
@@ -468,43 +468,43 @@ private Node jmp( IfNode iff ) {
     }
 
     private Node or(OrNode or) {
-        if( or.in(2) instanceof ConstantNode con && con._con instanceof SONTypeInteger ti && imm12(ti))
+        if( or.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti))
             return new OrIRISC(or, (int)ti.value());
         return new OrRISC(or);
     }
 
     private Node xor(XorNode xor) {
-        if( xor.in(2) instanceof ConstantNode con && con._con instanceof SONTypeInteger ti && imm12(ti))
+        if( xor.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti))
             return new XorIRISC(xor, (int)ti.value());
         return new XorRISC(xor);
     }
 
     private Node sra(SarNode sar) {
-        if( sar.in(2) instanceof ConstantNode con && con._con instanceof SONTypeInteger ti && imm12(ti))
+        if( sar.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti))
             return new SraIRISC(sar, (int)ti.value());
         return new SraRISC(sar);
     }
 
     private Node srl(ShrNode shr) {
-        if( shr.in(2) instanceof ConstantNode con && con._con instanceof SONTypeInteger ti && imm12(ti))
+        if( shr.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti))
             return new SrlIRISC(shr, (int)ti.value(),true);
         return new SrlRISC(shr);
     }
 
     private Node sll(ShlNode sll) {
-        if( sll.in(2) instanceof ConstantNode con && con._con instanceof SONTypeInteger ti && imm12(ti))
+        if( sll.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti))
             return new SllIRISC(sll, (int)ti.value());
         return new SllRISC(sll);
     }
 
     private Node sub(SubNode sub) {
-        return sub.in(2) instanceof ConstantNode con && con._con instanceof SONTypeInteger ti && imm12(ti)
+        return sub.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti)
             ? new AddIRISC(sub, (int)(-ti.value()),true)
             : new SubRISC(sub);
     }
 
     private Node i2f8(ToFloatNode tfn) {
-        assert tfn.in(1)._type instanceof SONTypeInteger ti;
+        assert tfn.in(1)._type instanceof TypeInteger ti;
         return new I2F8RISC(tfn);
     }
 
@@ -517,7 +517,7 @@ private Node ld(LoadNode ld) {
     }
 
     private Node st(StoreNode st) {
-        Node xval = st.val() instanceof ConstantNode con && con._con == SONTypeInteger.ZERO ? null : st.val();
+        Node xval = st.val() instanceof ConstantNode con && con._con == TypeInteger.ZERO ? null : st.val();
         return new StoreRISC(st,address(st), off, xval);
     }
 
@@ -530,8 +530,8 @@ private Node address( MemOpNode mop ) {
         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 SONTypeMemPtr; // Base ptr always, not some derived
-        if( mop.off() instanceof ConstantNode con && con._con instanceof SONTypeInteger ti && imm12(ti) ) {
+        assert !(base instanceof AddNode) && base._type instanceof TypeMemPtr; // Base ptr always, not some derived
+        if( mop.off() instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti) ) {
             off = (int)ti.value();
         } else {
             base = new AddRISC(base,mop.off());
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CallX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CallX86.java
index ec9b60d..ead9f8b 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CallX86.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CallX86.java
@@ -4,12 +4,12 @@
 import com.compilerprogramming.ezlang.compiler.codegen.*;
 import com.compilerprogramming.ezlang.compiler.nodes.CallNode;
 import com.compilerprogramming.ezlang.compiler.nodes.MachNode;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFunPtr;
+import com.compilerprogramming.ezlang.compiler.sontypes.TypeFunPtr;
 
 public class CallX86 extends CallNode implements MachNode, RIPRelSize {
-    final SONTypeFunPtr _tfp;
+    final TypeFunPtr _tfp;
     final String _name;
-    CallX86( CallNode call, SONTypeFunPtr tfp ) {
+    CallX86( CallNode call, TypeFunPtr tfp ) {
         super(call);
         _inputs.pop(); // Pop constant target
         assert tfp.isConstant();
@@ -19,7 +19,7 @@ public class CallX86 extends CallNode implements MachNode, RIPRelSize {
     @Override public String op() { return "call"; }
     @Override public String label() { return op(); }
     @Override public String name() { return _name; }
-    @Override public SONTypeFunPtr tfp() { return _tfp; }
+    @Override public TypeFunPtr tfp() { return _tfp; }
     @Override public RegMask regmap(int i) { return x86_64_v2.callInMask(_tfp,i,fun()._maxArgSlot);  }
     @Override public RegMask outregmap() { return null; }
     @Override public int nargs() { return nIns()-2; } // Minus control, memory, fptr
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CmpMemX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CmpMemX86.java
index d2bd42a..35c0efe 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CmpMemX86.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CmpMemX86.java
@@ -3,8 +3,8 @@
 import com.compilerprogramming.ezlang.compiler.SB;
 import com.compilerprogramming.ezlang.compiler.codegen.*;
 import com.compilerprogramming.ezlang.compiler.nodes.*;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFloat;
+import com.compilerprogramming.ezlang.compiler.sontypes.Type;
+import com.compilerprogramming.ezlang.compiler.sontypes.TypeFloat;
 
 public class CmpMemX86 extends MemOpX86 {
     final boolean _swap;      // Op switched LHS, RHS
@@ -45,10 +45,10 @@ public class CmpMemX86 extends MemOpX86 {
         }
     }
 
-    static void encVal(Encoding enc, SONType decl, short ptr, short idx, short src, int off, int scale, boolean _swap) {
-        if(decl instanceof SONTypeFloat) {
+    static void encVal(Encoding enc, Type decl, short ptr, short idx, short src, int off, int scale, boolean _swap) {
+        if(decl instanceof TypeFloat) {
             src -= (short)x86_64_v2.XMM_OFFSET;
-            enc.add1(decl==SONTypeFloat.F32 ? 0xF3 : 0xF2).add1(0x0F).add1(0xC2);
+            enc.add1(decl== TypeFloat.F32 ? 0xF3 : 0xF2).add1(0x0F).add1(0xC2);
         } else {
             int log = decl.log_size();
             x86_64_v2.rexF(src, ptr, idx, log == 3, enc);
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/ImmX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/ImmX86.java
index f86e852..7b45ec3 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/ImmX86.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/ImmX86.java
@@ -3,7 +3,6 @@
 import com.compilerprogramming.ezlang.compiler.*;
 import com.compilerprogramming.ezlang.compiler.codegen.*;
 import com.compilerprogramming.ezlang.compiler.nodes.*;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeInteger;
 
 public abstract class ImmX86 extends MachConcreteNode implements MachNode {
     final int _imm;
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/IntX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/IntX86.java
index 6067ac8..cc68d2a 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/IntX86.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/IntX86.java
@@ -3,14 +3,14 @@
 import com.compilerprogramming.ezlang.compiler.*;
 import com.compilerprogramming.ezlang.compiler.codegen.*;
 import com.compilerprogramming.ezlang.compiler.nodes.*;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeInteger;
+import com.compilerprogramming.ezlang.compiler.sontypes.Type;
+import com.compilerprogramming.ezlang.compiler.sontypes.TypeInteger;
 
 // Integer constants
 public class IntX86 extends ConstantNode implements MachNode {
     IntX86( ConstantNode con ) { super(con); }
     @Override public String op() {
-        return _con == SONType.NIL || _con == SONTypeInteger.ZERO ? "xor" : "ldi";
+        return _con == Type.NIL || _con == TypeInteger.ZERO ? "xor" : "ldi";
     }
     @Override public boolean isClone() { return true; }
     @Override public Node copy() { return new IntX86(this); }
@@ -18,13 +18,13 @@ public class IntX86 extends ConstantNode implements MachNode {
     @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
     // Zero-set uses XOR kills flags
     @Override public RegMask killmap() {
-        return _con == SONType.NIL || _con == SONTypeInteger.ZERO ? x86_64_v2.FLAGS_MASK : null;
+        return _con == Type.NIL || _con == TypeInteger.ZERO ? x86_64_v2.FLAGS_MASK : null;
     }
 
     @Override public void encoding( Encoding enc ) {
         short dst = enc.reg(this);
         // Short form for zero
-        if( _con==SONType.NIL || _con==SONTypeInteger.ZERO ) {
+        if( _con== Type.NIL || _con== TypeInteger.ZERO ) {
             // XOR dst,dst.  Can skip REX is dst is low 8, makes this a 32b
             // xor, which will also zero the high bits.
             if( dst >= 8 ) enc.add1(x86_64_v2.rex(dst, dst, 0));
@@ -37,7 +37,7 @@ public class IntX86 extends ConstantNode implements MachNode {
         // Conditional encoding based on 64 or 32 bits
         //REX.W + C7 /0 id	MOV r/m64, imm32
 
-        long imm = ((SONTypeInteger)_con).value();
+        long imm = ((TypeInteger)_con).value();
         if (Integer.MIN_VALUE <= imm && imm < 0) {
             // We need sign extension, so use imm32 into 64 bit register
             // REX.W + C7 /0 id    MOV r/m64, imm32
@@ -68,7 +68,7 @@ public class IntX86 extends ConstantNode implements MachNode {
     // General form: "op\tdst=src+src"
     @Override public void asm(CodeGen code, SB sb) {
         String reg = code.reg(this);
-        if( _con == SONType.NIL || _con == SONTypeInteger.ZERO )
+        if( _con == Type.NIL || _con == TypeInteger.ZERO )
             sb.p(reg).p(",").p(reg);
         else
             _con.print(sb.p(reg).p(" = #"));
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/LoadX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/LoadX86.java
index c35e176..c7f6624 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/LoadX86.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/LoadX86.java
@@ -5,10 +5,9 @@
 import com.compilerprogramming.ezlang.compiler.codegen.*;
 import com.compilerprogramming.ezlang.compiler.nodes.LoadNode;
 import com.compilerprogramming.ezlang.compiler.nodes.Node;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFloat;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeInteger;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeMemPtr;
+import com.compilerprogramming.ezlang.compiler.sontypes.Type;
+import com.compilerprogramming.ezlang.compiler.sontypes.TypeFloat;
+import com.compilerprogramming.ezlang.compiler.sontypes.TypeInteger;
 
 public class LoadX86 extends MemOpX86 {
     LoadX86( LoadNode ld, Node base, Node idx, int off, int scale ) {
@@ -26,23 +25,23 @@ public class LoadX86 extends MemOpX86 {
         enc(enc, _declaredType, dst, ptr, idx, _off, _scale);
     }
 
-    static void enc( Encoding enc, SONType decl, short dst, short ptr, short idx, int off, int scale ) {
-        if( decl == SONTypeFloat.F32) enc.add1(0xF3);
-        if( decl == SONTypeFloat.F64) enc.add1(0xF2);
+    static void enc(Encoding enc, Type decl, short dst, short ptr, short idx, int off, int scale ) {
+        if( decl == TypeFloat.F32) enc.add1(0xF3);
+        if( decl == TypeFloat.F64) enc.add1(0xF2);
 
-        if( decl.isa(SONTypeFloat.F64) )
+        if( decl.isa(TypeFloat.F64) )
             dst -= (short)x86_64_v2.XMM_OFFSET;
 
-        x86_64_v2.rexF(dst, ptr, idx, decl != SONTypeInteger.U32 && decl != SONTypeFloat.F32 && decl != SONTypeFloat.F64, enc);
+        x86_64_v2.rexF(dst, ptr, idx, decl != TypeInteger.U32 && decl != TypeFloat.F32 && decl != TypeFloat.F64, enc);
 
         if( false ) ;
-        else if( decl == SONTypeFloat.F32   ) enc.add1(0x0F).add1(0x10); // F3 0F 10 /r MOVSS xmm1, m32
-        else if( decl == SONTypeFloat.F64   ) enc.add1(0x0F).add1(0x10); // F2 0F 10 /r MOVSD xmm1, m64
-        else if( decl.isa(SONTypeInteger.I8)) enc.add1(0x0F).add1(0xBE); // sign extend: REX.W + 0F BE /r MOVSX r64, r/m8
-        else if( decl == SONTypeInteger.I16 ) enc.add1(0x0F).add1(0xBF); // sign extend: REX.W + 0F BF /r MOVSX r64, r/m16
-        else if( decl == SONTypeInteger.I32 ) enc.add1(0x63);            // sign extend: REX.W + 63 /r    MOVSXD r64, r/m32
-        else if( decl == SONTypeInteger.U8  ) enc.add1(0x0F).add1(0xB6); // zero extend: REX.W + 0F B6 /r MOVZX r64, r/m8
-        else if( decl == SONTypeInteger.U16 ) enc.add1(0x0F).add1(0xB7); // zero extend: REX.W + 0F B7 /r MOVZX r64, r/m16
+        else if( decl == TypeFloat.F32   ) enc.add1(0x0F).add1(0x10); // F3 0F 10 /r MOVSS xmm1, m32
+        else if( decl == TypeFloat.F64   ) enc.add1(0x0F).add1(0x10); // F2 0F 10 /r MOVSD xmm1, m64
+        else if( decl.isa(TypeInteger.I8)) enc.add1(0x0F).add1(0xBE); // sign extend: REX.W + 0F BE /r MOVSX r64, r/m8
+        else if( decl == TypeInteger.I16 ) enc.add1(0x0F).add1(0xBF); // sign extend: REX.W + 0F BF /r MOVSX r64, r/m16
+        else if( decl == TypeInteger.I32 ) enc.add1(0x63);            // sign extend: REX.W + 63 /r    MOVSXD r64, r/m32
+        else if( decl == TypeInteger.U8  ) enc.add1(0x0F).add1(0xB6); // zero extend: REX.W + 0F B6 /r MOVZX r64, r/m8
+        else if( decl == TypeInteger.U16 ) enc.add1(0x0F).add1(0xB7); // zero extend: REX.W + 0F B7 /r MOVZX r64, r/m16
         // Covers U32, I64/BOT, TMP
         else if( decl.log_size()>=2 )     enc.add1(0x8B);            // zero extend:         8B /r    MOV r32, r/m32
         else throw Utils.TODO();
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/MemOpX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/MemOpX86.java
index e6e064d..ca9e4aa 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/MemOpX86.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/MemOpX86.java
@@ -26,7 +26,7 @@ public abstract class MemOpX86 extends MemOpNode implements MachNode {
     final char _sz;             // Handy print name
     MemOpX86( Node op, MemOpNode mop, Node base, Node idx, int off, int scale, int imm ) {
         super(op,mop);
-        assert base._type instanceof SONTypeMemPtr && !(base instanceof AddNode);
+        assert base._type instanceof TypeMemPtr && !(base instanceof AddNode);
         assert (idx==null && scale==0) || (idx!=null && 0<= scale && scale<=3);
 
         // Copy memory parts from e.g. the LoadNode over the opcode, e.g. an Add
@@ -58,7 +58,7 @@ public abstract class MemOpX86 extends MemOpNode implements MachNode {
     }
 
     @Override public String label() { return op(); }
-    @Override public SONType compute() { throw Utils.TODO(); }
+    @Override public Type compute() { throw Utils.TODO(); }
     @Override public Node idealize() { throw Utils.TODO(); }
 
     // Register mask allowed on input i.
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/NewX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/NewX86.java
index aa81518..34198db 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/NewX86.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/NewX86.java
@@ -3,7 +3,6 @@
 import com.compilerprogramming.ezlang.compiler.*;
 import com.compilerprogramming.ezlang.compiler.codegen.*;
 import com.compilerprogramming.ezlang.compiler.nodes.*;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFunPtr;
 
 public class NewX86 extends NewNode implements MachNode {
     // A pre-zeroed chunk of memory.
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/SplitX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/SplitX86.java
index 545d0f1..278dfb2 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/SplitX86.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/SplitX86.java
@@ -1,14 +1,12 @@
 package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2;
 
 import com.compilerprogramming.ezlang.compiler.SB;
-import com.compilerprogramming.ezlang.compiler.Utils;
 import com.compilerprogramming.ezlang.compiler.codegen.*;
 import com.compilerprogramming.ezlang.compiler.nodes.FunNode;
 import com.compilerprogramming.ezlang.compiler.nodes.Node;
 import com.compilerprogramming.ezlang.compiler.nodes.SplitNode;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFloat;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeInteger;
+import com.compilerprogramming.ezlang.compiler.sontypes.TypeFloat;
+import com.compilerprogramming.ezlang.compiler.sontypes.TypeInteger;
 
 public class SplitX86 extends SplitNode {
     SplitX86( String kind, byte round ) { super(kind,round, new Node[2]); }
@@ -59,12 +57,12 @@ public class SplitX86 extends SplitNode {
                 x86_64_v2.indirectAdr(0, (short)-1/*index*/, (short)x86_64_v2.RSP,  off, 0, enc);
                 return;
             }
-            StoreX86.encVal(enc, srcX ? SONTypeFloat.F64 : SONTypeInteger.BOT, (short)x86_64_v2.RSP, (short)-1/*index*/, src, off, 0);
+            StoreX86.encVal(enc, srcX ? TypeFloat.F64 : TypeInteger.BOT, (short)x86_64_v2.RSP, (short)-1/*index*/, src, off, 0);
             return;
         }
         if( src >= x86_64_v2.MAX_REG ) {
             int off = enc._fun.computeStackOffset(enc._code,src);
-            LoadX86.enc(enc, dstX ? SONTypeFloat.F64 : SONTypeInteger.BOT, dst, (short)x86_64_v2.RSP, (short)-1, off, 0);
+            LoadX86.enc(enc, dstX ? TypeFloat.F64 : TypeInteger.BOT, dst, (short)x86_64_v2.RSP, (short)-1, off, 0);
             return;
         }
 
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/StoreX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/StoreX86.java
index 243f48b..aa641c0 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/StoreX86.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/StoreX86.java
@@ -2,11 +2,10 @@
 
 import com.compilerprogramming.ezlang.compiler.SB;
 import com.compilerprogramming.ezlang.compiler.codegen.*;
-import com.compilerprogramming.ezlang.compiler.nodes.ConstantNode;
 import com.compilerprogramming.ezlang.compiler.nodes.Node;
 import com.compilerprogramming.ezlang.compiler.nodes.StoreNode;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFloat;
+import com.compilerprogramming.ezlang.compiler.sontypes.Type;
+
 import java.util.BitSet;
 
 public class StoreX86 extends MemOpX86 {
@@ -52,7 +51,7 @@ public class StoreX86 extends MemOpX86 {
     }
 
     // Non-immediate encoding
-    static void encVal( Encoding enc, SONType decl, short ptr, short idx, short src, int off, int scale ) {
+    static void encVal(Encoding enc, Type decl, short ptr, short idx, short src, int off, int scale ) {
         int log = decl.log_size();
         // Float reg being stored
         if( src >= x86_64_v2.XMM_OFFSET ) {
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/TFPX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/TFPX86.java
index 4861b8d..19034fb 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/TFPX86.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/TFPX86.java
@@ -3,15 +3,13 @@
 import com.compilerprogramming.ezlang.compiler.*;
 import com.compilerprogramming.ezlang.compiler.codegen.*;
 import com.compilerprogramming.ezlang.compiler.nodes.*;
-import com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv.riscv;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFunPtr;
+import com.compilerprogramming.ezlang.compiler.sontypes.Type;
 
 // Function constants
 public class TFPX86 extends ConstantNode implements MachNode, RIPRelSize {
     TFPX86( ConstantNode con ) {  super(con); }
     @Override public String op() {
-        return _con == SONType.NIL ? "xor" : "ldx";
+        return _con == Type.NIL ? "xor" : "ldx";
     }
     @Override public boolean isClone() { return true; }
     @Override public Node copy() { return new TFPX86(this); }
@@ -19,13 +17,13 @@ public class TFPX86 extends ConstantNode implements MachNode, RIPRelSize {
     @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
     // Zero-set uses XOR kills flags
     @Override public RegMask killmap() {
-        return _con == SONType.NIL ? x86_64_v2.FLAGS_MASK : null;
+        return _con == Type.NIL ? x86_64_v2.FLAGS_MASK : null;
     }
 
     @Override public void encoding( Encoding enc ) {
         short dst = enc.reg(this);
         // Short form for zero
-        if( _con==SONType.NIL ) {
+        if( _con== Type.NIL ) {
             // XOR dst,dst.  Can skip REX is dst is low 8, makes this a 32b
             // xor, which will also zero the high bits.
             if( dst >= 8 ) enc.add1(x86_64_v2.rex(dst, dst, 0));
@@ -58,7 +56,7 @@ public class TFPX86 extends ConstantNode implements MachNode, RIPRelSize {
     // General form: "op\tdst=src+src"
     @Override public void asm(CodeGen code, SB sb) {
         String reg = code.reg(this);
-        if( _con == SONType.NIL )
+        if( _con == Type.NIL )
             sb.p(reg).p(",").p(reg);
         else
             _con.print(sb.p(reg).p(" = #"));
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/UJmpX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/UJmpX86.java
index d404bb5..7d05e25 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/UJmpX86.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/UJmpX86.java
@@ -3,7 +3,7 @@
 import com.compilerprogramming.ezlang.compiler.*;
 import com.compilerprogramming.ezlang.compiler.codegen.*;
 import com.compilerprogramming.ezlang.compiler.nodes.*;
-import com.compilerprogramming.ezlang.compiler.sontypes.SONType;
+import com.compilerprogramming.ezlang.compiler.sontypes.Type;
 import java.util.BitSet;
 
 // unconditional jump
@@ -16,7 +16,7 @@ public class UJmpX86 extends CFGNode implements MachNode, RIPRelSize {
     }
     @Override public RegMask regmap(int i) {return null; }
     @Override public RegMask outregmap() { return null; }
-    @Override public SONType compute() { throw Utils.TODO(); }
+    @Override public Type compute() { throw Utils.TODO(); }
     @Override public Node idealize() { throw Utils.TODO(); }
     @Override public void encoding( Encoding enc ) {
         enc.jump(this,uctrl());
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/x86_64_v2.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/x86_64_v2.java
index 547abda..ad134b5 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/x86_64_v2.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/x86_64_v2.java
@@ -185,8 +185,8 @@ public static void indirectAdr( int scale, short index, short base, int offset,
 
     // Map from function signature and argument index to register.
     // Used to set input registers to CallNodes, and ParmNode outputs.
-    @Override public RegMask callArgMask( SONTypeFunPtr tfp, int idx, int maxArgSlot ) { return callInMask(tfp,idx,maxArgSlot); }
-    static RegMask callInMask( SONTypeFunPtr tfp, int idx, int maxArgSlot ) {
+    @Override public RegMask callArgMask(TypeFunPtr tfp, int idx, int maxArgSlot ) { return callInMask(tfp,idx,maxArgSlot); }
+    static RegMask callInMask(TypeFunPtr tfp, int idx, int maxArgSlot ) {
         if( idx==0 ) return RPC_MASK;
         if( idx==1 ) return null;
         return switch( CodeGen.CODE._callingConv ) {
@@ -197,7 +197,7 @@ static RegMask callInMask( SONTypeFunPtr tfp, int idx, int maxArgSlot ) {
     }
 
     // Maximum stack args used by this signature
-    @Override public short maxArgSlot( SONTypeFunPtr tfp ) {
+    @Override public short maxArgSlot( TypeFunPtr tfp ) {
         return switch( CodeGen.CODE._callingConv ) {
         case "SystemV" -> maxArgSlotSys5 (tfp);
         case "Win64"   -> maxArgSlotWin64(tfp);
@@ -230,16 +230,16 @@ static RegMask callInMask( SONTypeFunPtr tfp, int idx, int maxArgSlot ) {
         R09_MASK,
     };
 
-    static RegMask callWin64(SONTypeFunPtr tfp, int idx, int maxArgSlot ) {
+    static RegMask callWin64(TypeFunPtr tfp, int idx, int maxArgSlot ) {
         // idx 2,3,4,5 passed in registers, with stack slot mirrors.
         // idx >= 6 passed on stack, starting at slot#1 (#0 reserved for RPC).
         if( idx >= 6 )
             return new RegMask(MAX_REG+maxArgSlot+(idx-2));
-        return tfp.arg(idx-2) instanceof SONTypeFloat
+        return tfp.arg(idx-2) instanceof TypeFloat
             ? XMMS8     [idx-2]
             : WIN64_CALL[idx-2];
     }
-    static short maxArgSlotWin64(SONTypeFunPtr tfp) {
+    static short maxArgSlotWin64(TypeFunPtr tfp) {
         return (short)tfp.nargs();
     }
 
@@ -271,23 +271,23 @@ static short maxArgSlotWin64(SONTypeFunPtr tfp) {
         R09_MASK,
     };
 
-    static RegMask callSys5(SONTypeFunPtr tfp, int idx, int maxArgSlot ) {
+    static RegMask callSys5(TypeFunPtr tfp, int idx, int maxArgSlot ) {
         // First 6 integers passed in registers: rdi,rsi,rdx,rcx,r08,r09
         // First 8 floats passed in registers: xmm0-xmm7
         int icnt=0, fcnt=0;     // Count of ints, floats
         for( int i=2; i<idx; i++ ) {
-            if( tfp.arg(i-2) instanceof SONTypeFloat ) fcnt++;
+            if( tfp.arg(i-2) instanceof TypeFloat) fcnt++;
             else icnt++;
         }
         int nstk = Math.max(icnt-6,0)+Math.max(fcnt-8,0);
-        return tfp.arg(idx-2) instanceof SONTypeFloat
+        return tfp.arg(idx-2) instanceof TypeFloat
             ? fcnt<8 ? XMMS8    [fcnt] : new RegMask(MAX_REG+maxArgSlot+nstk)
             : icnt<6 ? SYS5_CALL[icnt] : new RegMask(MAX_REG+maxArgSlot+nstk);
     }
-    static short maxArgSlotSys5(SONTypeFunPtr tfp) {
+    static short maxArgSlotSys5(TypeFunPtr tfp) {
         int icnt=0, fcnt=0;     // Count of ints, floats
         for( int i=0; i<tfp.nargs(); i++ ) {
-            if( tfp.arg(i) instanceof SONTypeFloat ) fcnt++;
+            if( tfp.arg(i) instanceof TypeFloat) fcnt++;
             else icnt++;
         }
         int nstk = Math.max(icnt-6,0)+Math.max(fcnt-8,0);
@@ -318,8 +318,8 @@ static short maxArgSlotSys5(SONTypeFunPtr tfp) {
         };
     }
     @Override public long neverSave() { return 1L<<RSP; }
-    @Override public RegMask retMask( SONTypeFunPtr tfp ) {
-        return tfp.ret() instanceof SONTypeFloat ? XMM0_MASK : RAX_MASK;
+    @Override public RegMask retMask( TypeFunPtr tfp ) {
+        return tfp.ret() instanceof TypeFloat ? XMM0_MASK : RAX_MASK;
     }
     @Override public int rpc() { return RPC; }
 
@@ -373,7 +373,7 @@ public Node instSelect(Node n) {
         case SubFNode    subf -> new SubFX86(subf);
         case SubNode      sub -> sub(sub);
         case ToFloatNode  tfn -> i2f8(tfn);
-        case XCtrlNode      x -> new ConstantNode(SONType.XCONTROL);
+        case XCtrlNode      x -> new ConstantNode(Type.XCONTROL);
         case XorNode      xor -> xor(xor);
 
         case LoopNode loop -> new LoopNode(loop);
@@ -399,7 +399,7 @@ private Node add(AddNode add) {
 
         // Attempt a full LEA-style break down.
         // Returns one of AddX86, AddIX86, LeaX86, or LHS
-        if( rhs instanceof ConstantNode off2 && off2._con instanceof SONTypeInteger toff ) {
+        if( rhs instanceof ConstantNode off2 && off2._con instanceof TypeInteger toff ) {
             long imm = toff.value();
             assert imm!=0;        // Folded in peeps
             if( (int)imm != imm ) // Full 64bit immediate
@@ -435,7 +435,7 @@ private Node _lea(Node add, Node base, Node idx, int off) {
         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 SONTypeInteger tscale && 0 <= tscale.value() && tscale.value() <= 3 ) {
+            shfcon._con instanceof TypeInteger tscale && 0 <= tscale.value() && tscale.value() <= 3 ) {
             idx = shift.in(1);
             scale = ((int) tscale.value());
         }
@@ -447,13 +447,13 @@ private Node _lea(Node add, Node base, Node idx, int off) {
 
 
     private Node and(AndNode and) {
-        if( and.in(2) instanceof ConstantNode con && con._con instanceof SONTypeInteger ti && imm32(ti.value()) )
+        if( and.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm32(ti.value()) )
             return new AndIX86(and, (int)ti.value());
         return new AndX86(and);
     }
 
     private Node call(CallNode call) {
-        return call.fptr() instanceof ConstantNode con && con._con instanceof SONTypeFunPtr tfp
+        return call.fptr() instanceof ConstantNode con && con._con instanceof TypeFunPtr tfp
             ? new CallX86(call, tfp)
             : new CallRX86(call);
     }
@@ -487,11 +487,11 @@ private Node _cmp(BoolNode bool) {
             return new CmpMemX86(bool, address(ld), ld.ptr(), idx, off, scale, imm(lhs), val, true );
 
         // Vs immediate
-        if( rhs instanceof ConstantNode con && con._con instanceof SONTypeInteger ti && imm32(ti.value()) )
+        if( rhs instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm32(ti.value()) )
             return new CmpIX86(bool, (int)ti.value(), false);
 
         // Operand swap compare; Set and Jmp need to swap `bop`
-        if( lhs instanceof ConstantNode con && con._con instanceof SONTypeInteger ti && imm32(ti.value()) )
+        if( lhs instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm32(ti.value()) )
             return new CmpIX86(bool, (int)ti.value(), swap=true);
 
         // x vs y
@@ -501,18 +501,18 @@ private Node _cmp(BoolNode bool) {
     private Node con( ConstantNode con ) {
         if(!con._con.isConstant()) return new ConstantNode(con); // Default unknown caller inputs
         return switch (con._con) {
-            case SONTypeInteger ti -> new IntX86(con);
-            case SONTypeFloat tf -> new FltX86(con);
-            case SONTypeFunPtr tfp -> new TFPX86(con);
-            case SONTypeMemPtr tmp -> throw Utils.TODO();
-            case SONTypeNil tn -> throw Utils.TODO();
+            case TypeInteger ti -> new IntX86(con);
+            case TypeFloat tf -> new FltX86(con);
+            case TypeFunPtr tfp -> new TFPX86(con);
+            case TypeMemPtr tmp -> throw Utils.TODO();
+            case TypeNil tn -> throw Utils.TODO();
             // TOP, BOTTOM, XCtrl, Ctrl, etc.  Never any executable code.
-            case SONType t -> t == SONType.NIL ? new IntX86(con) : new ConstantNode(con);
+            case Type t -> t == Type.NIL ? new IntX86(con) : new ConstantNode(con);
         };
     }
 
     private Node i2f8(ToFloatNode tfn) {
-        assert tfn.in(1)._type instanceof SONTypeInteger ti;
+        assert tfn.in(1)._type instanceof TypeInteger ti;
         return new I2f8X86(tfn);
     }
 
@@ -523,7 +523,7 @@ private Node jmp(IfNode iff) {
         String op = "!=";
         if( iff.in(1) instanceof BoolNode bool ) op = swap ? IfNode.swap(bool.op()) : bool.op();
         else if( iff.in(1)==null ) op = "=="; // Never-node cutout
-        else iff.setDef(1, new BoolNode.NE(iff.in(1), new ConstantNode(SONTypeInteger.ZERO)));
+        else iff.setDef(1, new BoolNode.NE(iff.in(1), new ConstantNode(TypeInteger.ZERO)));
         return new JmpX86(iff, op);
     }
 
@@ -532,13 +532,13 @@ private Node ld(LoadNode ld) {
     }
 
     private Node mul(MulNode mul) {
-        if( mul.in(2) instanceof ConstantNode con && con._con instanceof SONTypeInteger ti && imm32(ti.value()) )
+        if( mul.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm32(ti.value()) )
             return new MulIX86(mul, (int)ti.value());
         return new MulX86(mul);
     }
 
     private Node or(OrNode or) {
-        if( or.in(2) instanceof ConstantNode con && con._con instanceof SONTypeInteger ti && imm32(ti.value()) )
+        if( or.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm32(ti.value()) )
             return new OrIX86(or, (int)ti.value());
         return new OrX86(or);
     }
@@ -548,19 +548,19 @@ private Node prj( ProjNode prj ) {
     }
 
     private Node sar(SarNode sar) {
-        if( sar.in(2) instanceof ConstantNode con && con._con instanceof SONTypeInteger ti && imm8(ti.value()))
+        if( sar.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm8(ti.value()))
             return new SarIX86(sar, (int)(ti.value() & 0xff) );
         return new SarX86(sar);
     }
 
     private Node shl(ShlNode shl) {
-        if( shl.in(2) instanceof ConstantNode con && con._con instanceof SONTypeInteger ti && imm8(ti.value()))
+        if( shl.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm8(ti.value()))
             return new ShlIX86(shl, (int)(ti.value() & 0xff) );
         return new ShlX86(shl);
     }
 
     private Node shr( ShrNode shr ) {
-        if( shr.in(2) instanceof ConstantNode con && con._con instanceof SONTypeInteger ti && imm8(ti.value()) )
+        if( shr.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm8(ti.value()) )
             return new ShrIX86(shr, (int)(ti.value() & 0xff) );
         return new ShrX86(shr);
     }
@@ -586,13 +586,13 @@ private static boolean stld_match(StoreNode st, LoadNode ld ) {
 
 
     private Node sub (SubNode sub ){
-        return sub.in(2) instanceof ConstantNode con && con._con instanceof SONTypeInteger ti
+        return sub.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti
             ? new AddIX86(sub, (int)-ti.value())
             : new SubX86(sub);
     }
 
     private Node xor (XorNode xor){
-        if( xor.in(2) instanceof ConstantNode con && con._con instanceof SONTypeInteger ti && imm32(ti.value()) )
+        if( xor.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm32(ti.value()) )
             return new XorIX86(xor, (int)ti.value());
         return new XorX86(xor);
     }
@@ -608,18 +608,18 @@ private <N extends MemOpNode > N address(N mop) {
         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 SONTypeMemPtr; // Base ptr always, not some derived
-        if(mop.off() instanceof AddNode add && add.in(2) instanceof ConstantNode con && con._con instanceof SONTypeInteger ti) {
+        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 SONTypeInteger tscale && 0 <= tscale.value() && tscale.value() <= 3) {
+                    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 SONTypeInteger ti) {
+            if(mop.off() instanceof ConstantNode con && con._con instanceof TypeInteger ti) {
                 off = (int) ti.value();
                 assert off == ti.value(); // In 32-bit range
             } else {
@@ -631,7 +631,7 @@ private <N extends MemOpNode > N address(N mop) {
 
     private int imm( Node xval ) {
         assert val == null && imm == 0;
-        if( xval instanceof ConstantNode con && con._con instanceof SONTypeInteger ti) {
+        if( xval instanceof ConstantNode con && con._con instanceof TypeInteger ti) {
             val = null;
             imm = (int) ti.value();
             assert imm == ti.value(); // In 32-bit range
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/print/ASMPrinter.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/print/ASMPrinter.java
index 1a93013..20dd43d 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/print/ASMPrinter.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/print/ASMPrinter.java
@@ -28,7 +28,7 @@ public static SB print(SB sb, CodeGen code) {
         Encoding enc = code._encoding;
         if(  enc!=null && !enc._bigCons.isEmpty() ) {
             iadr = (iadr+15)&-16; // pad to 16
-            HashSet<SONType> targets = new HashSet<>();
+            HashSet<Type> targets = new HashSet<>();
             sb.p("--- Constant Pool ------").nl();
             // By log size
             for( int log = 3; log >= 0; log-- ) {
@@ -38,8 +38,8 @@ public static SB print(SB sb, CodeGen code) {
                     targets.add(relo._t);
                     if( relo._t.log_size()==log ) {
                         sb.hex2(iadr).p("  ");
-                        if( relo._t instanceof SONTypeTuple tt ) {
-                            for( SONType tx : tt._types ) {
+                        if( relo._t instanceof TypeTuple tt ) {
+                            for( Type tx : tt._types ) {
                                 switch( log ) {
                                 case 0: sb.hex1(enc.read1(iadr)); break;
                                 case 1: sb.hex2(enc.read2(iadr)); break;
@@ -109,7 +109,7 @@ static int doBlock(int iadr, SB sb, CodeGen code, FunNode fun, int cfgidx) {
         boolean once=false;
         for( Node n : bb.outs() ) {
             if( !(n instanceof PhiNode phi) ) continue;
-            if( phi._type instanceof SONTypeMem || phi._type instanceof SONTypeRPC ) continue; // Nothing for the hidden ones
+            if( phi._type instanceof TypeMem || phi._type instanceof TypeRPC) continue; // Nothing for the hidden ones
             // Post-RegAlloc phi prints all on one line
             if( postAlloc ) {
                 if( !once ) { once=true; sb.fix(4," ").p(" ").fix(encWidth,"").p("  "); }
@@ -164,7 +164,7 @@ static int doInst( int iadr, SB sb, CodeGen code, FunNode fun, int cfgidx, Node
         // ProjNodes following a multi (e.g. Call or New results),
         // get indent slightly and just print their index & node#
         if( n instanceof ProjNode proj ) {
-            if( proj._type instanceof SONTypeMem ) return iadr; // Nothing for the hidden ones
+            if( proj._type instanceof TypeMem) return iadr; // Nothing for the hidden ones
             sb.fix(4," ").p(" ").fix(encWidth,"").p("    ").fix(opWidth,proj._label==null ? "---" : proj._label).p(" ").p(code.reg(n,fun)).nl();
             return iadr;
         }
@@ -204,7 +204,7 @@ static int doInst( int iadr, SB sb, CodeGen code, FunNode fun, int cfgidx, Node
             isMultiOp = isMultiOp(sb,old,len);
             sb.fix(argWidth-(len-old),""); // Pad out
 
-        } else if( !(n._type instanceof SONTypeMem) ) {
+        } else if( !(n._type instanceof TypeMem) ) {
             // Room for some inputs
             sb.fix(5, n._nid+":" );
             int off = 0;
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/Field.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/Field.java
index d4cf783..70cee5e 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/Field.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/Field.java
@@ -7,20 +7,20 @@
 /**
  * Represents a field in a struct. This is not a Type in the type system.
  */
-public class Field extends SONType {
+public class Field extends Type {
 
     // The pair {fieldName,type} uniquely identifies a field.
 
     // Field name
     public final String _fname;
     // Type of the field
-    public final SONType _type;
+    public final Type _type;
     // Unique memory alias, not sensibly part of a "type" but very convenient here.
     public final int _alias;
     // Field must be written to exactly once, no more, no less
     public final boolean _final;
 
-    public Field(String fname, SONType type, int alias, boolean xfinal ) {
+    public Field(String fname, Type type, int alias, boolean xfinal ) {
         super(TFLD);
         _fname = fname;
         _type  = type;
@@ -28,20 +28,20 @@ public Field(String fname, SONType type, int alias, boolean xfinal ) {
         _final = xfinal;
     }
     // Make with existing alias
-    public static Field make(String fname, SONType type, int alias, boolean xfinal ) {
+    public static Field make(String fname, Type type, int alias, boolean xfinal ) {
         return new Field(fname,type,alias,xfinal).intern();
     }
-    public Field makeFrom( SONType type ) {
+    public Field makeFrom( Type type ) {
         return type == _type ? this : new Field(_fname,type,_alias,_final).intern();
     }
     @Override public Field makeRO() { return _final ? this : make(_fname,_type.makeRO(),_alias,true);  }
     @Override public boolean isFinal() { return _final && _type.isFinal(); }
 
-    public static final Field TEST = make("test", SONType.NIL,-2,false);
-    public static final Field TEST2= make("test", SONType.NIL,-2,true);
-    public static void gather(ArrayList<SONType> ts) { ts.add(TEST); ts.add(TEST2); }
+    public static final Field TEST = make("test", Type.NIL,-2,false);
+    public static final Field TEST2= make("test", Type.NIL,-2,true);
+    public static void gather(ArrayList<Type> ts) { ts.add(TEST); ts.add(TEST2); }
 
-    @Override Field xmeet( SONType that ) {
+    @Override Field xmeet( Type that ) {
         Field fld = (Field)that; // Invariant
         assert _fname.equals(fld._fname);
         assert _alias==fld._alias;
@@ -52,14 +52,14 @@ public Field makeFrom( SONType type ) {
     public Field dual() { return make(_fname,_type.dual(),_alias,!_final); }
 
     @Override public Field glb() {
-        SONType glb = _type.glb();
+        Type glb = _type.glb();
         return (glb==_type && _final) ? this : make(_fname,glb,_alias,true);
     }
 
     // Override in subclasses
     int hash() { return _fname.hashCode() ^ _type.hashCode() ^ _alias ^ (_final ? 1024 : 0); }
 
-    boolean eq(SONType t) {
+    boolean eq(Type t) {
         Field f = (Field)t;
         return _fname.equals(f._fname) && _type==f._type && _alias==f._alias && _final==f._final;
     }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONType.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/Type.java
similarity index 79%
rename from seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONType.java
rename to seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/Type.java
index 86a8f70..5c3e392 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONType.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/Type.java
@@ -21,8 +21,8 @@
  * simple constant folding e.g. 1+2 == 3 stuff.
  */
 
-public class SONType {
-    static final HashMap<SONType, SONType> INTERN = new HashMap<>();
+public class Type {
+    static final HashMap<Type, Type> INTERN = new HashMap<>();
 
     // ----------------------------------------------------------
     // Simple types are implemented fully here.  "Simple" means: the code and
@@ -50,35 +50,35 @@ public class SONType {
 
     public boolean is_simple() { return _type < TSIMPLE; }
     private static final String[] STRS = new String[]{"Bot","Top","Ctrl","~Ctrl","null","~nil"};
-    protected SONType(byte type) { _type = type; }
-
-    public static final SONType BOTTOM   = new SONType( TBOT   ).intern(); // ALL
-    public static final SONType TOP      = new SONType( TTOP   ).intern(); // ANY
-    public static final SONType CONTROL  = new SONType( TCTRL  ).intern(); // Ctrl
-    public static final SONType XCONTROL = new SONType( TXCTRL ).intern(); // ~Ctrl
-    public static final SONType NIL      = new SONType( TNIL   ).intern(); // low null of all flavors
-    public static final SONType XNIL     = new SONType( TXNIL  ).intern(); // high or choice null
-    public static SONType[] gather() {
-        ArrayList<SONType> ts = new ArrayList<>();
+    protected Type(byte type) { _type = type; }
+
+    public static final Type BOTTOM   = new Type( TBOT   ).intern(); // ALL
+    public static final Type TOP      = new Type( TTOP   ).intern(); // ANY
+    public static final Type CONTROL  = new Type( TCTRL  ).intern(); // Ctrl
+    public static final Type XCONTROL = new Type( TXCTRL ).intern(); // ~Ctrl
+    public static final Type NIL      = new Type( TNIL   ).intern(); // low null of all flavors
+    public static final Type XNIL     = new Type( TXNIL  ).intern(); // high or choice null
+    public static Type[] gather() {
+        ArrayList<Type> ts = new ArrayList<>();
         ts.add(BOTTOM);
         ts.add(CONTROL);
         ts.add(NIL);
         ts.add(XNIL);
-        SONTypeNil.gather(ts);
-        SONTypePtr.gather(ts);
-        SONTypeInteger.gather(ts);
-        SONTypeFloat.gather(ts);
-        SONTypeMemPtr.gather(ts);
-        SONTypeFunPtr.gather(ts);
-        SONTypeMem.gather(ts);
+        TypeNil.gather(ts);
+        TypePtr.gather(ts);
+        TypeInteger.gather(ts);
+        TypeFloat.gather(ts);
+        TypeMemPtr.gather(ts);
+        TypeFunPtr.gather(ts);
+        TypeMem.gather(ts);
         Field.gather(ts);
-        SONTypeStruct.gather(ts);
-        SONTypeTuple.gather(ts);
-        SONTypeRPC.gather(ts);
+        TypeStruct.gather(ts);
+        TypeTuple.gather(ts);
+        TypeRPC.gather(ts);
         int sz = ts.size();
         for( int i = 0; i < sz; i++ )
             ts.add(ts.get(i).dual());
-        return ts.toArray(new SONType[ts.size()]);
+        return ts.toArray(new Type[ts.size()]);
     }
 
     // Is high or on the lattice centerline.
@@ -104,7 +104,7 @@ public static SONType[] gather() {
 
     // Factory method which interns "this"
     @SuppressWarnings("unchecked")
-    public  <T extends SONType> T intern() {
+    public  <T extends Type> T intern() {
         T nnn = (T)INTERN.get(this);
         if( nnn==null )
             INTERN.put(nnn=(T)this,this);
@@ -125,41 +125,41 @@ public final int hashCode() {
     @Override
     public final boolean equals( Object o ) {
         if( o==this ) return true;
-        if( !(o instanceof SONType t)) return false;
+        if( !(o instanceof Type t)) return false;
         if( _type != t._type ) return false;
         return eq(t);
     }
     // Overridden in subclasses; subclass can assume "this!=t" and java classes are same
-    boolean eq(SONType t) { return true; }
+    boolean eq(Type t) { return true; }
 
 
     // ----------------------------------------------------------
-    public final SONType meet(SONType t) {
+    public final Type meet(Type t) {
         // Shortcut for the self case
         if( t == this ) return this;
         // Same-type is always safe in the subclasses
         if( _type==t._type ) return xmeet(t);
         // TypeNil vs TypeNil meet
-        if( this instanceof SONTypeNil ptr0 && t instanceof SONTypeNil ptr1 )
+        if( this instanceof TypeNil ptr0 && t instanceof TypeNil ptr1 )
             return ptr0.nmeet(ptr1);
         // Reverse; xmeet 2nd arg is never "is_simple" and never equal to "this".
         if(   is_simple() ) return this.xmeet(t   );
         if( t.is_simple() ) return t   .xmeet(this);
-        return SONType.BOTTOM;     // Mixing 2 unrelated types
+        return Type.BOTTOM;     // Mixing 2 unrelated types
     }
 
     // Compute meet right now.  Overridden in subclasses.
     // Handle cases where 'this.is_simple()' and unequal to 't'.
     // Subclassed xmeet calls can assert that '!t.is_simple()'.
-    SONType xmeet(SONType t) {
+    Type xmeet(Type t) {
         assert is_simple(); // Should be overridden in subclass
         // ANY meet anything is thing; thing meet ALL is ALL
         if( _type==TBOT || t._type==TTOP ) return this;
         if( _type==TTOP || t._type==TBOT ) return    t;
 
         // RHS TypeNil vs NIL/XNIL
-        if( _type==  TNIL ) return t instanceof SONTypeNil ptr ? ptr.meet0() : (t._type==TXNIL ? SONTypePtr.PTR : BOTTOM);
-        if( _type== TXNIL ) return t instanceof SONTypeNil ptr ? ptr.meetX() : (t._type== TNIL ? SONTypePtr.PTR : BOTTOM);
+        if( _type==  TNIL ) return t instanceof TypeNil ptr ? ptr.meet0() : (t._type==TXNIL ? TypePtr.PTR : BOTTOM);
+        if( _type== TXNIL ) return t instanceof TypeNil ptr ? ptr.meetX() : (t._type== TNIL ? TypePtr.PTR : BOTTOM);
         // 'this' is only {TCTRL,TXCTRL}
         // Other non-simple RHS things bottom out
         if( !t.is_simple() ) return BOTTOM;
@@ -169,7 +169,7 @@ SONType xmeet(SONType t) {
         return _type==TCTRL || t._type==TCTRL ? CONTROL : XCONTROL;
     }
 
-    public SONType dual() {
+    public Type dual() {
         return switch( _type ) {
         case TBOT  -> TOP;
         case TTOP  -> BOTTOM;
@@ -184,24 +184,24 @@ public SONType dual() {
     // ----------------------------------------------------------
     // Our lattice is defined with a MEET and a DUAL.
     // JOIN is dual of meet of both duals.
-    public final SONType join(SONType t) {
+    public final Type join(Type t) {
         if( this==t ) return this;
         return dual().meet(t.dual()).dual();
     }
 
     // True if this "isa" t; e.g. 17 isa TypeInteger.BOT
-    public boolean isa( SONType t ) { return meet(t)==t; }
+    public boolean isa( Type t ) { return meet(t)==t; }
 
     // True if this "isa" t up to named structures
-    public boolean shallowISA( SONType t ) { return isa(t); }
+    public boolean shallowISA( Type t ) { return isa(t); }
 
-    public SONType nonZero() { return SONTypePtr.NPTR; }
+    public Type nonZero() { return TypePtr.NPTR; }
 
     // Make a zero version of this type, 0 for integers and null for pointers.
-    public SONType makeZero() { return SONType.NIL; }
+    public Type makeZero() { return Type.NIL; }
 
     /** Compute greatest lower bound in the lattice */
-    public SONType glb() { return SONType.BOTTOM; }
+    public Type glb() { return Type.BOTTOM; }
 
     // Is forward-reference
     public boolean isFRef() { return false; }
@@ -210,7 +210,7 @@ public final SONType join(SONType t) {
     public boolean isFinal() { return true; }
 
     // Make all reachable struct Fields final
-    public SONType makeRO() { return this; }
+    public Type makeRO() { return this; }
 
     // ----------------------------------------------------------
 
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeFloat.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/TypeFloat.java
similarity index 63%
rename from seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeFloat.java
rename to seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/TypeFloat.java
index c4d2dba..60d8a32 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeFloat.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/TypeFloat.java
@@ -6,12 +6,12 @@
 /**
  * Float Type
  */
-public class SONTypeFloat extends SONType {
+public class TypeFloat extends Type {
 
-    public final static SONTypeFloat ONE = constant(1.0);
-    public final static SONTypeFloat FZERO = constant(0.0);
-    public final static SONTypeFloat F32 = make(32, 0);
-    public final static SONTypeFloat F64 = make(64, 0);
+    public final static TypeFloat ONE = constant(1.0);
+    public final static TypeFloat FZERO = constant(0.0);
+    public final static TypeFloat F32 = make(32, 0);
+    public final static TypeFloat F64 = make(64, 0);
 
     // - high -64, high -32, con 0, low +32, low +64
     public final byte _sz;
@@ -21,18 +21,18 @@ public class SONTypeFloat extends SONType {
      */
     public final double _con;
 
-    private SONTypeFloat(byte sz, double con) {
+    private TypeFloat(byte sz, double con) {
         super(TFLT);
         _sz = sz;
         _con = con;
     }
-    private static SONTypeFloat make(int sz, double con) {
-        return new SONTypeFloat((byte)sz,con).intern();
+    private static TypeFloat make(int sz, double con) {
+        return new TypeFloat((byte)sz,con).intern();
     }
 
-    public static SONTypeFloat constant(double con) { return make(0, con); }
+    public static TypeFloat constant(double con) { return make(0, con); }
 
-    public static void gather(ArrayList<SONType> ts) { ts.add(F64); ts.add(F32); ts.add(constant(3.141592653589793)); }
+    public static void gather(ArrayList<Type> ts) { ts.add(F64); ts.add(F32); ts.add(constant(3.141592653589793)); }
 
     @Override public String str() {
         return switch( _sz ) {
@@ -53,13 +53,13 @@ private static SONTypeFloat make(int sz, double con) {
     public double value() { return _con; }
 
     @Override
-    public SONTypeFloat xmeet(SONType other) {
-        SONTypeFloat f = (SONTypeFloat)other;
+    public TypeFloat xmeet(Type other) {
+        TypeFloat f = (TypeFloat)other;
         // Invariant from caller: 'this' != 'other' and same class (TypeFloat).
-        SONTypeFloat i = (SONTypeFloat)other; // Contract
+        TypeFloat i = (TypeFloat)other; // Contract
         // Larger size in i1, smaller in i0
-        SONTypeFloat i0 = _sz < i._sz ? this : i;
-        SONTypeFloat i1 = _sz < i._sz ? i : this;
+        TypeFloat i0 = _sz < i._sz ? this : i;
+        TypeFloat i1 = _sz < i._sz ? i : this;
 
         if( i1._sz== 64 ) return F64;
         if( i0._sz==-64 ) return i1;
@@ -75,18 +75,18 @@ public SONTypeFloat xmeet(SONType other) {
     }
 
     @Override
-    public SONType dual() {
+    public Type dual() {
         return isConstant() ? this : make(-_sz,0); // Constants are a self-dual
     }
 
-    @Override public SONType makeZero() { return FZERO; }
-    @Override public SONType glb() { return F64; }
+    @Override public Type makeZero() { return FZERO; }
+    @Override public Type glb() { return F64; }
 
     @Override
     int hash() { return (int)(Double.hashCode(_con) ^ _sz ^ (1<<17)); }
     @Override
-    public boolean eq( SONType t ) {
-        SONTypeFloat i = (SONTypeFloat)t; // Contract
+    public boolean eq( Type t ) {
+        TypeFloat i = (TypeFloat)t; // Contract
         // Allow NaN to check for equality
         return _sz==i._sz && Double.doubleToLongBits(_con)==Double.doubleToLongBits(i._con);
     }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeFunPtr.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/TypeFunPtr.java
similarity index 62%
rename from seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeFunPtr.java
rename to seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/TypeFunPtr.java
index 484c1d7..1069de5 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeFunPtr.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/TypeFunPtr.java
@@ -16,16 +16,16 @@
  * Functions with different signatures cannot be mixed, and will result in a
  * bottom function type, which can only be null-checked.
  */
-public class SONTypeFunPtr extends SONTypeNil {
+public class TypeFunPtr extends TypeNil {
     // A TypeFunPtr is Signature and a set of functions.
-    public final SONTypeTuple _sig;
-    public final SONType _ret;
+    public final TypeTuple _sig;
+    public final Type _ret;
     // Cheesy easy implementation for a small set; 1 bit per unique function
     // 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 SONTypeFunPtr(byte nil, SONTypeTuple sig, SONType ret, long fidxs) {
+    public TypeFunPtr(byte nil, TypeTuple sig, Type ret, long fidxs) {
         super(TFUNPTR,nil);
         assert sig != null;
         _sig = sig;
@@ -33,41 +33,42 @@ public SONTypeFunPtr(byte nil, SONTypeTuple sig, SONType ret, long fidxs) {
         _fidxs = fidxs;
     }
 
-    public static SONTypeFunPtr make(byte nil, SONTypeTuple sig, SONType ret, long fidxs ) { return new SONTypeFunPtr(nil,sig,ret,fidxs).intern(); }
-    public static SONTypeFunPtr make(boolean nil, SONTypeTuple sig, SONType ret ) { return make((byte)(nil ? 3 : 2),sig,ret,-1); }
-    @Override SONTypeFunPtr makeFrom(byte nil ) { return  nil ==_nil   ? this : make(  nil,_sig,_ret,   _fidxs); }
-    public SONTypeFunPtr makeFrom(SONType ret ) { return     ret ==_ret   ? this : make( _nil,_sig, ret,   _fidxs); }
-    public SONTypeFunPtr makeFrom(int fidx ) { return make((byte)2, _sig,_ret,1L<<fidx ); }
+    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); }
+    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 SONTypeFunPtr BOT   = make((byte)3, SONTypeTuple.BOT, SONType.BOTTOM,-1);
+    public static TypeFunPtr BOT   = make((byte)3, TypeTuple.BOT, Type.BOTTOM,-1);
 //    public static SONTypeFunPtr TEST  = make((byte)2, SONTypeTuple.TEST, SONTypeInteger.BOT,1);
 //    public static SONTypeFunPtr TEST0 = make((byte)3, SONTypeTuple.TEST, SONTypeInteger.BOT,3);
-    public static SONTypeFunPtr MAIN  = make((byte)3, SONTypeTuple.MAIN, SONTypeInteger.BOT,-1);
-    public static SONTypeFunPtr CALLOC= make((byte)3,SONTypeTuple.CALLOC,SONTypeMemPtr.BOT,-1);
-    public static void gather(ArrayList<SONType> ts) { /* ts.add(TEST); ts.add(TEST0); */ ts.add(BOT); ts.add(MAIN); }
+    public static TypeFunPtr MAIN  = make((byte)3, TypeTuple.MAIN, TypeInteger.BOT,-1);
+    public static TypeFunPtr CALLOC= make((byte)3, TypeTuple.CALLOC, TypeMemPtr.BOT,-1);
+    public static void gather(ArrayList<Type> ts) { /* ts.add(TEST); ts.add(TEST0); */ ts.add(BOT); ts.add(MAIN); }
 
     @Override
-    SONType xmeet(SONType t) {
-        SONTypeFunPtr that = (SONTypeFunPtr) t;
-        return SONTypeFunPtr.make(xmeet0(that),(SONTypeTuple)_sig.meet(that._sig), _ret.meet(that._ret), _fidxs | that._fidxs);
+    Type xmeet(Type t) {
+        TypeFunPtr that = (TypeFunPtr) t;
+        return TypeFunPtr.make(xmeet0(that),(TypeTuple)_sig.meet(that._sig), _ret.meet(that._ret), _fidxs | that._fidxs);
     }
 
     @Override
-    public SONTypeFunPtr dual() { return SONTypeFunPtr.make(dual0(), _sig.dual(), _ret.dual(), ~_fidxs); }
+    public TypeFunPtr dual() { return TypeFunPtr.make(dual0(), _sig.dual(), _ret.dual(), ~_fidxs); }
 
     // RHS is NIL; do not deep-dual when crossing the centerline
-    @Override public SONType meet0() { return _nil==3 ? this : make((byte)3,_sig,_ret,_fidxs); }
+    @Override public Type meet0() { return _nil==3 ? this : make((byte)3,_sig,_ret,_fidxs); }
 
-    @Override public SONTypeFunPtr glb() { return make((byte)3,_sig,_ret,-1L); }
+    @Override public TypeFunPtr glb() { return make((byte)3,_sig,_ret,-1L); }
 
     @Override public boolean isHigh    () { return _nil <= 1 || (_nil==2 && _fidxs==0); }
     @Override public boolean isConstant() { return (_nil==2 && Long.bitCount(_fidxs)==1) || (_nil==3 && _fidxs==0); }
 
     @Override public int log_size() { return 2; } // (1<<2)==4-byte pointers
 
-    public SONType arg(int i) { return _sig._types[i]; }
+    public Type arg(int i) { return _sig._types[i]; }
     public long fidxs() { return _fidxs; }
-    public SONType ret() { return _ret; }
+    public Type ret() { return _ret; }
     public int nargs() { return _sig._types.length; }
     public int fidx() { assert Long.bitCount(_fidxs)==1; return Long.numberOfTrailingZeros(_fidxs); }
 
@@ -75,8 +76,8 @@ SONType xmeet(SONType t) {
     int hash() { return Utils.fold(_sig.hashCode() ^ _ret.hashCode() ^ _fidxs ^ super.hash()); }
 
     @Override
-    boolean eq(SONType t) {
-        SONTypeFunPtr ptr = (SONTypeFunPtr)t; // Invariant
+    boolean eq(Type t) {
+        TypeFunPtr ptr = (TypeFunPtr)t; // Invariant
         return _sig == ptr._sig  && _ret == ptr._ret && _fidxs == ptr._fidxs && super.eq(ptr);
     }
 
@@ -84,11 +85,11 @@ boolean eq(SONType t) {
     @Override public SB  print(SB sb) { return _print(sb,false,true); }
     public SB print(SB sb, boolean n) { return _print(sb,false,n); }
     @Override public SB gprint(SB sb) { return _print(sb,true ,true); }
-    private static SB _print(SB sb, boolean g, SONType t) { return g ? t.gprint(sb) : t.print(sb); }
+    private static SB _print(SB sb, boolean g, Type t) { return g ? t.gprint(sb) : t.print(sb); }
     private SB _print(SB sb, boolean g, boolean n) {
         sb.p(x()).p("{ ");
         if( _sig._types!=null )
-            for( SONType t : _sig._types )
+            for( Type t : _sig._types )
                 _print(sb,g,t).p(" ");
         _print(sb.p(g ? "&rarr; " : "-> "),g,_ret).p(" #");
         if( isHigh() ) sb.p("~");
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeInteger.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/TypeInteger.java
similarity index 67%
rename from seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeInteger.java
rename to seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/TypeInteger.java
index 5030b57..3026653 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeInteger.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/TypeInteger.java
@@ -6,24 +6,24 @@
 /**
  * Integer Type
  */
-public class SONTypeInteger extends SONType {
+public class TypeInteger extends Type {
 
-    public final static SONTypeInteger ZERO= make(0,0);
-    public final static SONTypeInteger FALSE=ZERO;
-    public final static SONTypeInteger TRUE= make(1,1);
+    public final static TypeInteger ZERO= make(0,0);
+    public final static TypeInteger FALSE=ZERO;
+    public final static TypeInteger TRUE= make(1,1);
 
-    public final static SONTypeInteger I1  = make(-1,0);
-    public final static SONTypeInteger I8  = make(-128,127);
-    public final static SONTypeInteger I16 = make(-32768,32767);
-    public final static SONTypeInteger I32 = make(-1L<<31,(1L<<31)-1);
-    public final static SONTypeInteger BOT = make(Long.MIN_VALUE,Long.MAX_VALUE);
-    public final static SONTypeInteger TOP = BOT.dual();
+    public final static TypeInteger I1  = make(-1,0);
+    public final static TypeInteger I8  = make(-128,127);
+    public final static TypeInteger I16 = make(-32768,32767);
+    public final static TypeInteger I32 = make(-1L<<31,(1L<<31)-1);
+    public final static TypeInteger BOT = make(Long.MIN_VALUE,Long.MAX_VALUE);
+    public final static TypeInteger TOP = BOT.dual();
 
-    public final static SONTypeInteger U1  = make(0,1);
-    public final static SONTypeInteger BOOL= U1;
-    public final static SONTypeInteger U8  = make(0,255);
-    public final static SONTypeInteger U16 = make(0,65535);
-    public final static SONTypeInteger U32 = make(0,(1L<<32)-1);
+    public final static TypeInteger U1  = make(0,1);
+    public final static TypeInteger BOOL= U1;
+    public final static TypeInteger U8  = make(0,255);
+    public final static TypeInteger U16 = make(0,65535);
+    public final static TypeInteger U32 = make(0,(1L<<32)-1);
 
     /**
      * Describes an integer *range* - everything from min to max; both min and
@@ -34,18 +34,18 @@ public class SONTypeInteger extends SONType {
      */
     public final long _min, _max;
 
-    private SONTypeInteger(long min, long max) {
+    private TypeInteger(long min, long max) {
         super(TINT);
         _min = min;
         _max = max;
     }
 
     // Strict non-zero contract
-    public static SONTypeInteger make(long lo, long hi) { return new SONTypeInteger(lo,hi).intern(); }
+    public static TypeInteger make(long lo, long hi) { return new TypeInteger(lo,hi).intern(); }
 
-    public static SONTypeInteger constant(long con) { return make(con, con); }
+    public static TypeInteger constant(long con) { return make(con, con); }
 
-    public static void gather(ArrayList<SONType> ts) { ts.add(I32); ts.add(BOT); ts.add(U1); ts.add(I1); ts.add(U8); }
+    public static void gather(ArrayList<Type> ts) { ts.add(I32); ts.add(BOT); ts.add(U1); ts.add(I1); ts.add(U8); }
 
     @Override public String str() {
         if( isConstant() ) return ""+_min;
@@ -102,26 +102,26 @@ public long mask() {
     }
 
     @Override
-    public SONType xmeet(SONType other) {
+    public Type xmeet(Type other) {
         // Invariant from caller: 'this' != 'other' and same class (TypeInteger)
-        SONTypeInteger i = (SONTypeInteger)other; // Contract
+        TypeInteger i = (TypeInteger)other; // Contract
         return make(Math.min(_min,i._min), Math.max(_max,i._max));
     }
 
-    @Override public SONTypeInteger dual() { return make(_max,_min); }
+    @Override public TypeInteger dual() { return make(_max,_min); }
 
-    @Override public SONTypeInteger nonZero() {
+    @Override public TypeInteger nonZero() {
         if( isHigh() ) return this;
         if( this==ZERO ) return null;                  // No sane answer
         if( _min==0 ) return make(1,Math.max(_max,1)); // specifically good on BOOL
         if( _max==0 ) return make(_min,-1);
         return this;
     }
-    @Override public SONType makeZero() { return ZERO; }
-    @Override public SONTypeInteger glb() { return BOT; }
+    @Override public Type makeZero() { return ZERO; }
+    @Override public TypeInteger glb() { return BOT; }
     @Override int hash() { return Utils.fold(_min) * Utils.fold(_max); }
-    @Override public boolean eq( SONType t ) {
-        SONTypeInteger i = (SONTypeInteger)t; // Contract
+    @Override public boolean eq( Type t ) {
+        TypeInteger i = (TypeInteger)t; // Contract
         return _min==i._min && _max==i._max;
     }
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeMem.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/TypeMem.java
similarity index 59%
rename from seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeMem.java
rename to seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/TypeMem.java
index aed4569..6fd87d7 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeMem.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/TypeMem.java
@@ -8,52 +8,52 @@
 /**
  * Represents a slice of memory corresponding to a set of aliases
  */
-public class SONTypeMem extends SONType {
+public class TypeMem extends Type {
 
     // Which slice of memory?
     //  0 means TOP, no slice.
     //  0 means BOT, all memory.
     //  N means slice#N.
     public final int _alias;
-    public final SONType _t;       // Memory contents, some scalar type
+    public final Type _t;       // Memory contents, some scalar type
 
-    private SONTypeMem(int alias, SONType t) {
+    private TypeMem(int alias, Type t) {
         super(TMEM);
-        assert alias!=0 || (t== SONType.TOP || t== SONType.BOTTOM);
+        assert alias!=0 || (t== Type.TOP || t== Type.BOTTOM);
         _alias = alias;
         _t = t;
     }
 
-    public static SONTypeMem make(int alias, SONType t) { return new SONTypeMem(alias,t).intern(); }
-    public static final SONTypeMem TOP = make(0, SONType.TOP   );
-    public static final SONTypeMem BOT = make(0, SONType.BOTTOM);
+    public static TypeMem make(int alias, Type t) { return new TypeMem(alias,t).intern(); }
+    public static final TypeMem TOP = make(0, Type.TOP   );
+    public static final TypeMem BOT = make(0, Type.BOTTOM);
 
-    public static void gather(ArrayList<SONType> ts) { ts.add(make(1, SONType.NIL)); ts.add(make(1, SONTypeInteger.ZERO)); ts.add(BOT); }
+    public static void gather(ArrayList<Type> ts) { ts.add(make(1, Type.NIL)); ts.add(make(1, TypeInteger.ZERO)); ts.add(BOT); }
 
     @Override
-    SONTypeMem xmeet(SONType t) {
-        SONTypeMem that = (SONTypeMem) t; // Invariant: TypeMem and unequal
+    TypeMem xmeet(Type t) {
+        TypeMem that = (TypeMem) t; // Invariant: TypeMem and unequal
         if( this==TOP ) return that;
         if( that==TOP ) return this;
         if( this==BOT ) return BOT;
         if( that==BOT ) return BOT;
         int alias = _alias==that._alias ? _alias : 0;
-        SONType mt = _t.meet(that._t);
+        Type mt = _t.meet(that._t);
         return make(alias,mt);
     }
 
     @Override
-    public SONType dual() {
+    public Type dual() {
         return make(_alias,_t.dual());
     }
 
     @Override public boolean isHigh() { return _t.isHigh(); }
-    @Override public SONType glb() { return make(_alias,_t.glb()); }
+    @Override public Type glb() { return make(_alias,_t.glb()); }
 
     @Override int hash() { return 9876543 + _alias + _t.hashCode(); }
 
-    @Override boolean eq(SONType t) {
-        SONTypeMem that = (SONTypeMem) t; // Invariant
+    @Override boolean eq(Type t) {
+        TypeMem that = (TypeMem) t; // Invariant
         return _alias == that._alias && _t == that._t;
     }
 
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeMemPtr.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/TypeMemPtr.java
similarity index 60%
rename from seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeMemPtr.java
rename to seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/TypeMemPtr.java
index e3b197f..c213e07 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeMemPtr.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/TypeMemPtr.java
@@ -13,7 +13,7 @@
  * The distinguished *$BOT ptr represents union of *void and Null.
  * The distinguished *$TOP ptr represents the dual of *$BOT.
  */
-public class SONTypeMemPtr extends SONTypeNil {
+public class TypeMemPtr extends TypeNil {
     // A TypeMemPtr is pair (obj,nil)
     // where obj is a TypeStruct, possibly TypeStruct.BOT/TOP
     // where nil is an explicit null is allowed or not
@@ -24,49 +24,49 @@ public class SONTypeMemPtr extends SONTypeNil {
     // (BOT   ,false) - a not-nil void* (unspecified struct)
     // (TOP   ,true ) - a nil
 
-    public final SONTypeStruct _obj;
+    public final TypeStruct _obj;
 
-    public SONTypeMemPtr(byte nil, SONTypeStruct obj) {
+    public TypeMemPtr(byte nil, TypeStruct obj) {
         super(TMEMPTR,nil);
         assert obj!=null;
         _obj = obj;
     }
-    public static SONTypeMemPtr make(byte nil, SONTypeStruct obj) { return new SONTypeMemPtr(nil, obj).intern(); }
-    public static SONTypeMemPtr makeNullable(SONTypeStruct obj) { return make((byte)3, obj); }
-    public static SONTypeMemPtr make(SONTypeStruct obj) { return new SONTypeMemPtr((byte)2, obj).intern(); }
-    public SONTypeMemPtr makeFrom(SONTypeStruct obj) { return obj==_obj ? this : make(_nil, obj); }
-    public SONTypeMemPtr makeNullable() { return makeFrom((byte)3); }
+    public static TypeMemPtr make(byte nil, TypeStruct obj) { return new TypeMemPtr(nil, obj).intern(); }
+    public static TypeMemPtr makeNullable(TypeStruct obj) { return make((byte)3, obj); }
+    public static TypeMemPtr make(TypeStruct obj) { return new TypeMemPtr((byte)2, obj).intern(); }
+    public TypeMemPtr makeFrom(TypeStruct obj) { return obj==_obj ? this : make(_nil, obj); }
+    public TypeMemPtr makeNullable() { return makeFrom((byte)3); }
     @Override
-    SONTypeMemPtr makeFrom(byte nil) { return nil==_nil ? this : make(nil,_obj); }
-    @Override public SONTypeMemPtr makeRO() { return makeFrom(_obj.makeRO()); }
+    TypeMemPtr makeFrom(byte nil) { return nil==_nil ? this : make(nil,_obj); }
+    @Override public TypeMemPtr makeRO() { return makeFrom(_obj.makeRO()); }
     @Override public boolean isFinal() { return _obj.isFinal(); }
 
     // An abstract pointer, pointing to either a Struct or an Array.
     // Can also be null or not, so 4 choices {TOP,BOT} x {nil,not}
-    public static final SONTypeMemPtr BOT = make((byte)3, SONTypeStruct.BOT);
-    public static final SONTypeMemPtr TOP = BOT.dual();
-    public static final SONTypeMemPtr NOTBOT = make((byte)2, SONTypeStruct.BOT);
+    public static final TypeMemPtr BOT = make((byte)3, TypeStruct.BOT);
+    public static final TypeMemPtr TOP = BOT.dual();
+    public static final TypeMemPtr NOTBOT = make((byte)2, TypeStruct.BOT);
 
     //public static SONTypeMemPtr TEST= make((byte)2, SONTypeStruct.TEST);
-    public static void gather(ArrayList<SONType> ts) { ts.add(NOTBOT); ts.add(BOT); /* ts.add(TEST); */ }
+    public static void gather(ArrayList<Type> ts) { ts.add(NOTBOT); ts.add(BOT); /* ts.add(TEST); */ }
 
     @Override
-    public SONTypeNil xmeet(SONType t) {
-        SONTypeMemPtr that = (SONTypeMemPtr) t;
-        return SONTypeMemPtr.make(xmeet0(that), (SONTypeStruct)_obj.meet(that._obj));
+    public TypeNil xmeet(Type t) {
+        TypeMemPtr that = (TypeMemPtr) t;
+        return TypeMemPtr.make(xmeet0(that), (TypeStruct)_obj.meet(that._obj));
     }
 
     @Override
-    public SONTypeMemPtr dual() { return SONTypeMemPtr.make( dual0(), _obj.dual()); }
+    public TypeMemPtr dual() { return TypeMemPtr.make( dual0(), _obj.dual()); }
 
     // RHS is NIL; do not deep-dual when crossing the centerline
     @Override
-    SONType meet0() { return _nil==3 ? this : make((byte)3,_obj); }
+    Type meet0() { return _nil==3 ? this : make((byte)3,_obj); }
 
 
     // True if this "isa" t up to named structures
-    @Override public boolean shallowISA( SONType t ) {
-        if( !(t instanceof SONTypeMemPtr that) ) return false;
+    @Override public boolean shallowISA( Type t ) {
+        if( !(t instanceof TypeMemPtr that) ) return false;
         if( this==that ) return true;
         if( xmeet0(that)!=that._nil ) return false;
         if( _obj==that._obj ) return true;
@@ -75,7 +75,7 @@ public SONTypeNil xmeet(SONType t) {
         throw Utils.TODO(); // return _obj.shallowISA(that._obj);
     }
 
-    @Override public SONTypeMemPtr glb() { return make((byte)3,_obj.glb()); }
+    @Override public TypeMemPtr glb() { return make((byte)3,_obj.glb()); }
     // Is forward-reference
     @Override public boolean isFRef() { return _obj.isFRef(); }
 
@@ -83,8 +83,8 @@ public SONTypeNil xmeet(SONType t) {
 
     @Override int hash() { return _obj.hashCode() ^ super.hash(); }
 
-    @Override boolean eq(SONType t) {
-        SONTypeMemPtr ptr = (SONTypeMemPtr)t; // Invariant
+    @Override boolean eq(Type t) {
+        TypeMemPtr ptr = (TypeMemPtr)t; // Invariant
         return _obj == ptr._obj  && super.eq(ptr);
     }
 
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeNil.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/TypeNil.java
similarity index 61%
rename from seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeNil.java
rename to seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/TypeNil.java
index 3193c0c..7ee19bb 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeNil.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/TypeNil.java
@@ -5,48 +5,48 @@
 /**
  * Nil-able Scalar types
  */
-public abstract class SONTypeNil extends SONType {
+public abstract class TypeNil extends Type {
     // 0 = high-subclass choice nil
     // 1 = high-subclass no nil
     // 2 = low -subclass no nil
     // 3 = low -subclass also nil
     final byte _nil;
 
-    SONTypeNil(byte t, byte nil ) { super(t); _nil = nil; }
+    TypeNil(byte t, byte nil ) { super(t); _nil = nil; }
 
-    public static void gather(ArrayList<SONType> ts) { }
+    public static void gather(ArrayList<Type> ts) { }
 
-    abstract SONTypeNil makeFrom(byte nil);
+    abstract TypeNil makeFrom(byte nil);
 
-    byte xmeet0(SONTypeNil that) { return (byte)Math.max(_nil,that._nil); }
+    byte xmeet0(TypeNil that) { return (byte)Math.max(_nil,that._nil); }
 
     byte dual0() { return (byte)(3-_nil); }
 
     // RHS is NIL
-    abstract SONType meet0();
+    abstract Type meet0();
 
     // RHS is XNIL
-    SONType meetX() {
-        return _nil==0 ? XNIL : (_nil<=2 ? SONTypePtr.NPTR : SONTypePtr.PTR);
+    Type meetX() {
+        return _nil==0 ? XNIL : (_nil<=2 ? TypePtr.NPTR : TypePtr.PTR);
     }
 
-    SONType nmeet(SONTypeNil tn) {
+    Type nmeet(TypeNil tn) {
         // Invariants: both are TypeNil subclasses and unequal classes.
         // If this is TypePtr, we went to TypePtr.nmeet and not here.
         // If that is TypePtr, this is not (invariant); reverse and go again.
-        if( tn instanceof SONTypePtr ts ) return ts.nmeet(this);
+        if( tn instanceof TypePtr ts ) return ts.nmeet(this);
 
         // Two mismatched TypeNil, no Scalar.
         if( _nil==0 && tn._nil==0 ) return XNIL;
-        if( _nil<=2 && tn._nil<=2 ) return SONTypePtr.NPTR;
-        return SONTypePtr.PTR;
+        if( _nil<=2 && tn._nil<=2 ) return TypePtr.NPTR;
+        return TypePtr.PTR;
     }
 
     @Override public boolean isHigh       () { return _nil <= 1; }
     @Override public boolean isConstant   () { return false; }
     @Override public boolean isHighOrConst() { return isHigh() || isConstant(); }
 
-    @Override public SONType glb() { return SONType.NIL; }
+    @Override public Type glb() { return Type.NIL; }
 
     public boolean notNull() { return _nil==1 || _nil==2; }
     public boolean nullable() { return _nil==3; }
@@ -56,5 +56,5 @@ SONType nmeet(SONTypeNil tn) {
 
     int hash() { return _nil<<17; }
 
-    boolean eq(SONTypeNil ptr) { return _nil == ptr._nil; }
+    boolean eq(TypeNil ptr) { return _nil == ptr._nil; }
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypePtr.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/TypePtr.java
similarity index 51%
rename from seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypePtr.java
rename to seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/TypePtr.java
index dda8dd2..714271c 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypePtr.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/TypePtr.java
@@ -7,32 +7,32 @@
 /**
  * Represents a Scalar; a single register-sized value.
  */
-public class SONTypePtr extends SONTypeNil {
+public class TypePtr extends TypeNil {
 
-    private SONTypePtr(byte nil) { super(TPTR,nil);  }
+    private TypePtr(byte nil) { super(TPTR,nil);  }
 
     // An abstract pointer, pointing to either a Struct or an Array.
     // Can also be null or not, so 4 choices {TOP,BOT} x {nil,not}
-    public static SONTypePtr XPTR = new SONTypePtr((byte)0).intern();
-    public static SONTypePtr XNPTR= new SONTypePtr((byte)1).intern();
-    public static SONTypePtr NPTR = new SONTypePtr((byte)2).intern();
-    public static SONTypePtr PTR  = new SONTypePtr((byte)3).intern();
-    private static final SONTypePtr[] PTRS = new SONTypePtr[]{XPTR,XNPTR,NPTR,PTR};
+    public static TypePtr XPTR = new TypePtr((byte)0).intern();
+    public static TypePtr XNPTR= new TypePtr((byte)1).intern();
+    public static TypePtr NPTR = new TypePtr((byte)2).intern();
+    public static TypePtr PTR  = new TypePtr((byte)3).intern();
+    private static final TypePtr[] PTRS = new TypePtr[]{XPTR,XNPTR,NPTR,PTR};
 
-    public static void gather(ArrayList<SONType> ts) { ts.add(PTR); ts.add(NPTR); }
+    public static void gather(ArrayList<Type> ts) { ts.add(PTR); ts.add(NPTR); }
 
-    SONTypeNil makeFrom(byte nil) { throw Utils.TODO(); }
+    TypeNil makeFrom(byte nil) { throw Utils.TODO(); }
 
-    @Override public SONTypeNil xmeet(SONType t) {
-        SONTypePtr that = (SONTypePtr) t;
+    @Override public TypeNil xmeet(Type t) {
+        TypePtr that = (TypePtr) t;
         return PTRS[xmeet0(that)];
     }
 
-    @Override public SONTypePtr dual() { return PTRS[dual0()]; }
+    @Override public TypePtr dual() { return PTRS[dual0()]; }
 
     // High scalar loses, low scalar wins
     @Override
-    SONTypeNil nmeet(SONTypeNil tn) {
+    TypeNil nmeet(TypeNil tn) {
         if( _nil==0 ) return tn; // High scalar loses
         if( _nil==1 ) return tn.makeFrom(xmeet0(tn)); // High scalar loses
         if( _nil==2 ) return tn._nil==3 ? PTR : NPTR; // Low scalar wins
@@ -42,13 +42,13 @@ SONTypeNil nmeet(SONTypeNil tn) {
 
     // RHS is  NIL
     @Override
-    SONType meet0() { return isHigh() ? NIL : PTR; }
+    Type meet0() { return isHigh() ? NIL : PTR; }
     // RHS is XNIL
     // 0->xscalar, 1->nscalar, 2->nscalar, 3->scalar
     @Override
-    SONType meetX() { return _nil==0 ? XNIL : (_nil==3 ? PTR : NPTR); }
+    Type meetX() { return _nil==0 ? XNIL : (_nil==3 ? PTR : NPTR); }
 
-    @Override public SONTypePtr glb() { return PTR; }
+    @Override public TypePtr glb() { return PTR; }
 
     private static final String[] STRS = new String[]{"~ptr","~nptr","nptr","ptr"};
     @Override public String str() { return STRS[_nil]; }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeRPC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/TypeRPC.java
similarity index 74%
rename from seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeRPC.java
rename to seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/TypeRPC.java
index 82f0e2f..4070ab2 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeRPC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/TypeRPC.java
@@ -6,7 +6,7 @@
 /**
  *  Return Program Control or Return PC or RPC
  */
-public class SONTypeRPC extends SONType {
+public class TypeRPC extends Type {
 
     // A set of CallEndNode IDs (or StopNode); commonly just one.
     // Basically a sparse bit set
@@ -15,26 +15,26 @@ public class SONTypeRPC extends SONType {
     // If true, invert the meaning of the bits
     final boolean _any;
 
-    private SONTypeRPC(boolean any, HashSet<Integer> rpcs) {
+    private TypeRPC(boolean any, HashSet<Integer> rpcs) {
         super(TRPC);
         _any = any;
         _rpcs = rpcs;
     }
-    private static SONTypeRPC make(boolean any, HashSet<Integer> rpcs) {
-        return new SONTypeRPC(any,rpcs).intern();
+    private static TypeRPC make(boolean any, HashSet<Integer> rpcs) {
+        return new TypeRPC(any,rpcs).intern();
     }
 
-    public static SONTypeRPC constant(int cend) {
+    public static TypeRPC constant(int cend) {
         HashSet<Integer> rpcs = new HashSet<>();
         rpcs.add(cend);
         return make(false,rpcs);
     }
 
-    public  static final SONTypeRPC BOT = make(true,new HashSet<>());
-    private static final SONTypeRPC TEST2 = constant(2);
-    private static final SONTypeRPC TEST3 = constant(2);
+    public  static final TypeRPC BOT = make(true,new HashSet<>());
+    private static final TypeRPC TEST2 = constant(2);
+    private static final TypeRPC TEST3 = constant(2);
 
-    public static void gather(ArrayList<SONType> ts) { ts.add(BOT); ts.add(TEST2); ts.add(TEST3); }
+    public static void gather(ArrayList<Type> ts) { ts.add(BOT); ts.add(TEST2); ts.add(TEST3); }
 
     @Override public String str() {
         if( _rpcs.isEmpty() )
@@ -50,8 +50,8 @@ public static SONTypeRPC constant(int cend) {
     }
 
     @Override
-    public SONTypeRPC xmeet(SONType other) {
-        SONTypeRPC rpc = (SONTypeRPC)other;
+    public TypeRPC xmeet(Type other) {
+        TypeRPC rpc = (TypeRPC)other;
         // If the two sets are equal, the _any must be unequal (invariant),
         // so they cancel and all bits are set.
         if( _rpcs.equals(rpc._rpcs) )
@@ -84,13 +84,13 @@ public SONTypeRPC xmeet(SONType other) {
     }
 
     @Override
-    public SONType dual() { return make(!_any,_rpcs); }
+    public Type dual() { return make(!_any,_rpcs); }
 
     @Override
     int hash() { return _rpcs.hashCode() ^ (_any ? -1 : 0) ; }
     @Override
-    public boolean eq( SONType t ) {
-        SONTypeRPC rpc = (SONTypeRPC)t; // Contract
+    public boolean eq( Type t ) {
+        TypeRPC rpc = (TypeRPC)t; // Contract
         return _any==rpc._any && _rpcs.equals(rpc._rpcs);
     }
 
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeStruct.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/TypeStruct.java
similarity index 84%
rename from seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeStruct.java
rename to seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/TypeStruct.java
index cd69999..fa44da4 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeStruct.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/TypeStruct.java
@@ -1,13 +1,13 @@
 package com.compilerprogramming.ezlang.compiler.sontypes;
 
 import com.compilerprogramming.ezlang.compiler.SB;
-import com.compilerprogramming.ezlang.compiler.Utils;
+
 import java.util.ArrayList;
 
 /**
  * Represents a struct type.
  */
-public class SONTypeStruct extends SONType {
+public class TypeStruct extends Type {
 
     // A Struct has a name and a set of fields; the fields themselves have
     // names, types and aliases.  The name has no semantic meaning, but is
@@ -25,21 +25,21 @@ public class SONTypeStruct extends SONType {
     public final String _name;
     public Field[] _fields;
 
-    public SONTypeStruct(String name, Field[] fields) {
+    public TypeStruct(String name, Field[] fields) {
         super(TSTRUCT);
         _name = name;
         _fields = fields;
     }
 
     // All fields directly listed
-    public static SONTypeStruct make(String name, Field... fields) { return new SONTypeStruct(name, fields).intern(); }
-    public static final SONTypeStruct TOP = make("$TOP",new Field[0]);
-    public static final SONTypeStruct BOT = make("$BOT",new Field[0]);
+    public static TypeStruct make(String name, Field... fields) { return new TypeStruct(name, fields).intern(); }
+    public static final TypeStruct TOP = make("$TOP",new Field[0]);
+    public static final TypeStruct BOT = make("$BOT",new Field[0]);
     //public static final SONTypeStruct TEST = make("test",new Field[]{Field.TEST});
     // Forward-ref version
-    public static SONTypeStruct makeFRef(String name) { return make(name, (Field[])null); }
+    public static TypeStruct makeFRef(String name) { return make(name, (Field[])null); }
     // Make a read-only version
-    @Override public SONTypeStruct makeRO() {
+    @Override public TypeStruct makeRO() {
         if( isFinal() ) return this;
         Field[] flds = new Field[_fields.length];
         for( int i=0; i<flds.length; i++ )
@@ -48,18 +48,18 @@ public SONTypeStruct(String name, Field[] fields) {
     }
 
     // Array
-    public static SONTypeStruct makeAry(SONTypeInteger len, int lenAlias, SONType body, int bodyAlias) {
-        assert body instanceof SONTypeInteger || body instanceof SONTypeFloat || (body instanceof SONTypeNil tn && tn.nullable());
-        assert len.isa(SONTypeInteger.U32);
+    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));
     }
     // Array
-    public static SONTypeStruct makeArray(SONTypeInteger len, int lenAlias, SONType body, int bodyAlias) {
-        assert body instanceof SONTypeInteger || body instanceof SONTypeFloat || (body instanceof SONTypeNil tn && tn.nullable());
-        assert len.isa(SONTypeInteger.U32);
-        return new SONTypeStruct("[" + body.str() + "]",
+    public static TypeStruct makeArray(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 new TypeStruct("[" + body.str() + "]",
                 new Field[] {
                 new Field("#" ,len , lenAlias,true ),
                 new Field("[]",body,bodyAlias,false) });
@@ -73,7 +73,7 @@ public static SONTypeStruct makeArray(SONTypeInteger len, int lenAlias, SONType
 //
 //    private static final SONTypeStruct ARY = makeAry(SONTypeInteger.U32,-1, SONTypeInteger.BOT,-2);
 
-    public static void gather(ArrayList<SONType> ts) { /* ts.add(TEST); ts.add(BOT); ts.add(S1); ts.add(S2); ts.add(ARY); */ }
+    public static void gather(ArrayList<Type> ts) { /* ts.add(TEST); ts.add(BOT); ts.add(S1); ts.add(S2); ts.add(ARY); */ }
 
     // Find field index by name
     public int find(String fname) {
@@ -97,8 +97,8 @@ public int findAlias( int alias ) {
 
 
     @Override
-    SONType xmeet(SONType t) {
-        SONTypeStruct that = (SONTypeStruct) t;
+    Type xmeet(Type t) {
+        TypeStruct that = (TypeStruct) t;
         if( this==TOP ) return that;
         if( that==TOP ) return this;
         if( this==BOT ) return BOT;
@@ -127,7 +127,7 @@ SONType xmeet(SONType t) {
     }
 
     @Override
-    public SONTypeStruct dual() {
+    public TypeStruct dual() {
         if( this==TOP ) return BOT;
         if( this==BOT ) return TOP;
         if( _fields == null ) return this;
@@ -138,7 +138,7 @@ public SONTypeStruct dual() {
     }
 
     // Keeps the same struct, but lower-bounds all fields.
-    @Override public SONTypeStruct glb() {
+    @Override public TypeStruct glb() {
         if( _glb() ) return this;
         // Need to glb each field
         Field[] flds = new Field[_fields.length];
@@ -166,8 +166,8 @@ private boolean _glb() {
     }
 
     @Override
-    boolean eq(SONType t) {
-        SONTypeStruct ts = (SONTypeStruct)t; // Invariant
+    boolean eq(Type t) {
+        TypeStruct ts = (TypeStruct)t; // Invariant
         if( !_name.equals(ts._name) )
             return false;
 //        if( _fields == ts._fields ) return true;
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeTuple.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/TypeTuple.java
similarity index 50%
rename from seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeTuple.java
rename to seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/TypeTuple.java
index 73ea8f4..70f2762 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeTuple.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/TypeTuple.java
@@ -3,61 +3,61 @@
 import com.compilerprogramming.ezlang.compiler.SB;
 import java.util.ArrayList;
 
-public class SONTypeTuple extends SONType {
+public class TypeTuple extends Type {
 
-    public final SONType[] _types;
+    public final Type[] _types;
 
-    public SONTypeTuple(SONType[] types) { super(TTUPLE); _types = types; }
-    public  static SONTypeTuple make(SONType... types) { return new SONTypeTuple(types).intern(); }
+    public TypeTuple(Type[] types) { super(TTUPLE); _types = types; }
+    public  static TypeTuple make(Type... types) { return new TypeTuple(types).intern(); }
 
-    public static final SONTypeTuple BOT = new SONTypeTuple(new SONType[0]).intern();
-    public static final SONTypeTuple TOP = new SONTypeTuple(null).intern();
+    public static final TypeTuple BOT = new TypeTuple(new Type[0]).intern();
+    public static final TypeTuple TOP = new TypeTuple(null).intern();
 
     //public static final SONTypeTuple TEST = make(SONTypeInteger.BOT, SONTypeMemPtr.TEST);
-    public static final SONTypeTuple START= make(SONType.CONTROL, SONTypeMem.TOP, SONTypeInteger.BOT);
-    public static final SONTypeTuple MAIN = make(SONTypeInteger.BOT);
-    public static final SONTypeTuple RET  = make(SONType.CONTROL, SONTypeMem.BOT, SONType.BOTTOM);
-    public static final SONTypeTuple CALLOC = make(SONTypeInteger.BOT,SONTypeInteger.BOT);
+    public static final TypeTuple START= make(Type.CONTROL, TypeMem.TOP, TypeInteger.BOT);
+    public static final TypeTuple MAIN = make(TypeInteger.BOT);
+    public static final TypeTuple RET  = make(Type.CONTROL, TypeMem.BOT, Type.BOTTOM);
+    public static final TypeTuple CALLOC = make(TypeInteger.BOT, TypeInteger.BOT);
 
-    public static final SONTypeTuple IF_BOTH    = make(new SONType[]{SONType. CONTROL, SONType. CONTROL});
-    public static final SONTypeTuple IF_NEITHER = make(new SONType[]{SONType.XCONTROL, SONType.XCONTROL});
-    public static final SONTypeTuple IF_TRUE    = make(new SONType[]{SONType. CONTROL, SONType.XCONTROL});
-    public static final SONTypeTuple IF_FALSE   = make(new SONType[]{SONType.XCONTROL, SONType. CONTROL});
+    public static final TypeTuple IF_BOTH    = make(new Type[]{Type. CONTROL, Type. CONTROL});
+    public static final TypeTuple IF_NEITHER = make(new Type[]{Type.XCONTROL, Type.XCONTROL});
+    public static final TypeTuple IF_TRUE    = make(new Type[]{Type. CONTROL, Type.XCONTROL});
+    public static final TypeTuple IF_FALSE   = make(new Type[]{Type.XCONTROL, Type. CONTROL});
 
-    public  static void gather(ArrayList<SONType> ts) { ts.add(BOT); /* ts.add(TEST); */ ts.add(START); ts.add(MAIN); ts.add(IF_TRUE); }
+    public  static void gather(ArrayList<Type> ts) { ts.add(BOT); /* ts.add(TEST); */ ts.add(START); ts.add(MAIN); ts.add(IF_TRUE); }
 
     @Override
-    SONType xmeet(SONType other) {
-        SONTypeTuple tt = (SONTypeTuple)other;     // contract from xmeet
+    Type xmeet(Type other) {
+        TypeTuple tt = (TypeTuple)other;     // contract from xmeet
         if( this==TOP ) return other;
         if( tt  ==TOP ) return this ;
         if( _types.length != tt._types.length )
             return BOT;
-        SONType[] ts = new SONType[_types.length];
+        Type[] ts = new Type[_types.length];
         for( int i=0; i<_types.length; i++ )
             ts[i] = _types[i].meet(tt._types[i]);
         return make(ts);
     }
 
-    @Override public SONTypeTuple dual() {
+    @Override public TypeTuple dual() {
         if( this==TOP ) return BOT;
         if( this==BOT ) return TOP;
-        SONType[] ts = new SONType[_types.length];
+        Type[] ts = new Type[_types.length];
         for( int i=0; i<_types.length; i++ )
             ts[i] = _types[i].dual();
         return make(ts);
     }
 
     @Override
-    public SONType glb() {
-        SONType[] ts = new SONType[_types.length];
+    public Type glb() {
+        Type[] ts = new Type[_types.length];
         for( int i=0; i<_types.length; i++ )
             ts[i] = _types[i].glb();
         return make(ts);
     }
 
     @Override public boolean isConstant() {
-        for( SONType t : _types )
+        for( Type t : _types )
             if( !t.isConstant() )
                 return false;
         return true;
@@ -66,12 +66,12 @@ public SONType glb() {
     @Override public int log_size() {
         assert isConstant();
         int log_size = 0;
-        for( SONType t : _types )
+        for( Type t : _types )
             log_size = Math.max(log_size,t.log_size());
         return log_size;
     }
 
-    public SONType ret() { assert _types.length==3; return _types[2]; }
+    public Type ret() { assert _types.length==3; return _types[2]; }
 
     @Override public String str() { return print(new SB()).toString(); }
 
@@ -79,7 +79,7 @@ public SONType glb() {
         if( this==TOP ) return sb.p("[TOP]");
         if( this==BOT ) return sb.p("[BOT]");
         sb.p("[  ");
-        for( SONType t : _types )
+        for( Type t : _types )
             t.print(sb).p(", ");
         return sb.unchar(2).p("]");
     }
@@ -87,7 +87,7 @@ public SONType glb() {
         if( this==TOP ) return sb.p("[TOP]");
         if( this==BOT ) return sb.p("[BOT]");
         sb.p("[  ");
-        for( SONType t : _types )
+        for( Type t : _types )
             t.gprint(sb).p(", ");
         return sb.unchar(2).p("]");
     }
@@ -95,13 +95,13 @@ public SONType glb() {
     @Override
     int hash() {
         int sum = 0;
-        if( _types!=null ) for( SONType type : _types ) sum ^= type.hashCode();
+        if( _types!=null ) for( Type type : _types ) sum ^= type.hashCode();
         return sum;
     }
 
     @Override
-    boolean eq( SONType t ) {
-        SONTypeTuple tt = (SONTypeTuple)t; // Contract
+    boolean eq( Type t ) {
+        TypeTuple tt = (TypeTuple)t; // Contract
         if( _types==null && tt._types==null ) return true;
         if( _types==null || tt._types==null ) return false;
         if( _types.length != tt._types.length ) return false;

From e8eb5df1f7dd5e3a3880aaf1bbba549ea5672a7e Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Sun, 27 Jul 2025 20:09:44 +0100
Subject: [PATCH 094/119] WIP update

---
 .../ezlang/compiler/Compiler.java             |   2 +-
 .../ezlang/compiler/Var.java                  |   4 -
 .../ezlang/compiler/codegen/CodeGen.java      |  41 +++---
 .../ezlang/compiler/codegen/ElfFile.java      |   9 +-
 .../ezlang/compiler/codegen/Encoding.java     | 118 +++++++++++++-----
 .../compiler/codegen/GlobalCodeMotion.java    |  17 ++-
 .../ezlang/compiler/codegen/RegAlloc.java     |  28 +++--
 .../ezlang/compiler/nodes/AddNode.java        |  56 +++------
 .../ezlang/compiler/nodes/AndNode.java        |  27 ++--
 .../ezlang/compiler/nodes/ArithNode.java      |  98 +++++++++++++++
 .../ezlang/compiler/nodes/BoolNode.java       |  22 +++-
 .../ezlang/compiler/nodes/CFGNode.java        |  14 ++-
 .../ezlang/compiler/nodes/CallEndMach.java    |  36 ++++++
 .../ezlang/compiler/nodes/CallEndNode.java    |  39 ++----
 .../ezlang/compiler/nodes/CallNode.java       |  10 +-
 .../{cpus/arm/CastARM.java => CastMach.java}  |  13 +-
 .../ezlang/compiler/nodes/CastNode.java       |   5 +
 .../ezlang/compiler/nodes/ConstantNode.java   |   4 +-
 .../ezlang/compiler/nodes/DivNode.java        |  30 +----
 .../ezlang/compiler/nodes/ExternNode.java     |  19 +++
 .../ezlang/compiler/nodes/FunNode.java        |  13 +-
 .../ezlang/compiler/nodes/IfNode.java         |   2 +-
 .../ezlang/compiler/nodes/LoadNode.java       |   6 +
 .../ezlang/compiler/nodes/LogicalNode.java    |  28 -----
 .../ezlang/compiler/nodes/LoopNode.java       |   2 +-
 .../ezlang/compiler/nodes/MemMergeNode.java   |  39 ++++--
 .../ezlang/compiler/nodes/MemOpNode.java      |   3 +-
 .../ezlang/compiler/nodes/MinusNode.java      |   2 +-
 .../ezlang/compiler/nodes/MulNode.java        |  36 +-----
 .../ezlang/compiler/nodes/NewNode.java        |   6 +-
 .../ezlang/compiler/nodes/Node.java           |  14 ++-
 .../ezlang/compiler/nodes/OrNode.java         |  18 +--
 .../ezlang/compiler/nodes/ParmNode.java       |   4 +
 .../ezlang/compiler/nodes/PhiNode.java        |  11 +-
 .../riscv/CastRISC.java => ReadOnlyMach.java} |  13 +-
 .../ezlang/compiler/nodes/ReadOnlyNode.java   |   1 +
 .../ezlang/compiler/nodes/ReturnNode.java     |   7 +-
 .../ezlang/compiler/nodes/SarNode.java        |  25 ++--
 .../ezlang/compiler/nodes/ScopeNode.java      |  39 +++---
 .../ezlang/compiler/nodes/ShlNode.java        |  26 ++--
 .../ezlang/compiler/nodes/ShrNode.java        |  21 +---
 .../ezlang/compiler/nodes/StartNode.java      |   2 +-
 .../ezlang/compiler/nodes/StopNode.java       |  20 ++-
 .../ezlang/compiler/nodes/StoreNode.java      |  29 ++++-
 .../ezlang/compiler/nodes/SubNode.java        |  38 ++----
 .../ezlang/compiler/nodes/XorNode.java        |  17 +--
 .../compiler/nodes/cpus/arm/CallARM.java      |   9 +-
 .../compiler/nodes/cpus/arm/CallEndARM.java   |   8 --
 .../compiler/nodes/cpus/arm/FloatARM.java     |   2 -
 .../compiler/nodes/cpus/arm/IntARM.java       |   2 +-
 .../compiler/nodes/cpus/arm/TFPARM.java       |  13 +-
 .../compiler/nodes/cpus/arm/TMPARM.java       |  53 ++++++++
 .../ezlang/compiler/nodes/cpus/arm/arm.java   |  94 +++++++-------
 .../nodes/cpus/riscv/CallEndRISC.java         |   8 --
 .../compiler/nodes/cpus/riscv/CallRISC.java   |  20 +--
 .../compiler/nodes/cpus/riscv/FltRISC.java    |   3 +-
 .../compiler/nodes/cpus/riscv/ImmRISC.java    |   3 +-
 .../compiler/nodes/cpus/riscv/Int8RISC.java   |  47 +++++++
 .../compiler/nodes/cpus/riscv/IntRISC.java    |   2 +-
 .../ezlang/compiler/nodes/cpus/riscv/LUI.java |   2 +-
 .../compiler/nodes/cpus/riscv/MemOpRISC.java  |  12 +-
 .../compiler/nodes/cpus/riscv/SraIRISC.java   |   4 +-
 .../compiler/nodes/cpus/riscv/TFPRISC.java    |  11 +-
 .../compiler/nodes/cpus/riscv/TMPRISC.java    |  41 ++++++
 .../compiler/nodes/cpus/riscv/XorIRISC.java   |   1 +
 .../compiler/nodes/cpus/riscv/riscv.java      |  34 ++---
 .../nodes/cpus/x86_64_v2/AddIX86.java         |   9 ++
 .../nodes/cpus/x86_64_v2/CallEndX86.java      |   8 --
 .../nodes/cpus/x86_64_v2/CallX86.java         |  10 +-
 .../nodes/cpus/x86_64_v2/CastX86.java         |  16 ---
 .../nodes/cpus/x86_64_v2/CmpMemX86.java       |   4 +-
 .../compiler/nodes/cpus/x86_64_v2/CmpX86.java |   6 +
 .../compiler/nodes/cpus/x86_64_v2/DivX86.java |   2 +-
 .../compiler/nodes/cpus/x86_64_v2/ImmX86.java |   3 +-
 .../compiler/nodes/cpus/x86_64_v2/IntX86.java |   2 +-
 .../nodes/cpus/x86_64_v2/LoadX86.java         |   2 +-
 .../nodes/cpus/x86_64_v2/MemAddX86.java       |   5 +-
 .../nodes/cpus/x86_64_v2/MulIX86.java         |  35 +++++-
 .../compiler/nodes/cpus/x86_64_v2/RegX86.java |   1 +
 .../compiler/nodes/cpus/x86_64_v2/SetX86.java |   4 +-
 .../nodes/cpus/x86_64_v2/StoreX86.java        |   2 +-
 .../compiler/nodes/cpus/x86_64_v2/TFPX86.java |   3 +-
 .../compiler/nodes/cpus/x86_64_v2/TMPX86.java |  35 ++++++
 .../nodes/cpus/x86_64_v2/x86_64_v2.java       |  36 +++---
 .../ezlang/compiler/print/ASMPrinter.java     |  90 +++++++------
 .../ezlang/compiler/sontypes/Field.java       |  14 ++-
 .../ezlang/compiler/sontypes/Type.java        |  10 +-
 .../ezlang/compiler/sontypes/TypeFloat.java   |   2 +-
 .../ezlang/compiler/sontypes/TypeFunPtr.java  |  20 +--
 .../ezlang/compiler/sontypes/TypeInteger.java |  10 +-
 .../ezlang/compiler/sontypes/TypeMem.java     |   8 +-
 .../ezlang/compiler/sontypes/TypeMemPtr.java  |   5 +-
 .../ezlang/compiler/sontypes/TypeNil.java     |   2 +-
 .../ezlang/compiler/sontypes/TypePtr.java     |  11 +-
 .../ezlang/compiler/sontypes/TypeStruct.java  |  50 +++++---
 .../ezlang/compiler/sontypes/TypeTuple.java   |  16 +--
 96 files changed, 1098 insertions(+), 706 deletions(-)
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ArithNode.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CallEndMach.java
 rename seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/{cpus/arm/CastARM.java => CastMach.java} (58%)
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ExternNode.java
 delete mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/LogicalNode.java
 rename seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/{cpus/riscv/CastRISC.java => ReadOnlyMach.java} (57%)
 delete mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CallEndARM.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/TMPARM.java
 delete mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CallEndRISC.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/Int8RISC.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/TMPRISC.java
 delete mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CallEndX86.java
 delete mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CastX86.java
 create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/TMPX86.java

diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java
index 87b9624..b184240 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java
@@ -656,7 +656,7 @@ private Node compileCallExpr(AST.CallExpr callExpr) {
         if( expr._type == Type.NIL )
             throw error("Calling a null function pointer");
         if( !(expr instanceof FRefNode) && !expr._type.isa(TypeFunPtr.BOT) )
-            throw error("Expected a function but got "+expr._type.glb().str());
+            throw error("Expected a function but got "+expr._type.glb(false).str());
         expr.keep();            // Keep while parsing args
 
         Ary<Node> args = new Ary<Node>(Node.class);
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/Var.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/Var.java
index 29327fa..65d82ee 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/Var.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/Var.java
@@ -30,10 +30,6 @@ public Type type() {
         Type def = Compiler.TYPES.get(((TypeMemPtr)_type)._obj._name);
         return (_type=_type.meet(def));
     }
-    public Type lazyGLB() {
-        Type t = type();
-        return t instanceof TypeMemPtr ? t : t.glb();
-    }
 
     // Forward reference variables (not types) must be BOTTOM and
     // distinct from inferred variables
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/CodeGen.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/CodeGen.java
index b5666e6..e1dfe42 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/CodeGen.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/CodeGen.java
@@ -23,7 +23,7 @@ public enum Phase {
         Opto,                   // Run ideal optimizations
         TypeCheck,              // Last check for bad programs
         LoopTree,               // Build a loop tree; break infinite loops
-        InstSelect,             // Convert to target hardware nodes
+        Select,                 // Convert to target hardware nodes
         Schedule,               // Global schedule (code motion) nodes
         LocalSched,             // Local schedule
         RegAlloc,               // Register allocation
@@ -40,7 +40,7 @@ public enum Phase {
 
     // ---------------------------
     public CodeGen( String src ) { this(src, TypeInteger.BOT, 123L ); }
-    public CodeGen(String src, TypeInteger arg, long workListSeed ) {
+    public CodeGen( String src, TypeInteger arg, long workListSeed ) {
         CODE = this;
         _phase = null;
         _callingConv = null;
@@ -57,14 +57,15 @@ public CodeGen(String src, TypeInteger arg, long workListSeed ) {
     public CodeGen driver( Phase phase ) { return driver(phase,null,null); }
     public CodeGen driver( Phase phase, String cpu, String callingConv ) {
         if( _phase==null )                       parse();
-        if( _phase.ordinal() < phase.ordinal() ) opto();
-        if( _phase.ordinal() < phase.ordinal() ) typeCheck();
-        if( _phase.ordinal() < phase.ordinal() ) loopTree();
-        if( _phase.ordinal() < phase.ordinal() && cpu != null ) instSelect(cpu,callingConv);
-        if( _phase.ordinal() < phase.ordinal() ) GCM();
-        if( _phase.ordinal() < phase.ordinal() ) localSched();
-        if( _phase.ordinal() < phase.ordinal() ) regAlloc();
-        if( _phase.ordinal() < phase.ordinal() ) encode();
+        int p1 = phase.ordinal();
+        if( _phase.ordinal() < p1 && _phase.ordinal() < Phase.Opto      .ordinal() ) opto();
+        if( _phase.ordinal() < p1 && _phase.ordinal() < Phase.TypeCheck .ordinal() ) typeCheck();
+        if( _phase.ordinal() < p1 && _phase.ordinal() < Phase.LoopTree  .ordinal() ) loopTree();
+        if( _phase.ordinal() < p1 && _phase.ordinal() < Phase.Select    .ordinal() && cpu != null ) instSelect(cpu,callingConv);
+        if( _phase.ordinal() < p1 && _phase.ordinal() < Phase.Schedule  .ordinal() ) GCM();
+        if( _phase.ordinal() < p1 && _phase.ordinal() < Phase.LocalSched.ordinal() ) localSched();
+        if( _phase.ordinal() < p1 && _phase.ordinal() < Phase.RegAlloc  .ordinal() ) regAlloc();
+        if( _phase.ordinal() < p1 && _phase.ordinal() < Phase.Encoding  .ordinal() ) encode();
         return this;
     }
 
@@ -123,7 +124,7 @@ public int iDepthFrom(int idepth) {
     // 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 ) {
+    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
@@ -229,7 +230,7 @@ public CodeGen loopTree() {
         _phase = Phase.LoopTree;
         long t0 = System.currentTimeMillis();
         // Build the loop tree, fix never-exit loops
-        _start.buildLoopTree(_stop);
+        _start.buildLoopTree(_start,_stop);
         _tLoopTree = (int)(System.currentTimeMillis() - t0);
         return this;
     }
@@ -257,7 +258,7 @@ public CodeGen loopTree() {
     public CodeGen instSelect( String cpu, String callingConv ) { return instSelect(cpu,callingConv,PORTS); }
     public CodeGen instSelect( String cpu, String callingConv, String base ) {
         assert _phase.ordinal() == Phase.LoopTree.ordinal();
-        _phase = Phase.InstSelect;
+        _phase = Phase.Select;
 
         _callingConv = callingConv;
 
@@ -288,14 +289,14 @@ public CodeGen instSelect( String cpu, String callingConv, String base ) {
         _rpcMask = new RegMask(_mach.rpc());
         _retMasks[3] = _rpcMask;
 
-
         // Convert to machine ops
         long t0 = System.currentTimeMillis();
         _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);
+        StartNode start = (StartNode)map.get(_start);
+        _start = start==null ? new StartNode(_start) : start;
         _instOuts(_stop,visit());
         _visit.clear();
         _tInsSel = (int)(System.currentTimeMillis() - t0);
@@ -326,8 +327,6 @@ private Node _instSelect( Node n, IdentityHashMap<Node,Node> map ) {
         if( x instanceof MachNode mach )
             mach.postSelect(this);  // Post selection action
 
-        // Updates forward edges only.
-        n._outputs.clear();
         return x;
     }
 
@@ -351,7 +350,7 @@ private void _instOuts( Node n, BitSet visit ) {
     // Global schedule (code motion) nodes
     public CodeGen GCM() { return GCM(false); }
     public CodeGen GCM( boolean show) {
-        assert _phase.ordinal() <= Phase.InstSelect.ordinal();
+        assert _phase.ordinal() <= Phase.Select.ordinal();
         _phase = Phase.Schedule;
         long t0 = System.currentTimeMillis();
 
@@ -451,8 +450,10 @@ public CodeGen exportELF(String fname) throws IOException {
     String printCFG() {
         if( _cfg==null ) return "no CFG";
         SB sb = new SB();
-        for( CFGNode cfg : _cfg )
-            IRPrinter.printLine(cfg,sb);
+        for( CFGNode cfg : _cfg ) {
+            sb.fix(8,""+cfg._idepth);
+            IRPrinter.printLine( cfg, sb );
+        }
         return sb.toString();
     }
 
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/ElfFile.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/ElfFile.java
index 54bd967..d5b3462 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/ElfFile.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/ElfFile.java
@@ -273,8 +273,6 @@ public void export(String fname) throws IOException {
         int start_global = num+1; // Add one to skip the final .rela.text local symbol
         for( Symbol a: symbols._symbols )
             a._index = start_global++;
-        int bigConIdx = start_global;
-        start_global += enc._bigCons.size();
 
         // create .text relocations
         DataSection text_rela = new DataSection(".rela.text", 4 /* SHT_RELA */);
@@ -298,10 +296,10 @@ public void export(String fname) throws IOException {
 
         // Write relocations for the constant pool
         for( Encoding.Relo relo : enc._bigCons.values() ) {
-            Symbol glob = new Symbol("GLOB$"+bigConIdx, rdata._index, SYM_BIND_GLOBAL, SYM_TYPE_FUNC);
+            Symbol glob = new Symbol("CPOOL$"+start_global, rdata._index, SYM_BIND_GLOBAL, SYM_TYPE_FUNC);
             glob._value = relo._target;
-            glob._size = 1 << relo._t.log_size();
-            glob._index = bigConIdx++;
+            glob._size = relo._t.size();
+            glob._index = start_global++;
             symbols.push(glob);
             write8(text_rela._contents, relo._opStart+relo._off);
             write8(text_rela._contents, ((long)glob._index << 32L) | relo._elf );
@@ -313,6 +311,7 @@ public void export(String fname) throws IOException {
         text_rela._info = text._index;
         pushSection(text_rela);
 
+        // Final .rela.text symbol
         Symbol sym = new Symbol(text_rela._name, num++, SYM_BIND_LOCAL, SYM_TYPE_SECTION);
         sym._name_pos = text_rela._name_pos;
         sym._size = text_rela.size();
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/Encoding.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/Encoding.java
index b37e8fe..68c7f7f 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/Encoding.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/Encoding.java
@@ -98,7 +98,7 @@ static void padN(int n, BAOS bits) {
     }
 
     // Convenience for writing log-N
-    static void addN(int log, Type t, BAOS bits ) {
+    static void addN( int log, Type t, BAOS bits ) {
         long x = t instanceof TypeInteger ti
             ? ti.value()
             : log==3
@@ -138,14 +138,14 @@ public void jump( CFGNode jmp, CFGNode dst ) {
 
 
     final HashMap<Node,String> _externals = new HashMap<>();
-    public Encoding external( Node call, String extern ) {
-        _externals.put(call,extern);
+    public Encoding external( Node n, String extern ) {
+        _externals.put(n,extern);
         return this;
     }
 
     // Store t as a 32/64 bit constant in the code space; generate RIP-relative
     // addressing to load it
-    public void largeConstant(Node relo, Type t, int off, int elf ) {
+    public void largeConstant( Node relo, Type t, int off, int elf ) {
         assert t.isConstant();
         assert (byte)off == off;
         assert (byte)elf == elf;
@@ -183,12 +183,10 @@ private void basicBlockLayout() {
         Ary<CFGNode> rpo = new Ary<>(CFGNode.class);
         rpos.put(_code._start.loop(),rpo);
         BitSet visit = _code.visit();
-        //IdentityHashMap<CFGNode,LoopNode> looptail = new IdentityHashMap<>();
         rpo.add(_code._stop);
         for( Node n : _code._start._outputs )
             if( n instanceof FunNode fun ) {
                 int x = rpo._len;
-                //_rpo_cfg2(fun, visit, rpo, looptail);
                 _rpo_cfg(fun, visit, rpos );
                 assert rpo.at(x) instanceof ReturnNode;
             }
@@ -299,7 +297,7 @@ private static boolean shouldInvert(CFGNode t, CFGNode f, int bld) {
     private static boolean forwardsEmptyScan( CFGNode c, int bld ) {
         if( c.nOuts()!=1 || c.loopDepth()!=bld ) return false;
         return c.uctrl() instanceof RegionNode cfg &&
-            (cfg instanceof LoopNode || forwardsEmptyScan(cfg,bld));
+            ((cfg instanceof LoopNode && cfg._ltree==c._ltree) || forwardsEmptyScan(cfg,bld));
     }
 
     // Is the CFG from "next" to the end empty?  This means jumping to "next"
@@ -387,6 +385,23 @@ private void compactShortForm() {
         }
 
 
+        // Go again, inserting padding on function headers.  Since no
+        // short-jumps span function headers, the padding will not make any
+        // short jumps fail.
+        for( int i=0; i<len; i++ ) {
+            CFGNode bb = _code._cfg.at(i);
+            // Functions pad to align 16
+            if( bb instanceof FunNode ) {
+                int newStart = _opStart[bb._nid]+slide;
+                slide += (newStart+15 & -16)-newStart;
+            }
+            _opStart[bb._nid] += slide;
+            for( Node n : bb._outputs )
+                if( n instanceof MachNode && !(n instanceof CFGNode) )
+                    _opStart[n._nid] += slide;
+        }
+
+
         // Copy/slide the bits to make space for all the longer branches
         int grow = _opStart[_code._cfg.at(len-1)._nid] - oldStarts[len-1];
         if( grow > 0 ) {        // If no short-form ops, nothing to do here
@@ -422,47 +437,90 @@ void patchLocalRelocations() {
     void writeConstantPool( BAOS bits, boolean patch ) {
         padN(16,bits);
 
+        // radix sort the big constants by alignment
+        Ary<Relo>[] raligns = new Ary[5];
+        for( Node op : _bigCons.keySet() ) {
+            Relo relo = _bigCons.get(op);
+            int align = relo._t.alignment();
+            Ary<Relo> relos = raligns[align]==null ? (raligns[align]=new Ary<>(Relo.class)) : raligns[align];
+            relos.add(relo);
+        }
+
+
+        // Types can be used more than once; collapse the dups
         HashMap<Type,Integer> targets = new HashMap<>();
 
-        // By log size
-        for( int log = 3; log >= 0; log-- ) {
-            // Write the 8-byte constants
-            for( Node op : _bigCons.keySet() ) {
-                Relo relo = _bigCons.get(op);
-                if( relo._t.log_size()==log ) {
-                    // Map from relo to constant start and patch
-                    Integer target = targets.get(relo._t);
-                    if( target==null ) {
-                        targets.put(relo._t,target = bits.size());
-                        // Put constant into code space.
-                        if( relo._t instanceof TypeTuple tt ) // Constant tuples put all entries
-                            for( Type tx : tt._types )
-                                addN(log,tx,bits);
-                        else
-                            addN(log,relo._t,bits);
+        // By alignment
+        for( int align = 4; align >= 0; align-- ) {
+            Ary<Relo> relos = raligns[align];
+            if( relos == null ) continue;
+            for( Relo relo : relos ) {
+                // Map from relo to constant start and patch
+                Integer target = targets.get(relo._t);
+                if( target==null ) {
+                    targets.put(relo._t,target = bits.size());
+                    // Write constant into constant pool
+                    switch( relo._t ) {
+                    case TypeTuple  tt -> cpool(align,bits,tt);
+                    case TypeStruct ts -> cpool(bits,ts);
+                    // Simple primitive (e.g. larger int, float)
+                    default -> addN(align,relo._t,bits);
                     }
-                    relo._target = target;
-                    relo._opStart= _opStart[op._nid];
-                    // Go ahead and locally patch in-memory
-                    if( patch )
-                        ((RIPRelSize)op).patch(this, relo._opStart, _opLen[op._nid], relo._target - relo._opStart);
                 }
+                // Record target address and opcode start
+                relo._target = target;
+                relo._opStart= _opStart[relo._op._nid];
+                // Go ahead and locally patch in-memory
+                if( patch )
+                    ((RIPRelSize)relo._op).patch(this, relo._opStart, _opLen[relo._op._nid], relo._target - relo._opStart);
             }
         }
     }
 
+    // Constant tuples put all entries at same alignment
+    private void cpool(int align, BAOS bits, TypeTuple tt) {
+        for( Type tx : tt._types )
+            addN(align,tx,bits);
+    }
+
+    // Structs use internal field layout
+    private void cpool( BAOS bits, TypeStruct ts ) {
+        // Field order by offset
+        int[] layout = ts.layout();
+        int off=0; // offset in the struct
+        for( int fn=0; fn<ts._fields.length; fn++ ) {
+            Field f  = ts._fields[layout[fn]];
+            int foff = ts. offset(layout[fn]);
+            // Pad up to field
+            while( off < foff ) { bits.write(0); off++; };
+            // Constant array fields are special
+            // FIXME Dibyendu
+//            if( f._fname=="[]" ) {   // Must be a constant array
+//                ts._con.write(bits); // Write the constant array bits
+//                off += ts._con.len();
+//            } else {
+//                int log = f._type.log_size();
+//                if( f._fname=="#" )  addN(log,ts._con.len(),bits); // Must be a constant array
+//                else                 addN(log,f._type      ,bits);
+//                off += 1<<log;
+//            }
+        }
+    }
+
 
     // A series of libc/external calls that Simple can link against in a JIT.
     // Since no runtime in the JVM process, using magic numbers for the CPU
     // emulators to pick up on.
-    public static int SENTINAL_CALLOC = -4;
+    public static int SENTINEL_CALLOC = -4;
+    public static int SENTINEL_WRITE  = -8;
 
     void patchGlobalRelocations() {
         for( Node src : _externals.keySet() ) {
             int start  = _opStart[src._nid];
             String dst =  _externals.get(src);
             int target = switch( dst ) {
-            case "calloc" -> SENTINAL_CALLOC;
+            case "calloc" -> SENTINEL_CALLOC;
+            case "write"  -> SENTINEL_WRITE ;
             default -> throw Utils.TODO();
             };
             ((RIPRelSize)src).patch(this, start, _opLen[src._nid], target - start);
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/GlobalCodeMotion.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/GlobalCodeMotion.java
index 1611468..f6f2f2c 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/GlobalCodeMotion.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/GlobalCodeMotion.java
@@ -32,7 +32,6 @@ public static void buildCFG( CodeGen code ) {
 
     // Post-Order of CFG
     private static void _rpo_cfg(CFGNode def, Node use, BitSet visit, Ary<CFGNode> rpo) {
-        if( use instanceof CallNode call ) call.unlink_all();
         if( !(use instanceof CFGNode cfg) || visit.get(cfg._nid) )
             return;             // Been there, done that
         if( def instanceof ReturnNode && use instanceof CallEndNode )
@@ -178,14 +177,22 @@ private static void breadth(Node stop, Node[] ns, CFGNode[] late) {
             }
 
             // Walk all inputs and put on worklist, as their last-use might now be done
-            for( Node def : n._inputs )
-                if( def!=null && late[def._nid]==null ) {
+            for( Node def : n._inputs ) {
+                if( def!=null && late[def._nid]==null )
                     work.push(def);
-                    // if the def has a load use, maybe the load can fire
+                // if the def has a load use, maybe the load can fire
+                if( def!=null )
                     for( Node out : def._outputs )
                         if( out instanceof MemOpNode ld && ld._isLoad && late[ld._nid]==null )
                             work.push(ld);
+            }
+
+            if( n instanceof LoopNode loop ) {
+                for( Node phi : loop._outputs ) {
+                    if( phi instanceof PhiNode && late[phi._nid]==null )
+                        work.push(phi);
                 }
+            }
         }
     }
 
@@ -274,7 +281,7 @@ private static CFGNode find_anti_dep(CFGNode lca, MemOpNode load, CFGNode early,
         for( Node mem : load.mem()._outputs ) {
             switch( mem ) {
             case MemOpNode st:
-                if( !st._isLoad ) {
+                if( !st._isLoad && load._alias == st._alias ) {
                     assert late[mem._nid] != null;
                     lca = anti_dep( load, late[mem._nid], mem.cfg0(), lca, st );
                 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/RegAlloc.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/RegAlloc.java
index 0c778d5..c75d6c4 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/RegAlloc.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/RegAlloc.java
@@ -162,7 +162,7 @@ public void regAlloc() {
             }
         // Cache reg masks for New and Call
         for( CFGNode bb : _code._cfg ) {
-            if( bb instanceof CallEndNode cend ) cend.cacheRegs(_code);
+            if( bb instanceof CallEndMach cend ) cend.cacheRegs(_code);
             for( Node n : bb._outputs )
                 if( n instanceof NewNode nnn ) nnn.cacheRegs(_code);
         }
@@ -287,24 +287,25 @@ boolean splitEmptyMaskByUse( byte round, LRG lrg ) {
         // all registers anyways.
         if( rclass._len <= 1 || ncalls > 1 ) return false;
 
-        // Split by classh
+        // Split by class
         Ary<Node> ns = new Ary<>(Node.class);
         for( RegMask rmask : rclass ) {
             ns.addAll(def._outputs);
-            Node split = makeSplit(def,"popular",round,lrg);
+            Node split = makeSplit(def,"popular",round,lrg,rmask);
             split.insertAfter( def );
             if( split.nIns()>1 ) split.setDef(1,def);
             // all uses by class to split
-            for( Node use : ns ) {
+            for( int j=0; j < def._outputs._len; j++ ) {
+                Node use = def._outputs.at(j);
                 if( use instanceof MachNode mach && use!=split ) {
                     // Check all use inputs for n, in case there's several
                     for( int i = 1; i < use.nIns(); i++ )
                         // Find a def input, and check register class
-                        if( use.in( i ) == def ) {
+                        if( use.in( i ) == def && mach.regmap( i ).overlap( rmask ) ) {
                             RegMask m = mach.regmap( i );
                             if( m!=null && mach.regmap( i ).overlap( rmask ) )
                                 // Modify use to use the split version specialized to this rclass
-                                use.setDefOrdered( i, split );
+                                { use.setDefOrdered( i, split ); j--; break; }
                         }
                 }
             }
@@ -500,15 +501,17 @@ void insertBefore(Node n, int i, String kind, byte round, LRG lrg, boolean skip)
         Node def = n.in(i);
         // Effective block for use
         CFGNode cfg = n instanceof PhiNode phi ? phi.region().cfg(i) : n.cfg0();
+        // Use-side RegMask, if available
+        RegMask umask = n instanceof MachNode mach ? mach.regmap(i) : null;
         // Def is a split ?
         if( skip && def instanceof SplitNode ) {
-            boolean singleReg = n instanceof MachNode mach && mach.regmap(i).size1();
+            boolean singleReg = umask!=null && umask.size1();
             // Same block, multiple registers, split is only used by n,
             // assume this is good enough and do not split again.
             if( cfg==def.cfg0() && def.nOuts()==1 && !singleReg )
                 return;
         }
-        makeSplit(def,kind,round,lrg).insertBefore(n, i);
+        makeSplit(def,kind,round,lrg,umask).insertBefore(n, i);
         // Skip split-of-split same block
         if( skip && def instanceof SplitNode && cfg==def.cfg0() )
             n.in(i).setDefOrdered(1,def.in(1));
@@ -535,8 +538,9 @@ public void insertAfterAndReplace( Node split, Node def, boolean must ) {
         }
     }
 
-    private Node makeSplit( Node def, String kind, byte round, LRG lrg ) {
-        Node split = def instanceof MachNode mach && mach.isClone()
+    private Node makeSplit( Node def, String kind, byte round, LRG lrg, RegMask umask ) {
+        // Clone simple constants if possible
+        Node split = def instanceof MachNode mach && mach.isClone() && (umask==null || mach.outregmap().overlap(umask))
             ? mach.copy()
             : _code._mach.split(kind,round,lrg);
         _lrgs.put(split,lrg);
@@ -554,13 +558,13 @@ private SplitNode makeSplit( String kind, byte round, LRG lrg ) {
     private void postColor() {
         int maxReg = -1;
         for( CFGNode bb : _code._cfg ) { // For all ops
-            if( bb instanceof FunNode fun )
+            if( bb instanceof FunNode )
                 maxReg = -1;   // Reset for new function
             // Compute frame size, based on arguments and largest reg seen
             if( bb instanceof ReturnNode ret )
                 ret.fun().computeFrameAdjust(_code,maxReg);
             // Raise frame size by max stack args passed, even if ignored
-            if( bb instanceof CallEndNode cend )
+            if( bb instanceof CallEndMach cend )
                 maxReg = Math.max(maxReg,cend._xslot);
 
             for( int j=0; j<bb.nOuts(); j++ ) {
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/AddNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/AddNode.java
index 1b8af35..a0808c6 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/AddNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/AddNode.java
@@ -6,42 +6,21 @@
 
 import static com.compilerprogramming.ezlang.compiler.Compiler.con;
 
-public class AddNode extends Node {
-    public AddNode(Node lhs, Node rhs) { super(null, lhs, rhs); }
+public class AddNode extends ArithNode {
+    public AddNode(Node lhs, Node rhs) { super(lhs, rhs); }
 
     @Override public String label() { return "Add"; }
-
-    @Override public String glabel() { return "+"; }
-
-    @Override
-    public StringBuilder _print1(StringBuilder sb, BitSet visited) {
-        in(1)._print0(sb.append("("), visited);
-        in(2)._print0(sb.append("+"), visited);
-        return sb.append(")");
-    }
-
-
-    @Override
-    public Type compute() {
-        Type t1 = in(1)._type, t2 = in(2)._type;
-        if( t1.isHigh() || t2.isHigh() )
-            return TypeInteger.TOP;
-        if( t1 instanceof TypeInteger i1 &&
-            t2 instanceof TypeInteger i2 ) {
-            if (i1.isConstant() && i2.isConstant())
-                return TypeInteger.constant(i1.value()+i2.value());
-            // Fold ranges like {0-1} + {2-3} into {2-4}.
-            if( !overflow(i1._min,i2._min) &&
-                !overflow(i1._max,i2._max) )
-                return TypeInteger.make(i1._min+i2._min,i1._max+i2._max);
-        }
+    @Override public String op() { return "+"; }
+
+    @Override long doOp( long x, long y ) { return x + y; }
+    @Override TypeInteger doOp(TypeInteger x, TypeInteger y) {
+        // Fold ranges like {0-1} + {2-3} into {2-4}.
+        if( !overflow(x._min,y._min) &&
+            !overflow(x._max,y._max) )
+            return TypeInteger.make(x._min+y._min,x._max+y._max);
         return TypeInteger.BOT;
     }
 
-    static boolean overflow( long x, long y ) {
-        if( (x ^    y ) < 0 ) return false; // unequal signs, never overflow
-        return (x ^ (x + y)) < 0; // sum has unequal signs, so overflow
-    }
     @Override
     public Node idealize () {
         Node lhs = in(1);
@@ -112,7 +91,7 @@ public Node idealize () {
         if( spine_cmp(lhs.in(2),rhs,this) )
             return new AddNode(new AddNode(lhs.in(1),rhs).peephole(),lhs.in(2));
 
-        return null;
+        return super.idealize();
     }
 
     // Rotation is only valid for associative ops, e.g. Add, Mul, And, Or, Xor.
@@ -121,7 +100,7 @@ public Node idealize () {
     static Node phiCon(Node op, boolean rotate) {
         Node lhs = op.in(1);
         Node rhs = op.in(2);
-        if( rhs._type== TypeInteger.TOP ) return null;
+        if( rhs._type==TypeInteger.TOP ) return null;
         // LHS is either a Phi of constants, or another op with Phi of constants
         PhiNode lphi = pcon(lhs,op);
         if( rotate && lphi==null && lhs.nIns() > 2 ) {
@@ -174,8 +153,8 @@ static boolean spine_cmp( Node hi, Node lo, Node dep ) {
         if( lo._type.isConstant() ) return false;
         if( hi._type.isConstant() ) return true ;
 
-        if( lo instanceof PhiNode lphi && lphi.region()._type== Type.XCONTROL ) return false;
-        if( hi instanceof PhiNode hphi && hphi.region()._type== Type.XCONTROL ) return false;
+        if( lo instanceof PhiNode lphi && lphi.region()._type==Type.XCONTROL ) return false;
+        if( hi instanceof PhiNode hphi && hphi.region()._type==Type.XCONTROL ) return false;
 
         if( lo instanceof PhiNode && lo.allCons(dep) ) return false;
         if( hi instanceof PhiNode && hi.allCons(dep) ) return true ;
@@ -189,11 +168,4 @@ static boolean spine_cmp( Node hi, Node lo, Node dep ) {
 
     @Override Node copy(Node lhs, Node rhs) { return new AddNode(lhs,rhs); }
     @Override Node copyF() { return new AddFNode(null,null); }
-// FIXME Dibyendu
-//    @Override public Parser.ParseException err() {
-//        if( in(1)._type.isHigh() || in(2)._type.isHigh() ) return null;
-//        if( !(in(1)._type instanceof SONTypeInteger) ) return Parser.error("Cannot '"+label()+"' " + in(1)._type,null);
-//        if( !(in(2)._type instanceof SONTypeInteger) ) return Parser.error("Cannot '"+label()+"' " + in(2)._type,null);
-//        return null;
-//    }
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/AndNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/AndNode.java
index fb35217..9308283 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/AndNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/AndNode.java
@@ -3,28 +3,18 @@
 import com.compilerprogramming.ezlang.compiler.sontypes.Type;
 import com.compilerprogramming.ezlang.compiler.sontypes.TypeInteger;
 
-public class AndNode extends LogicalNode {
+public class AndNode extends ArithNode {
     public AndNode(Node lhs, Node rhs) { super(lhs, rhs); }
 
     @Override public String label() { return "And"; }
     @Override public String op() { return "&"; }
-
     @Override public String glabel() { return "&"; }
 
-    @Override
-    public Type compute() {
-        Type t1 = in(1)._type, t2 = in(2)._type;
-        if( t1.isHigh() || t2.isHigh() )
-            return TypeInteger.TOP;
-        if( t1 instanceof TypeInteger i0 &&
-            t2 instanceof TypeInteger i1 ) {
-            if( i0.isConstant() && i1.isConstant() )
-                return TypeInteger.constant(i0.value()&i1.value());
-            // Sharpen allowed bits if either value is narrowed
-            long mask = i0.mask() & i1.mask();
-            return mask < 0 ? TypeInteger.BOT : TypeInteger.make(0,mask);
-        }
-        return TypeInteger.BOT;
+    @Override long doOp( long x, long y ) { return x & y; }
+    @Override TypeInteger doOp(TypeInteger x, TypeInteger y) {
+        // Sharpen allowed bits if either value is narrowed
+        long mask = x.mask() & y.mask();
+        return mask < 0 ? TypeInteger.BOT : TypeInteger.make(0,mask);
     }
 
     @Override
@@ -50,9 +40,10 @@ public Node idealize() {
         // Do we have ((x & (phi cons)) & (phi cons)) ?
         // Push constant up through the phi: x & (phi con0&con0 con1&con1...)
         Node phicon = AddNode.phiCon(this,true);
-        if( phicon!=null ) return phicon;
+        if( phicon!=null )
+            return phicon;
 
-        return null;
+        return super.idealize();
     }
     @Override Node copy(Node lhs, Node rhs) { return new AndNode(lhs,rhs); }
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ArithNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ArithNode.java
new file mode 100644
index 0000000..8cc3854
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ArithNode.java
@@ -0,0 +1,98 @@
+package com.compilerprogramming.ezlang.compiler.nodes;
+
+import com.compilerprogramming.ezlang.compiler.Compiler;
+import com.compilerprogramming.ezlang.compiler.sontypes.Type;
+import com.compilerprogramming.ezlang.compiler.sontypes.TypeInteger;
+import com.compilerprogramming.ezlang.exceptions.CompilerException;
+
+import java.util.BitSet;
+
+public abstract class ArithNode extends Node {
+    // Source location for late reported errors
+
+    public ArithNode(Node lhs, Node rhs) { super(lhs, rhs); }
+    @Override public String glabel() { return op(); }
+    abstract String op();
+
+    @Override
+    public StringBuilder _print1(StringBuilder sb, BitSet visited) {
+        in(1)._print0(sb.append("("), visited);
+        in(2)._print0(sb.append(op()), visited);
+        return sb.append(")");
+    }
+
+    abstract long doOp(long x, long y);
+    abstract TypeInteger doOp(TypeInteger x, TypeInteger y);
+
+    // Generic airthmetic op math: high returns high; low returns low; 2
+    // constants fold; only 2 non-constants call specialized math.
+    @Override
+    public final TypeInteger compute() {
+        Type t1 = in(1)._type, t2 = in(2)._type;
+        if( t1.isHigh() || t2.isHigh() )
+            return TypeInteger.TOP;
+        if( t1 instanceof TypeInteger x &&
+            t2 instanceof TypeInteger y )
+            return x.isConstant() && y.isConstant()
+                ? con(x,y)
+                : doOp(x,y);
+        return TypeInteger.BOT;
+    }
+
+    private TypeInteger con( Type t1, Type t2 ) {
+        return TypeInteger.constant( doOp( ((TypeInteger)t1).value(), ((TypeInteger)t2).value()) );
+    }
+
+    // Check for "op(Phi(con,x),Phi(con,y))" and push-up through the Phi.
+    // Note that this is the exact reverse of Phi pulling a common op down
+    // to reduce total op-count.  We don't get in an endless push-up
+    // push-down peephole cycle because the constants all fold first.
+    // Returns "Phi(op(con,con),op(x,y))".
+
+    // This check is slightly different from the
+
+    // Expected to be called after other ideal checks are done.
+    @Override public Node idealize() {
+        if( in(1) instanceof PhiNode lhs &&
+            in(2) instanceof PhiNode rhs &&
+            lhs.nIns() >= 2 && !lhs.inProgress() &&
+            lhs.region()==rhs.region() ) {
+            // Profit check: only 1 instance of `this` will remain, all the
+            // others will fold to constants.
+            int cnt=0;
+            for( int i=1; i<lhs.nIns(); i++ )
+                if( isCon(lhs,rhs,i) )
+                    cnt++;
+            if( lhs.nIns()-1 - cnt <= 1 ) {
+                // Profit!  Push up-phi and fold
+                Node[] ns = new Node[lhs.nIns()];
+                ns[0] = lhs.in(0);
+                for( int i=1; i<lhs.nIns(); i++ ) {
+                    ns[i] = isCon(lhs,rhs,i)
+                        ? new ConstantNode(con(lhs.in(i)._type, rhs.in(i)._type)).peephole()
+                        : copy(lhs.in(i), rhs.in(i)).peephole();
+                }
+                String label = lhs._label==rhs._label ? lhs._label : lhs._label + rhs._label;
+                return new PhiNode(label,lhs._declaredType,ns).peephole();
+            }
+        }
+        return null;
+    }
+
+    static boolean isCon( PhiNode lhs, PhiNode rhs, int i ) {
+        return lhs.in(i)._type.isConstant() && rhs.in(i)._type.isConstant();
+    }
+
+
+    static boolean overflow( long x, long y ) {
+        if( (x ^    y ) < 0 ) return false; // unequal signs, never overflow
+        return (x ^ (x + y)) < 0; // sum has unequal signs, so overflow
+    }
+
+    @Override public CompilerException err() {
+        if( in(1)._type.isHigh() || in(2)._type.isHigh() ) return null;
+        if( !(in(1)._type instanceof TypeInteger) ) return Compiler.error("Cannot '"+op()+"' " + in(1)._type);
+        if( !(in(2)._type instanceof TypeInteger) ) return Compiler.error("Cannot '"+op()+"' " + in(2)._type);
+        return null;
+    }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/BoolNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/BoolNode.java
index 3248345..e73adc9 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/BoolNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/BoolNode.java
@@ -116,6 +116,7 @@ TypeInteger doOp(TypeInteger i1, TypeInteger i2) {
         Node copy(Node lhs, Node rhs) { return new LT(lhs,rhs); }
         Node copyF() { return new LTF(null,null); }
     }
+
     public static class LE extends BoolNode {
         public LE(Node lhs, Node rhs) { super(lhs,rhs); }
         public String op() { return "<="; }
@@ -129,9 +130,24 @@ TypeInteger doOp(TypeInteger i1, TypeInteger i2) {
         Node copyF() { return new LEF(null,null); }
     }
 
-    public static class EQF extends EQ { public EQF(Node lhs, Node rhs) { super(lhs,rhs); } boolean doOp(double lhs, double rhs) { return lhs == rhs; } public boolean isFloat() { return true; } }
-    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 boolean isFloat() { return true; } }
-    public static class LEF extends LE { public LEF(Node lhs, Node rhs) { super(lhs,rhs); } boolean doOp(double lhs, double rhs) { return lhs <= rhs; } public boolean isFloat() { return true; } }
+    public static class EQF extends EQ {
+        public EQF(Node lhs, Node rhs) { super(lhs,rhs); }
+        @Override boolean doOp(double lhs, double rhs) { return lhs == rhs; }
+        @Override public boolean isFloat() { return true; }
+        @Override Node copy(Node lhs, Node rhs) { return new EQF(lhs,rhs); }
+    }
+    public static class LTF extends LT {
+        public LTF(Node lhs, Node rhs) { super(lhs,rhs); }
+        @Override boolean doOp(double lhs, double rhs) { return lhs <  rhs; }
+        @Override public boolean isFloat() { return true; }
+        @Override Node copy(Node lhs, Node rhs) { return new LTF(lhs,rhs); }
+    }
+    public static class LEF extends LE {
+        public LEF(Node lhs, Node rhs) { super(lhs,rhs); }
+        @Override boolean doOp(double lhs, double rhs) { return lhs <= rhs; }
+        @Override public boolean isFloat() { return true; }
+        @Override Node copy(Node lhs, Node rhs) { return new LEF(lhs,rhs); }
+    }
 
     // Unsigned less that, for range checks.  Not directly user writable.
     public static class ULT extends BoolNode {
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CFGNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CFGNode.java
index afd1622..23b4c82 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CFGNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CFGNode.java
@@ -122,7 +122,19 @@ int depth() {
     // Tag all CFG Nodes with their containing LoopNode; LoopNodes themselves
     // also refer to *their* containing LoopNode, as well as have their depth.
     // Start is a LoopNode which contains all at depth 1.
-    public void buildLoopTree(StopNode stop) {
+    public void buildLoopTree(StartNode start, StopNode stop) {
+        // Unlink all linked calls.  This can remove RPC constants which
+        // shuffled the StartNode outputs so requires a while loop.
+        boolean done=false;
+        while(!done) {
+            done = true;
+            for( Node use : start._outputs )
+                if( use instanceof FunNode fun )
+                    for( Node c : fun._inputs )
+                        if( c instanceof CallNode call )
+                            { call.unlink_all(); done=false; }
+        }
+
         _ltree = stop._ltree = Compiler.XCTRL._ltree = new LoopTree((StartNode)this);
         _bltWalk(2,null,stop, new BitSet());
     }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CallEndMach.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CallEndMach.java
new file mode 100644
index 0000000..9474a43
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CallEndMach.java
@@ -0,0 +1,36 @@
+package com.compilerprogramming.ezlang.compiler.nodes;
+
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+
+public class CallEndMach extends CallEndNode implements MachNode {
+    public CallEndMach( CallEndNode n ) { super(n); }
+    // MachNode specifics, shared across all CPUs
+    public int _xslot;
+    private RegMask _retMask;
+    private RegMask _kills;
+    public void cacheRegs(CodeGen code) {
+        // Return mask depends on TFP (either GPR or FPR)
+        _retMask = code._mach.retMask(call().tfp());
+        // Kill mask is all caller-saves, and any mirror stack slots for args
+        // in registers.
+        RegMaskRW kills = code._callerSave.copy();
+        // Start of stack slots
+        int maxReg = code._mach.regs().length;
+        // Incoming function arg slots, all low numbered in the RA
+        int fslot = fun()._maxArgSlot;
+        // Killed slots for this calls outgoing args
+        int xslot = code._mach.maxArgSlot(call().tfp());
+        _xslot = (maxReg+fslot)+xslot;
+        for( int i=0; i<xslot; i++ )
+            kills.set((maxReg+fslot)+i);
+        _kills = kills;
+    }
+    public String op() { return "cend"; }
+    public RegMask regmap(int i) { return null; }
+    public RegMask outregmap() { return null; }
+    public RegMask outregmap(int idx) { return idx==2  ? _retMask : null; }
+    public RegMask killmap() { return _kills; }
+    public void encoding( Encoding enc ) { }
+    public void asm(CodeGen code, SB sb) {  }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CallEndNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CallEndNode.java
index 79a1c03..947d3e9 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CallEndNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CallEndNode.java
@@ -26,6 +26,11 @@ public class CallEndNode extends CFGNode implements MultiNode {
 
     public CallNode call() { return (CallNode)in(0); }
 
+    @Override public CFGNode idom(Node dep) {
+        // Folding the idom is the one inlining Return
+        return _folding ? cfg(1) : super.idom(dep);
+    }
+
     @Override
     public StringBuilder _print1(StringBuilder sb, BitSet visited) {
         sb.append("cend( ");
@@ -41,7 +46,6 @@ public Type compute() {
         if( !(in(0) instanceof CallNode call) )
             return TypeTuple.RET.dual();
         Type ret = Type.BOTTOM;
-        TypeMem mem = TypeMem.BOT;
         if( addDep(call.fptr())._type instanceof TypeFunPtr tfp ) {
             ret = tfp.ret();
             // Here, if I can figure out I've found *all* callers, then I can meet
@@ -51,7 +55,7 @@ public Type compute() {
                 ret = ((TypeTuple)in(1)._type).ret(); // Return type
             }
         }
-        return TypeTuple.make(call._type, TypeMem.BOT,ret);
+        return TypeTuple.make(call._type,TypeMem.BOT,ret);
     }
 
     @Override
@@ -59,7 +63,7 @@ public Node idealize() {
 
         // Trivial inlining: call site calls a single function; single function
         // is only called by this call site.
-        if( false && !_folding && nIns()==2 && in(0) instanceof CallNode 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
@@ -106,33 +110,4 @@ public Node idealize() {
         return _folding ? in(1).in(idx) : null;
     }
 
-    // ------------
-    // MachNode specifics, shared across all CPUs
-    public int _xslot;
-    private RegMask _retMask;
-    private RegMask _kills;
-    public void cacheRegs(CodeGen code) {
-        // Return mask depends on TFP (either GPR or FPR)
-        _retMask = code._mach.retMask(call().tfp());
-        // Kill mask is all caller-saves, and any mirror stack slots for args
-        // in registers.
-        RegMaskRW kills = code._callerSave.copy();
-        // Start of stack slots
-        int maxReg = code._mach.regs().length;
-        // Incoming function arg slots, all low numbered in the RA
-        int fslot = fun()._maxArgSlot;
-        // Killed slots for this calls outgoing args
-        int xslot = code._mach.maxArgSlot(call().tfp());
-        _xslot = (maxReg+fslot)+xslot;
-        for( int i=0; i<xslot; i++ )
-            kills.set((maxReg+fslot)+i);
-        _kills = kills;
-    }
-    public String op() { return "cend"; }
-    public RegMask regmap(int i) { return null; }
-    public RegMask outregmap() { return null; }
-    public RegMask outregmap(int idx) { return idx==2  ? _retMask : null; }
-    public RegMask killmap() { return _kills; }
-    public void encoding( Encoding enc ) { }
-    public void asm(CodeGen code, SB sb) {  }
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CallNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CallNode.java
index 78c0632..c18ebf0 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CallNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CallNode.java
@@ -30,8 +30,11 @@ public class CallNode extends CFGNode {
         return sb.append(")");
     }
     public String name() {
-        if( fptr()._type instanceof TypeFunPtr tfp && tfp.isConstant() )
-            return CodeGen.CODE.link(tfp)._name;
+        if( fptr()._type instanceof TypeFunPtr tfp && tfp.isConstant() ) {
+            FunNode fun = CodeGen.CODE.link(tfp);
+            if( fun !=null ) return fun._name;
+            if( fptr() instanceof ExternNode ex )  return ex._extern;
+        }
         return null;
     }
 
@@ -160,6 +163,9 @@ public CompilerException err() {
             if( !arg(i+2)._type.isa(tfp.arg(i)) )
                 return Compiler.error( "Argument #"+i+" isa "+arg(i+2)._type+", but must be a "+tfp.arg(i));
 
+        if( tfp.fidxs() < 0 )
+            throw Utils.TODO(); // Infinite unknown TFPs?  Should be fairly precise CG
+
         return null;
     }
 
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CastARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CastMach.java
similarity index 58%
rename from seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CastARM.java
rename to seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CastMach.java
index 432a5b8..3fcf37b 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CastARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CastMach.java
@@ -1,18 +1,19 @@
-package com.compilerprogramming.ezlang.compiler.nodes.cpus.arm;
+package com.compilerprogramming.ezlang.compiler.nodes;
 
 import com.compilerprogramming.ezlang.compiler.SB;
 import com.compilerprogramming.ezlang.compiler.codegen.CodeGen;
 import com.compilerprogramming.ezlang.compiler.codegen.Encoding;
 import com.compilerprogramming.ezlang.compiler.codegen.RegMask;
-import com.compilerprogramming.ezlang.compiler.nodes.CastNode;
-import com.compilerprogramming.ezlang.compiler.nodes.MachNode;
 
-public class CastARM extends CastNode implements MachNode {
-    public CastARM( CastNode cast ) { super(cast); }
+/**
+ * Cast a pointer to read-only
+ */
+public class CastMach extends CastNode implements MachNode {
+    public CastMach( CastNode n ) { super(n); }
     @Override public String op() { return label(); }
     @Override public RegMask regmap(int i) { assert i==1; return RegMask.FULL; }
     @Override public RegMask outregmap() { return RegMask.FULL; }
     @Override public int twoAddress( ) { return 1; }
     @Override public void encoding( Encoding enc ) { }
-    @Override public void asm(CodeGen code, SB sb) { _t.print(sb.p(code.reg(in(1))).p(" isa ")); }
+    @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(in(1))); }
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CastNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CastNode.java
index 7645f39..c37e22c 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CastNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CastNode.java
@@ -2,6 +2,8 @@
 
 import com.compilerprogramming.ezlang.compiler.Utils;
 import com.compilerprogramming.ezlang.compiler.sontypes.Type;
+import com.compilerprogramming.ezlang.compiler.sontypes.TypeInteger;
+import com.compilerprogramming.ezlang.compiler.sontypes.TypeMemPtr;
 import com.compilerprogramming.ezlang.exceptions.CompilerException;
 
 import java.util.BitSet;
@@ -37,6 +39,9 @@ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
 
     @Override
     public Type compute() {
+        // Cast array to int
+        if( _t == TypeInteger.BOT && in(1)._type instanceof TypeMemPtr tmp && tmp._obj.isAry() )
+            return _t;
         return in(1)._type.join(_t);
     }
 
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ConstantNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ConstantNode.java
index 407ab01..a689a00 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ConstantNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ConstantNode.java
@@ -29,8 +29,8 @@ public ConstantNode( Type type ) {
     public ConstantNode( ConstantNode con ) { this(con,con._type);  }
 
     public static Node make( Type type ) {
-        if( type== Type. CONTROL ) return new CtrlNode();
-        if( type== Type.XCONTROL ) return new XCtrlNode();
+        if( type==Type. CONTROL ) return new CtrlNode();
+        if( type==Type.XCONTROL ) return new XCtrlNode();
         return new ConstantNode(type);
     }
 
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/DivNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/DivNode.java
index 34d6eb2..82d77df 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/DivNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/DivNode.java
@@ -4,32 +4,14 @@
 import com.compilerprogramming.ezlang.compiler.sontypes.TypeInteger;
 import java.util.BitSet;
 
-public class DivNode extends Node {
-    public DivNode(Node lhs, Node rhs) { super(null, lhs, rhs); }
+public class DivNode extends ArithNode {
+    public DivNode(Node lhs, Node rhs) { super(lhs, rhs); }
 
     @Override public String label() { return "Div"; }
+    @Override public String op() { return "//"; }
 
-    @Override public String glabel() { return "//"; }
-
-    @Override
-    public StringBuilder _print1(StringBuilder sb, BitSet visited) {
-        in(1)._print0(sb.append("("), visited);
-        in(2)._print0(sb.append("/"), visited);
-        return sb.append(")");
-    }
-
-    @Override
-    public Type compute() {
-        Type t1 = in(1)._type, t2 = in(2)._type;
-        if( t1.isHigh() || t2.isHigh() )
-            return TypeInteger.TOP;
-        if( t1 instanceof TypeInteger i1 &&
-            t2 instanceof TypeInteger i2 ) {
-            if (i1.isConstant() && i2.isConstant())
-                return i2.value() == 0
-                    ? TypeInteger.ZERO
-                    : TypeInteger.constant(i1.value()/i2.value());
-        }
+    @Override long doOp( long x, long y ) { return y==0 ? 0 : x / y; }
+    @Override TypeInteger doOp(TypeInteger x, TypeInteger y) {
         return TypeInteger.BOT;
     }
 
@@ -38,7 +20,7 @@ public Node idealize() {
         // Div of 1.
         if( in(2)._type == TypeInteger.TRUE )
             return in(1);
-        return null;
+        return super.idealize();
     }
 
     @Override Node copy(Node lhs, Node rhs) { return new DivNode(lhs,rhs); }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ExternNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ExternNode.java
new file mode 100644
index 0000000..8f0b36d
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ExternNode.java
@@ -0,0 +1,19 @@
+package com.compilerprogramming.ezlang.compiler.nodes;
+
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.sontypes.Type;
+
+/**
+   A constant with external linkage.
+ */
+
+public class ExternNode extends ConstantNode {
+    public final String _extern;
+    public ExternNode(Type t, String ex) { super(t); _extern = ex; }
+
+    @Override public String  label() { return "#"+_con+":"+_extern; }
+    @Override public String glabel() { return _con.gprint(new SB().p("#")).p(":").p(_extern).toString(); }
+    @Override public String uniqueName() { return "Extern_" + _nid; }
+
+    @Override public boolean eq(Node n) { return this==n; }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/FunNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/FunNode.java
index f7de008..28cc554 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/FunNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/FunNode.java
@@ -69,10 +69,17 @@ public void setSig( TypeFunPtr sig ) {
         }
     }
 
+    public void setName( String name ) {
+        if( _name==null ) _name=name;
+        else _name += "."+name;
+    }
+
     @Override
     public Type compute() {
         // Only dead if no callers after SCCP
-        return Type.CONTROL;
+        if( unknownCallers() )
+            return Type.CONTROL;
+        return super.compute();
     }
 
     @Override
@@ -120,10 +127,10 @@ public Node idealize() {
         return _folding ? super.idepth() : CodeGen.CODE.iDepthAt(1);
     }
     // Bypass Region idom, always assume idom is Start
-    @Override public CFGNode idom(Node dep) { return cfg(1); }
+    @Override public CFGNode idom(Node dep) { return _folding && nIns()==3 ? cfg(2) : cfg(1); }
 
     // Always in-progress until we run out of unknown callers
-    public boolean unknownCallers() { return in(1) instanceof StartNode; }
+    public boolean unknownCallers() { return nIns()<2 || in(1) instanceof StartNode; }
 
     @Override public boolean inProgress() { return unknownCallers(); }
 
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/IfNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/IfNode.java
index 5cc6a2e..56ed032 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/IfNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/IfNode.java
@@ -44,7 +44,7 @@ public Type compute() {
         // If constant is 0 then false branch is reachable
         // Else true branch is reachable
         if( t.isConstant() )
-            return (t== Type.NIL || t== TypeInteger.ZERO || (t instanceof TypeFunPtr tfp && tfp._fidxs==0) ) ? TypeTuple.IF_FALSE : TypeTuple.IF_TRUE;
+            return (t==Type.NIL || t==TypeInteger.ZERO || (t instanceof TypeFunPtr tfp && tfp._fidxs==0) ) ? TypeTuple.IF_FALSE : TypeTuple.IF_TRUE;
         // If adding a zero makes a difference, the predicate must not have a zero/null
         if( !t.makeZero().isa(t) )
             return TypeTuple.IF_TRUE;
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/LoadNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/LoadNode.java
index 28842c2..ff9c9b7 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/LoadNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/LoadNode.java
@@ -55,6 +55,12 @@ public Node idealize() {
             return extend(st.val());
         }
 
+        // Simple load-after-MemMerge to a known alias can bypass.  Happens when inlining.
+        if( mem instanceof MemMergeNode mem2 ) {
+            setDef(1,mem2.alias(_alias));
+            return this;
+        }
+
         // Simple Load-after-New on same address.
         if( mem instanceof ProjNode p && p.in(0) instanceof NewNode nnn &&
             ptr == nnn.proj(1) ) // Must check same object
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/LogicalNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/LogicalNode.java
deleted file mode 100644
index e7c3450..0000000
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/LogicalNode.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package com.compilerprogramming.ezlang.compiler.nodes;
-
-import com.compilerprogramming.ezlang.compiler.Compiler;
-import com.compilerprogramming.ezlang.compiler.sontypes.TypeInteger;
-import com.compilerprogramming.ezlang.exceptions.CompilerException;
-
-import java.util.BitSet;
-
-public abstract class LogicalNode extends Node {
-
-    public LogicalNode(Node lhs, Node rhs) { super(null, lhs, rhs); }
-    abstract String op();
-
-    @Override
-    public StringBuilder _print1(StringBuilder sb, BitSet visited) {
-        in(1)._print0(sb.append("("), visited);
-        in(2)._print0(sb.append(op()), visited);
-        return sb.append(")");
-    }
-
-
-    @Override public CompilerException err() {
-        if( in(1)._type.isHigh() || in(2)._type.isHigh() ) return null;
-        if( !(in(1)._type instanceof TypeInteger) ) return Compiler.error("Cannot '"+op()+"' " + in(1)._type);
-        if( !(in(2)._type instanceof TypeInteger) ) return Compiler.error("Cannot '"+op()+"' " + in(2)._type);
-        return null;
-    }
-}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/LoopNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/LoopNode.java
index 87c1a03..088865f 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/LoopNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/LoopNode.java
@@ -21,7 +21,7 @@ public Type compute() {
     }
 
     // Bypass Region idom, same as the default idom() using use in(1) instead of in(0)
-    public int idepth() {
+    @Override public int idepth() {
         return CodeGen.CODE.validIDepth(_idepth) ? _idepth : (_idepth=CodeGen.CODE.iDepthFrom(idom().idepth()));
     }
     // Bypass Region idom, same as the default idom() using use in(1) instead of in(0)
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MemMergeNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MemMergeNode.java
index 8de4ab8..ee69554 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MemMergeNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MemMergeNode.java
@@ -54,7 +54,8 @@ public Node merge() {
         MemMergeNode merge = new MemMergeNode(false);
         for( Node n : _inputs )
             merge.addDef(n);
-        merge._mem(1,null);
+        for( int i=1; i<nIns(); i++ )
+            merge._mem(i,null);
         return merge.peephole();
     }
 
@@ -64,24 +65,38 @@ public Node merge() {
     @Override public Node idealize() {
         if( inProgress() ) return null;
 
+        // Fold defaults into the default
+        boolean progress=false, allDefault=true;
+        for( int i=2; i<nIns(); i++ )
+            if( in(1) == in(i) ) { setDef(i,null); progress=true; }
+            else                 { allDefault=false; }
+
         // If not merging any memory (all memory is just the default)
-        if( allDefault() )
+        if( allDefault )
             return in(1);       // Become default memory
 
-        return null;
-    }
-    private boolean allDefault() {
-        for( int i=2; i<nIns(); i++ )
-            if( in(1) != in(i) )
-                return false;
-        return true;
+        // Collapse stacked all-mem
+        if( in(1) instanceof MemMergeNode mem ) {
+            // Goal is to swap my default mem with mem's default mem
+            for( int i=2; i<mem.nIns(); i++ ) {
+                if( mem.in(i) != null ) {
+                    // deeper default mem has a non-default
+                    if( i>=nIns() || in(i)==null )
+                        setDefX(i,mem.in(i));
+                }
+            }
+            setDef(1,mem.in(1));
+            return this;
+        }
+
+        return progress ? this : null;
     }
 
 
     public Node in( Var v ) { return in(v._idx); }
 
     public Node alias( int alias ) {
-        return in(alias<nIns() && in(alias)!=null ? alias : 1);
+        return alias < nIns() && in(alias)!=null ? in(alias) : in(1);
     }
 
     Node alias( int alias, Node st ) {
@@ -122,7 +137,7 @@ void _merge( MemMergeNode that, RegionNode r) {
                 // by alias as it will trigger a phi creation
                 Node lhs = this._mem(i,null);
                 Node rhs = that._mem(i,null);
-                alias(i, new PhiNode(Compiler.memName(i), lhs._type.glb().meet(rhs._type.glb()), r, lhs, rhs).peephole());
+                alias(i, new PhiNode(Compiler.memName(i), lhs._type.glb(true).meet(rhs._type.glb(true)), r, lhs, rhs).peephole());
             }
     }
 
@@ -132,7 +147,7 @@ void _endLoopMem( ScopeNode scope, MemMergeNode back, MemMergeNode exit ) {
         for( int i=1; i<nIns(); i++ ) {
             if( in(i) instanceof PhiNode phi && phi.region()==scope.ctrl() ) {
                 assert phi.in(2)==null;
-                phi.setDef(2,back.in(i)==scope ? phi : back.in(i)); // Fill backedge
+                phi.setDef(2,back.alias(i)==scope ? phi : back.alias(i)); // Fill backedge
             }
             if( exit_def == scope ) // Replace a lazy-phi on the exit path also
                 exit.alias(i,in(i));
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MemOpNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MemOpNode.java
index 87c1c2e..d87f6ed 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MemOpNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MemOpNode.java
@@ -72,7 +72,6 @@ public MemOpNode( boolean isLoad ) {
         _declaredType = Type.BOTTOM;
     }
 
-    //
     static String mlabel(String name) { return "[]".equals(name) ? "ary" : ("#".equals(name) ? "len" : name); }
     String mlabel() { return mlabel(_name); }
 
@@ -82,7 +81,7 @@ public MemOpNode( boolean isLoad ) {
 
     @Override public StringBuilder _print1( StringBuilder sb, BitSet visited ) { return _printMach(sb,visited);  }
     public StringBuilder _printMach( StringBuilder sb, BitSet visited ) { throw Utils.TODO(); }
-
+    public int log_size() { return _declaredType.log_size();  }
 
     @Override
     public boolean eq(Node n) {
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MinusNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MinusNode.java
index 8b72814..2c7debd 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MinusNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MinusNode.java
@@ -22,7 +22,7 @@ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
     public Type compute() {
         if( in(1)._type.isHigh() ) return TypeInteger.TOP;
         if( in(1)._type instanceof TypeInteger i0 &&
-            !(i0== TypeInteger.BOT || i0._min == Long.MIN_VALUE || i0._max == Long.MIN_VALUE) )
+            i0._min != Long.MIN_VALUE && i0._max != Long.MIN_VALUE )
             return TypeInteger.make(-i0._max,-i0._min);
         return TypeInteger.BOT;
     }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MulNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MulNode.java
index 81f378c..a56192d 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MulNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MulNode.java
@@ -7,32 +7,14 @@
 
 import java.util.BitSet;
 
-public class MulNode extends Node {
-    public MulNode(Node lhs, Node rhs) { super(null, lhs, rhs); }
+public class MulNode extends ArithNode {
+    public MulNode(Node lhs, Node rhs) { super(lhs, rhs); }
 
     @Override public String label() { return "Mul"; }
+    @Override public String op() { return "*"; }
 
-    @Override public String glabel() { return "*"; }
-
-    @Override
-    public StringBuilder _print1(StringBuilder sb, BitSet visited) {
-        in(1)._print0(sb.append("("), visited);
-        in(2)._print0(sb.append("*"), visited);
-        return sb.append(")");
-    }
-
-    @Override
-    public Type compute() {
-        Type t1 = in(1)._type, t2 = in(2)._type;
-        if( t1.isHigh() || t2.isHigh() )
-            return TypeInteger.TOP;
-        if( t1 instanceof TypeInteger i1 &&
-            t2 instanceof TypeInteger i2 ) {
-            if( i1== TypeInteger.ZERO || i2== TypeInteger.ZERO)
-                return TypeInteger.ZERO;
-            if (i1.isConstant() && i2.isConstant())
-                return TypeInteger.constant(i1.value()*i2.value());
-        }
+    @Override long doOp( long x, long y ) { return x * y; }
+    @Override TypeInteger doOp(TypeInteger x, TypeInteger y) {
         return TypeInteger.BOT;
     }
 
@@ -76,14 +58,8 @@ public Node idealize() {
         Node phicon = AddNode.phiCon(this,true);
         if( phicon!=null ) return phicon;
 
-        return null;
+        return super.idealize();
     }
     @Override Node copy(Node lhs, Node rhs) { return new MulNode(lhs,rhs); }
     @Override Node copyF() { return new MulFNode(null,null); }
-    @Override public CompilerException err() {
-        if( in(1)._type.isHigh() || in(2)._type.isHigh() ) return null;
-        if( !(in(1)._type instanceof TypeInteger) ) return Compiler.error("Cannot '"+label()+"' " + in(1)._type.glb());
-        if( !(in(2)._type instanceof TypeInteger) ) return Compiler.error("Cannot '"+label()+"' " + in(2)._type.glb());
-        return null;
-    }
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/NewNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/NewNode.java
index daac5e8..d8265f1 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/NewNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/NewNode.java
@@ -25,9 +25,9 @@ public NewNode(TypeMemPtr ptr, Node... nodes) {
         _ptr = ptr;
         _len = ptr._obj._fields.length;
         // Control in slot 0
-        assert nodes[0]._type== Type.CONTROL || nodes[0]._type == Type.XCONTROL;
+        assert nodes[0]._type==Type.CONTROL || nodes[0]._type == Type.XCONTROL;
         // Malloc-length in slot 1
-        assert nodes[1]._type instanceof TypeInteger || nodes[1]._type== Type.NIL;
+        assert nodes[1]._type instanceof TypeInteger || nodes[1]._type==Type.NIL;
         for( int i=0; i<_len; i++ )
             // Memory slices for all fields.
             assert nodes[2+i]._type.isa(TypeMem.BOT);
@@ -68,7 +68,7 @@ public TypeTuple compute() {
         ts[1] = _ptr;
         for( int i=0; i<fs.length; i++ ) {
             Type mt = in(i+2)._type;
-            TypeMem mem = mt== Type.TOP ? TypeMem.TOP : (TypeMem)mt;
+            TypeMem mem = mt==Type.TOP ? TypeMem.TOP : (TypeMem)mt;
             Type tfld = mem._t.meet(mem._t.makeZero());
             Type tfld2 = tfld.join(fs[i]._type);
             ts[i+2] = TypeMem.make(fs[i]._alias,tfld2);
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/Node.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/Node.java
index 53fe5ca..d7d0d7e 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/Node.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/Node.java
@@ -44,7 +44,7 @@ public abstract class Node implements Cloneable {
      * walked in either direction.  These outputs are typically used for
      * efficient optimizations but otherwise have no semantics meaning.
      */
-    public Ary<Node> _outputs;
+     public Ary<Node> _outputs;
 
 
     /**
@@ -67,7 +67,7 @@ public abstract class Node implements Cloneable {
     // Make a Node using the existing arrays of nodes.
     // Used by any pass rewriting all Node classes but not the edges.
     Node( Node n ) {
-        assert CodeGen.CODE._phase.ordinal() >= CodeGen.Phase.InstSelect.ordinal();
+        assert CodeGen.CODE._phase.ordinal() >= CodeGen.Phase.Select.ordinal();
         _nid = CODE.getUID(); // allocate unique dense ID
         _inputs  = new Ary<>(n==null ? new Node[0] : n._inputs.asAry());
         _outputs = new Ary<>(Node.class);
@@ -240,7 +240,7 @@ protected boolean delUse( Node use ) {
 
     // Shortcut for "popping" until n nodes.  A "pop" is basically a
     // setDef(last,null) followed by lowering the nIns() count.
-    void popUntil(int n) {
+    public void popUntil(int n) {
         unlock();
         while( nIns() > n ) {
             Node old_def = _inputs.pop();
@@ -256,9 +256,9 @@ void popUntil(int n) {
      * code elimination.
      */
     public void kill( ) {
+        assert isUnused();      // Has no uses, so it is dead
         unlock();
         moveDepsToWorklist();
-        assert isUnused();      // Has no uses, so it is dead
         _type=null;             // Flag as dead
         while( nIns()>0 ) { // Set all inputs to null, recursively killing unused Nodes
             Node old_def = _inputs.removeLast();
@@ -296,6 +296,10 @@ public void unkill() {
         if( unkeep().isUnused() )
             kill();
     }
+    public void isKill() {
+        if( isUnused() )
+            kill();
+    }
 
 
     // Replace self with nnn in the graph, making 'this' go dead
@@ -640,7 +644,7 @@ public final Node widen() {
     }
     private boolean hasFloatInput() {
         for( int i=1; i<nIns(); i++ )
-            if( in(i)._type instanceof TypeFloat)
+            if( in(i)._type instanceof TypeFloat )
                 return true;
         return false;
     }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/OrNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/OrNode.java
index 444e2ca..211090f 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/OrNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/OrNode.java
@@ -3,24 +3,16 @@
 import com.compilerprogramming.ezlang.compiler.sontypes.Type;
 import com.compilerprogramming.ezlang.compiler.sontypes.TypeInteger;
 
-public class OrNode extends LogicalNode {
+public class OrNode extends ArithNode {
     public OrNode(Node lhs, Node rhs) { super(lhs, rhs); }
 
     @Override public String label() { return "Or"; }
     @Override public String op() { return "|"; }
-
     @Override public String glabel() { return "|"; }
 
-    @Override
-    public Type compute() {
-        Type t1 = in(1)._type, t2 = in(2)._type;
-        if( t1.isHigh() || t2.isHigh() )
-            return TypeInteger.TOP;
-        if( t1 instanceof TypeInteger i0 &&
-            t2 instanceof TypeInteger i1 ) {
-            if( i0.isConstant() && i1.isConstant() )
-                return TypeInteger.constant(i0.value()|i1.value());
-        }
+
+    @Override long doOp( long x, long y ) { return x | y; }
+    @Override TypeInteger doOp(TypeInteger x, TypeInteger y) {
         return TypeInteger.BOT;
     }
 
@@ -46,7 +38,7 @@ public Node idealize() {
         Node phicon = AddNode.phiCon(this,true);
         if( phicon!=null ) return phicon;
 
-        return null;
+        return super.idealize();
     }
     @Override Node copy(Node lhs, Node rhs) { return new OrNode(lhs,rhs); }
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ParmNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ParmNode.java
index 054d3a6..efc6903 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ParmNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ParmNode.java
@@ -44,4 +44,8 @@ public Node idealize() {
 
     // Always in-progress until we run out of unknown callers
     @Override public boolean inProgress() { return fun().inProgress(); }
+
+    @Override public boolean eq( Node n ) {
+        return ((ParmNode)n)._idx==_idx && super.eq(n);
+    }
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/PhiNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/PhiNode.java
index 86f7818..b48fa25 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/PhiNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/PhiNode.java
@@ -51,11 +51,11 @@ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
     @Override
     public Type compute() {
         if( !(region() instanceof RegionNode r) )
-            return region()._type== Type.XCONTROL ? (_type instanceof TypeMem ? TypeMem.TOP : Type.TOP) : _type;
+            return region()._type==Type.XCONTROL ? (_type instanceof TypeMem ? TypeMem.TOP : Type.TOP) : _type;
         // During parsing Phis have to be computed type pessimistically.
         if( r.inProgress() ) return _declaredType;
         // Set type to local top of the starting type
-        Type t = _declaredType.glb().dual();//Type.TOP;
+        Type t = _declaredType.glb(false).dual();//Type.TOP;
         for (int i = 1; i < nIns(); i++)
             // If the region's control input is live, add this as a dependency
             // to the control because we can be peeped should it become dead.
@@ -122,12 +122,17 @@ private boolean same_op() {
             Node op = in(i);
             if( in(1).getClass() != op.getClass() || op.in(0)!=null || in(1).nIns() != op.nIns() )
                 return false;      // Wrong class or CFG bound or mismatched inputs
+            if( in(1) instanceof MemOpNode mem && mem._alias != ((MemOpNode)op)._alias )
+                return false;
             if( op.nOuts() > 1 ) { // Too many users, but addDep in case lose users
                 for( Node out : op._outputs )
                     if( out!=null && out!=this )
                         addDep(out);
                 return false;
             }
+            for( int j=1; j<in(1).nIns(); j++ )
+                if( op.in(j) instanceof ScopeNode || (op.in(j)==null ^ in(1).in(j)==null) )
+                    return false; // Lazy Phi input
         }
         return true;
     }
@@ -146,7 +151,7 @@ private Node drop_same_op() {
                 if( in(i).in(j) != x )
                     { needsPhi=true; break; }
             if( needsPhi ) {
-                x = new PhiNode(_label,op.in(j)._type.glb());
+                x = new PhiNode(_label,op.in(j)._type.glb(false));
                 x.addDef(region());
                 for( int i=1; i<nIns(); i++ )
                     x.addDef(in(i).in(j));
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CastRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ReadOnlyMach.java
similarity index 57%
rename from seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CastRISC.java
rename to seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ReadOnlyMach.java
index e879d3e..b9342cc 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CastRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ReadOnlyMach.java
@@ -1,18 +1,19 @@
-package com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv;
+package com.compilerprogramming.ezlang.compiler.nodes;
 
 import com.compilerprogramming.ezlang.compiler.SB;
 import com.compilerprogramming.ezlang.compiler.codegen.CodeGen;
 import com.compilerprogramming.ezlang.compiler.codegen.Encoding;
 import com.compilerprogramming.ezlang.compiler.codegen.RegMask;
-import com.compilerprogramming.ezlang.compiler.nodes.CastNode;
-import com.compilerprogramming.ezlang.compiler.nodes.MachNode;
 
-public class CastRISC extends CastNode implements MachNode {
-    public CastRISC( CastNode cast ) { super(cast); }
+/**
+ * Cast a pointer to read-only
+ */
+public class ReadOnlyMach extends ReadOnlyNode implements MachNode {
+    public ReadOnlyMach( ReadOnlyNode n ) { super(n); }
     @Override public String op() { return label(); }
     @Override public RegMask regmap(int i) { assert i==1; return RegMask.FULL; }
     @Override public RegMask outregmap() { return RegMask.FULL; }
     @Override public int twoAddress( ) { return 1; }
     @Override public void encoding( Encoding enc ) { }
-    @Override public void asm(CodeGen code, SB sb) { _t.print(sb.p(code.reg(in(1))).p(" isa ")); }
+    @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(in(1))); }
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ReadOnlyNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ReadOnlyNode.java
index 6e63ca0..b3080bd 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ReadOnlyNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ReadOnlyNode.java
@@ -27,4 +27,5 @@ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
             return in(1);
         return null;
     }
+
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ReturnNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ReturnNode.java
index 056f5ce..e20501c 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ReturnNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ReturnNode.java
@@ -53,7 +53,6 @@ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
     @Override
     public Type compute() {
         if( inProgress () ) return TypeTuple.RET; // In progress
-        if( _fun.isDead() ) return TypeTuple.RET.dual(); // Dead another way
         return TypeTuple.make(ctrl()._type,mem()._type,expr()._type);
     }
 
@@ -98,9 +97,9 @@ void addReturn( Node ctrl, Node rmem, Node expr ) {
         Type t = expr._type;
         mt = mt.meet(t);
         ti |= t instanceof TypeInteger x;
-        tf |= t instanceof TypeFloat x;
-        tp |= t instanceof TypeMemPtr x;
-        tn |= t== Type.NIL;
+        tf |= t instanceof TypeFloat   x;
+        tp |= t instanceof TypeMemPtr  x;
+        tn |= t==Type.NIL;
 
         // Merge path into the One True Return
         RegionNode r = (RegionNode)ctrl();
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/SarNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/SarNode.java
index 7192cfe..776b18c 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/SarNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/SarNode.java
@@ -3,29 +3,18 @@
 import com.compilerprogramming.ezlang.compiler.sontypes.Type;
 import com.compilerprogramming.ezlang.compiler.sontypes.TypeInteger;
 
-public class SarNode extends LogicalNode {
+public class SarNode extends ArithNode {
     public SarNode(Node lhs, Node rhs) { super(lhs, rhs); }
 
     @Override public String label() { return "Sar"; }
     @Override public String op() { return ">>"; }
-
     @Override public String glabel() { return "&gt;&gt;"; }
 
-    @Override
-    public Type compute() {
-        Type t1 = in(1)._type, t2 = in(2)._type;
-        if( t1.isHigh() || t2.isHigh() )
-            return TypeInteger.TOP;
-        if (t1 instanceof TypeInteger i0 &&
-            t2 instanceof TypeInteger i1) {
-            if( i0 == TypeInteger.ZERO )
-                return TypeInteger.ZERO;
-            if( i0.isConstant() && i1.isConstant() )
-                return TypeInteger.constant(i0.value()>>i1.value());
-            if( i1.isConstant() ) {
-                int log = (int)i1.value();
-                return TypeInteger.make(-1L<<(63-log),(1L<<(63-log))-1);
-            }
+    @Override long doOp( long x, long y ) { return x >> y; }
+    @Override TypeInteger doOp( TypeInteger x, TypeInteger y ) {
+        if( y.isConstant() ) {
+            int log = (int)y.value();
+            return TypeInteger.make(-1L<<(63-log),(1L<<(63-log))-1);
         }
         return TypeInteger.BOT;
     }
@@ -42,7 +31,7 @@ public Node idealize() {
 
         // TODO: x >> 3 >> (y ? 1 : 2) ==> x >> (y ? 4 : 5)
 
-        return null;
+        return super.idealize();
     }
     @Override Node copy(Node lhs, Node rhs) { return new SarNode(lhs,rhs); }
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ScopeNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ScopeNode.java
index bf3b53e..525acd3 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ScopeNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ScopeNode.java
@@ -49,7 +49,7 @@ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
         int j=1;
         for( int i=0; i<nIns(); i++ ) {
             if( j < _lexSize._len && i == _lexSize.at(j) ) { sb.append("| "); j++; }
-            Var v = _vars.get(i);
+            Var v = _vars.at(i);
             sb.append(v.type().print(new SB()));
             sb.append(" ");
             if( v._final ) sb.append("!");
@@ -71,6 +71,7 @@ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
 
     public Node ctrl() { return in(0); }
     public MemMergeNode mem() { return (MemMergeNode)in(1); }
+    public Var var(int i) { return _vars.at(i); }
 
     /**
      * The ctrl of a ScopeNode is always bound to the currently active
@@ -85,7 +86,7 @@ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
     public <N extends Node> N ctrl(N n) { return setDef(0,n); }
     public Node mem(Node n) { return setDef(1,n); }
 
-    public void push(Kind kind) {
+    public void push( Kind kind ) {
         assert _lexSize._len==_kinds._len;
         _lexSize.push(_vars.size());
         _kinds  .push(kind);
@@ -94,11 +95,11 @@ public void push(Kind kind) {
     // Pop a lexical scope
     public void pop() {
         assert _lexSize._len==_kinds._len;
-        promote();
+        promote();              // Promote forward references to the next outer scope
         int n = _lexSize.pop();
         _kinds.pop();
-        popUntil(n);
-        _vars.setLen(n);
+        popUntil(n);            // Pop off inputs going out of scope
+        _vars.setLen(n);        // Pop off variables going out of scope
     }
 
 
@@ -107,7 +108,7 @@ public void pop() {
     public void promote() {
         int n = _lexSize.last();
         for( int i=n; i<nIns(); i++ ) {
-            Var v = _vars.at(i);
+            Var v = var(i);
             if( !v.isFRef() ) continue;
             if( _lexSize._len==1 )
                 throw Compiler.error("Undefined name '" + v._name + "'");
@@ -120,7 +121,7 @@ public void promote() {
     }
 
 
-    public boolean inCon() { return _kinds.last() == Kind.Constructor; }
+    public boolean inConstructor() { return _kinds.last() == Kind.Constructor; }
 
     // Is v outside any current function scope?
     public boolean outOfFunction( Var v ) {
@@ -134,9 +135,9 @@ public boolean outOfFunction( Var v ) {
     // Find name in reverse, return an index into _vars or -1.  Linear scan
     // instead of hashtable, but probably doesn't matter until the scan
     // typically hits many dozens of variables.
-    int find( String name ) {
+    public int find( String name ) {
         for( int i=_vars.size()-1; i>=0; i-- )
-            if( _vars.at(i)._name.equals(name) )
+            if( var(i)._name.equals(name) )
                 return i;
         return -1;
     }
@@ -148,7 +149,7 @@ public boolean define(String name, Type declaredType, boolean xfinal, Node init)
         assert _lexSize.isEmpty() || name.charAt(0)!='$' ; // Later scopes do not define memory
         if( _lexSize._len > 0 )
             for( int i=_vars.size()-1; i>=_lexSize.last(); i-- ) {
-                Var n = _vars.at(i);
+                Var n = var(i);
                 if( n._name.equals(name) ) {
                     if( !n.isFRef() ) return false;       // Double define
                     FRefNode fref = (FRefNode)in(n._idx); // Get forward ref
@@ -181,11 +182,11 @@ public boolean define(String name, Type declaredType, boolean xfinal, Node init)
     public Var lookup( String name ) {
         int idx = find(name);
         // -1 is missed in all scopes, not found
-        return idx == -1 ? null : update(_vars.at(idx),null);
+        return idx == -1 ? null : update(var(idx),null);
     }
 
     /**
-     * If the name is present in any scope, then redefine else null
+     * Redefine an existing name
      *
      * @param name Name being redefined
      * @param n    The node to bind to the name
@@ -193,7 +194,7 @@ public Var lookup( String name ) {
     public void update( String name, Node n ) {
         int idx = find(name);
         assert idx>=0;
-        update(_vars.at(idx),n);
+        update(var(idx),n);
     }
 
     public Var update( Var v, Node st ) {
@@ -207,7 +208,7 @@ public Var update( Var v, Node st ) {
                 // Set real Phi in the loop head
                 // The phi takes its one input (no backedge yet) from a recursive
                 // lookup, which might have insert a Phi in every loop nest.
-                : loop.setDef(v._idx,new PhiNode(v._name, v.lazyGLB(), loop.ctrl(), loop.in(loop.update(v,null)._idx),null).peephole());
+                : loop.setDef(v._idx,new PhiNode(v._name, v.type(), loop.ctrl(), loop.in(loop.update(v,null)._idx),null).peephole());
             setDef(v._idx,old);
         }
         assert !v._final || st==null;
@@ -260,7 +261,7 @@ public ScopeNode dup(boolean loop) {
         for( int i=2; i<nIns(); i++ )
             // For lazy phis on loops we use a sentinel
             // that will trigger phi creation on update
-            dup.addDef(loop && !_vars.at(i)._final ? this : in(i));
+            dup.addDef(loop && !var(i)._final ? this : in(i));
         return dup;
     }
 
@@ -286,7 +287,7 @@ private void _merge(ScopeNode that, RegionNode r) {
             if( in(i) != that.in(i) ) { // No need for redundant Phis
                 // If we are in lazy phi mode we need to a lookup
                 // by name as it will trigger a phi creation
-                Var v = _vars.at(i);
+                Var v = var(i);
                 Node lhs = this.in(this.update(v,null));
                 Node rhs = that.in(that.update(v,null));
                 setDef(i, new PhiNode(v._name, v.type(), r, lhs, rhs).peephole());
@@ -297,7 +298,7 @@ private void _merge(ScopeNode that, RegionNode r) {
     // Forward refs are copied to the other side, "as if" they were there all along.
     public void balanceIf( ScopeNode scope ) {
         for( int i = nIns(); i < scope.nIns(); i++ ) {
-            Var n = scope._vars.at(i);
+            Var n = scope.var(i);
             if( n.isFRef() ) {  // RHS has forward refs
                 _vars.add(n);   // Copy to LHS
                 addDef(scope.in(i));
@@ -329,8 +330,8 @@ public void endLoop(ScopeNode back, ScopeNode exit ) {
     // Fill in the backedge of any inserted Phis
     void _endLoop( ScopeNode scope, Node back, Node exit ) {
         for( int i=2; i<nIns(); i++ ) {
-            if( _vars.at(i)._final ) continue; // Final vars did not get modified in the loop
-            if( _vars.at(i).type().isHighOrConst() ) continue; // Cannot lift higher than a constant, so no Phi
+            if( var(i)._final ) continue; // Final vars did not get modified in the loop
+            if( var(i).type().isHighOrConst() ) continue; // Cannot lift higher than a constant, so no Phi
             if( back.in(i) != scope ) {
                 PhiNode phi = (PhiNode)in(i);
                 assert phi.region()==scope.ctrl() && phi.in(2)==null;
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ShlNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ShlNode.java
index 8b9a66c..ea5f254 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ShlNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ShlNode.java
@@ -4,25 +4,23 @@
 import com.compilerprogramming.ezlang.compiler.sontypes.Type;
 import com.compilerprogramming.ezlang.compiler.sontypes.TypeInteger;
 
-public class ShlNode extends LogicalNode {
+public class ShlNode extends ArithNode {
     public ShlNode(Node lhs, Node rhs) { super(lhs, rhs); }
 
     @Override public String label() { return "Shl"; }
     @Override public String op() { return "<<"; }
-
     @Override public String glabel() { return "&lt;&lt;"; }
 
-    @Override
-    public Type compute() {
-        Type t1 = in(1)._type, t2 = in(2)._type;
-        if( t1.isHigh() || t2.isHigh() )
-            return TypeInteger.TOP;
-        if (t1 instanceof TypeInteger i0 &&
-            t2 instanceof TypeInteger i1 ) {
-            if( i0 == TypeInteger.ZERO )
-                return TypeInteger.ZERO;
-            if( i0.isConstant() && i1.isConstant() )
-                return TypeInteger.constant(i0.value()<<i1.value());
+    @Override long doOp( long x, long y ) { return x << y; }
+    @Override TypeInteger doOp( TypeInteger x, TypeInteger y ) {
+        if( x == TypeInteger.ZERO )
+            return TypeInteger.ZERO;
+        if( y.isConstant() ) {
+            int shf = (int)y.value();
+            // If no overflow, shift endpoints
+            if( !(((x._min<<shf)>>shf) != x._min ||
+                  ((x._max<<shf)>>shf) != x._max ) )
+                    return TypeInteger.make(x._min<<shf,x._max<<shf);
         }
         return TypeInteger.BOT;
     }
@@ -46,7 +44,7 @@ public Node idealize() {
 
         // TODO: x << 3 << (y ? 1 : 2) ==> x << (y ? 4 : 5)
 
-        return null;
+        return super.idealize();
     }
     @Override Node copy(Node lhs, Node rhs) { return new ShlNode(lhs,rhs); }
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ShrNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ShrNode.java
index f50ac65..2fb8aad 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ShrNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ShrNode.java
@@ -3,27 +3,16 @@
 import com.compilerprogramming.ezlang.compiler.sontypes.Type;
 import com.compilerprogramming.ezlang.compiler.sontypes.TypeInteger;
 
-public class ShrNode extends LogicalNode {
+public class ShrNode extends ArithNode {
     public ShrNode(Node lhs, Node rhs) { super(lhs, rhs); }
 
     @Override public String label() { return "Shr"; }
     @Override public String op() { return ">>>"; }
-
     @Override public String glabel() { return "&gt;&gt;&gt;"; }
 
-    @Override
-    public Type compute() {
-        Type t1 = in(1)._type, t2 = in(2)._type;
-        if( t1.isHigh() || t2.isHigh() )
-            return TypeInteger.TOP;
-        if (t1 instanceof TypeInteger i0 &&
-            t2 instanceof TypeInteger i1 ) {
-            if( i0 == TypeInteger.ZERO )
-                return TypeInteger.ZERO;
-            if( i0.isConstant() && i1.isConstant() )
-                return TypeInteger.constant(i0.value()>>>i1.value());
-        }
-        return TypeInteger.BOT;
+    @Override long doOp( long x, long y ) { return x >>> y; }
+    @Override TypeInteger doOp( TypeInteger x, TypeInteger y ) {
+        return x == TypeInteger.ZERO ? x : TypeInteger.BOT;
     }
 
     @Override
@@ -38,7 +27,7 @@ public Node idealize() {
 
         // TODO: x >>> 3 >>> (y ? 1 : 2) ==> x >>> (y ? 4 : 5)
 
-        return null;
+        return super.idealize();
     }
     @Override Node copy(Node lhs, Node rhs) { return new ShrNode(lhs,rhs); }
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StartNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StartNode.java
index ba18e35..b03a3a4 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StartNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StartNode.java
@@ -44,7 +44,7 @@ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
 
 
     @Override public TypeTuple compute() {
-        return TypeTuple.make(Type.CONTROL, TypeMem.TOP,_arg);
+        return TypeTuple.make(Type.CONTROL,TypeMem.TOP,_arg);
     }
 
     @Override public Node idealize() { return null; }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StopNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StopNode.java
index 5f19131..9dcc8e3 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StopNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StopNode.java
@@ -22,11 +22,15 @@ public String label() {
     @Override
     public StringBuilder _print1(StringBuilder sb, BitSet visited) {
         // For the sake of many old tests, and single value prints as "return val"
-        if( ret()!=null ) return ret()._print0(sb,visited);
+        ReturnNode ret1 = ret();
+        if( ret1!=null ) return ret1._print0(sb,visited);
         sb.append("Stop[ ");
         for( Node ret : _inputs )
-            if( ret!=null )
-                ret._print0(sb, visited).append(" ");
+            if( ret!=null ) {
+                String name = ((ReturnNode)ret).fun()._name;
+                if( name== null || !name.startsWith("sys.") )
+                    ret._print0(sb, visited).append(" ");
+            }
         return sb.append("]");
     }
 
@@ -36,7 +40,13 @@ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
     // If a single Return, return it.
     // Otherwise, null because ambiguous.
     public ReturnNode ret() {
-        return nIns()==1 && in(0) instanceof ReturnNode ret ? ret : null;
+        Node ret1 = this;
+        for( Node ret : _inputs ) {
+            String name = ((ReturnNode)ret).fun()._name;
+            if( name==null || !name.startsWith("sys.") )
+                ret1 = ret1==this ? ((ReturnNode)ret) : null;
+        }
+        return ret1==this ? null : (ReturnNode)ret1;
     }
 
     @Override
@@ -48,7 +58,7 @@ public Type compute() {
     public Node idealize() {
         int len = nIns();
         for( int i=0; i<nIns(); i++ )
-            if( ((ReturnNode)in(i)).fun().isDead() )
+            if( addDep(((ReturnNode)in(i)).fun()).isDead() )
                 delDef(i--);
         if( len != nIns() ) return this;
         return null;
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StoreNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StoreNode.java
index fc3d9a9..37e3fc3 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StoreNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StoreNode.java
@@ -73,6 +73,12 @@ public Node idealize() {
             return this;
         }
 
+        // Simple store-after-MemMerge to a known alias can bypass.  Happens when inlining.
+        if( mem() instanceof MemMergeNode mem ) {
+            setDef(1,mem.alias(_alias));
+            return this;
+        }
+
         // Value is automatically truncated by narrow store
         if( val() instanceof AndNode and && and.in(2)._type.isConstant()  ) {
             int log = _declaredType.log_size();
@@ -86,12 +92,23 @@ public Node idealize() {
             }
         }
 
-        // Store of zero after alloc
-        if( mem() instanceof ProjNode prj && prj.in(0) instanceof NewNode &&
-            prj.in(0)==ptr().in(0) &&  // Same NewNode memory & pointer
-            (val()._type== TypeInteger.ZERO || val()._type== Type.NIL ) )
-            return mem();
-
+        // Store will chop high order bits off; math to change those bits can be dropped.
+        if( val() instanceof SarNode shr &&
+            shr.in(1) instanceof ShlNode shl &&
+            shr.in(2)._type.isConstant() &&
+            shl.in(2)._type.isConstant() ) {
+            TypeInteger shrC = (TypeInteger) shr.in(2)._type;
+
+            // size of the thing that sign-extends
+            int base_size = (1 << shr.in(1)._type.log_size()) << 3;
+            int not_affected_bits = base_size - (int) shrC.value();
+            int store_size = (1 << log_size()) << 3;
+            // if the store is unrelated to the shift amount, then get rid of the shift
+            if( shl.in(2)._type == shr.in(2)._type && shrC.value() >= store_size && not_affected_bits >= store_size) {
+                setDef(4, shl.in(1));
+                return this;
+            }
+        }
         return null;
     }
 
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/SubNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/SubNode.java
index 151de7f..78fb8bc 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/SubNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/SubNode.java
@@ -4,38 +4,22 @@
 import com.compilerprogramming.ezlang.compiler.sontypes.TypeInteger;
 import java.util.BitSet;
 
-public class SubNode extends Node {
-    public SubNode(Node lhs, Node rhs) { super(null, lhs, rhs); }
+public class SubNode extends ArithNode {
+    public SubNode(Node lhs, Node rhs) { super(lhs, rhs); }
 
     @Override public String label() { return "Sub"; }
+    @Override public String op() { return "-"; }
 
-    @Override public String glabel() { return "-"; }
-
-    @Override
-    public StringBuilder _print1(StringBuilder sb, BitSet visited) {
-        in(1)._print0(sb.append("("), visited);
-        in(2)._print0(sb.append("-"), visited);
-        return sb.append(")");
-    }
-
-    @Override
-    public Type compute() {
-        Type t1 = in(1)._type, t2 = in(2)._type;
-        if( t1.isHigh() || t2.isHigh() )
-            return TypeInteger.TOP;
+    @Override long doOp( long x, long y ) { return x - y; }
+    @Override TypeInteger doOp(TypeInteger x, TypeInteger y) {
         // Sub of same is 0
         if( in(1)==in(2) )
             return TypeInteger.ZERO;
-        if( t1 instanceof TypeInteger i1 &&
-            t2 instanceof TypeInteger i2 ) {
-            if (i1.isConstant() && i2.isConstant())
-                return TypeInteger.constant(i1.value()-i2.value());
-            // Fold ranges like {2-3} - {0-1} into {1-3}.
-            if( !AddNode.overflow(i1._min,-i2._max) &&
-                !AddNode.overflow(i1._max,-i2._min) &&
-                i2._min != Long.MIN_VALUE  )
-                return TypeInteger.make(i1._min-i2._max,i1._max-i2._min);
-        }
+        // Fold ranges like {2-3} - {0-1} into {1-3}.
+        if( !AddNode.overflow(x._min,-y._max) &&
+            !AddNode.overflow(x._max,-y._min) &&
+            y._min != Long.MIN_VALUE  )
+            return TypeInteger.make(x._min-y._max,x._max-y._min);
 
         return TypeInteger.BOT;
     }
@@ -50,7 +34,7 @@ public Node idealize() {
         if( in(1) instanceof MinusNode minus )
             return new MinusNode(new AddNode(minus.in(1),in(2)).peephole());
 
-        return null;
+        return super.idealize();
     }
 
     @Override Node copy(Node lhs, Node rhs) { return new SubNode(lhs,rhs); }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/XorNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/XorNode.java
index 3595b56..933ecc5 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/XorNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/XorNode.java
@@ -3,24 +3,15 @@
 import com.compilerprogramming.ezlang.compiler.sontypes.Type;
 import com.compilerprogramming.ezlang.compiler.sontypes.TypeInteger;
 
-public class XorNode extends LogicalNode {
+public class XorNode extends ArithNode {
     public XorNode(Node lhs, Node rhs) { super(lhs, rhs); }
 
     @Override public String label() { return "Xor"; }
     @Override public String op() { return "^"; }
-
     @Override public String glabel() { return "^"; }
 
-    @Override
-    public Type compute() {
-        Type t1 = in(1)._type, t2 = in(2)._type;
-        if( t1.isHigh() || t2.isHigh() )
-            return TypeInteger.TOP;
-        if( t1 instanceof TypeInteger i0 &&
-            t2 instanceof TypeInteger i1 ) {
-            if( i0.isConstant() && i1.isConstant() )
-                return TypeInteger.constant(i0.value()^i1.value());
-        }
+    @Override long doOp( long x, long y ) { return x ^ y; }
+    @Override TypeInteger doOp( TypeInteger x, TypeInteger y ) {
         return TypeInteger.BOT;
     }
 
@@ -46,7 +37,7 @@ public Node idealize() {
         Node phicon = AddNode.phiCon(this,true);
         if( phicon!=null ) return phicon;
 
-        return null;
+        return super.idealize();
     }
     @Override Node copy(Node lhs, Node rhs) { return new XorNode(lhs,rhs); }
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CallARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CallARM.java
index 86b53ef..ca89abd 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CallARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CallARM.java
@@ -8,13 +8,13 @@
 public class CallARM extends CallNode implements MachNode, RIPRelSize {
     final TypeFunPtr _tfp;
     final String _name;
-
     CallARM(CallNode call, TypeFunPtr tfp) {
         super(call);
         _inputs.pop(); // Pop constant target
         assert tfp.isConstant();
         _tfp = tfp;
-        _name = CodeGen.CODE.link(tfp)._name;
+        FunNode fun = CodeGen.CODE.link(tfp);
+        _name = fun==null ? ((ExternNode)call.fptr())._extern : fun._name; // Can be null for extern calls
     }
 
     @Override public String op() { return "call"; }
@@ -23,9 +23,12 @@ public class CallARM extends CallNode implements MachNode, RIPRelSize {
     @Override public TypeFunPtr tfp() { return _tfp; }
     @Override public RegMask regmap(int i) { return arm.callInMask(_tfp,i,fun()._maxArgSlot); }
     @Override public RegMask outregmap() { return null; }
+    @Override public int nargs() { return nIns()-2; } // Minus control, memory, fptr
 
     @Override public void encoding( Encoding enc ) {
-        enc.relo(this);
+        FunNode fun = CodeGen.CODE.link(_tfp);
+        if( fun==null ) enc.external(this,_name);
+        else enc.relo(this);
         // BL
         enc.add4(arm.b(arm.OP_CALL,0)); // Target patched at link time
     }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CallEndARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CallEndARM.java
deleted file mode 100644
index eb868c1..0000000
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CallEndARM.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package com.compilerprogramming.ezlang.compiler.nodes.cpus.arm;
-
-import com.compilerprogramming.ezlang.compiler.nodes.CallEndNode;
-import com.compilerprogramming.ezlang.compiler.nodes.MachNode;
-
-public class CallEndARM extends CallEndNode implements MachNode {
-    CallEndARM( CallEndNode cend ) { super(cend); }
-}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/FloatARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/FloatARM.java
index 5d1b14a..dbf422a 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/FloatARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/FloatARM.java
@@ -19,8 +19,6 @@ public class FloatARM extends ConstantNode implements MachNode, RIPRelSize {
     @Override public void encoding( Encoding enc ) {
         enc.largeConstant(this,_con,0,-1/*TODO: ARM-style ELF patching*/);
         short dst = (short)(enc.reg(this) - arm.D_OFFSET);
-        double d = ((TypeFloat)_con).value();
-        long x = Double.doubleToRawLongBits(d);
         enc.add4(arm.load_pc(arm.OPF_ARM, 0, dst));
     }
 
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/IntARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/IntARM.java
index 4190ce4..6553b48 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/IntARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/IntARM.java
@@ -20,7 +20,7 @@ public class IntARM extends ConstantNode implements MachNode {
 
     @Override public void encoding( Encoding enc ) {
         short self = enc.reg(this);
-        long x = _con== Type.NIL ? 0 : ((TypeInteger)_con).value();
+        long x = _con==Type.NIL ? 0 : ((TypeInteger)_con).value();
         int nb0 = 0;
         int nb1 = 0;
         // Count number of 0000 and FFFF blocks
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/TFPARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/TFPARM.java
index eda874e..4742edb 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/TFPARM.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/TFPARM.java
@@ -4,6 +4,7 @@
 import com.compilerprogramming.ezlang.compiler.codegen.*;
 import com.compilerprogramming.ezlang.compiler.nodes.ConstantNode;
 import com.compilerprogramming.ezlang.compiler.nodes.MachNode;
+import com.compilerprogramming.ezlang.compiler.nodes.Node;
 
 public class TFPARM extends ConstantNode implements MachNode, RIPRelSize {
     TFPARM( ConstantNode con ) { super(con); }
@@ -23,21 +24,22 @@ public class TFPARM extends ConstantNode implements MachNode, RIPRelSize {
     }
 
     @Override public byte encSize(int delta) {
-        if( -(1L<<12) <= delta && delta < (1L<<12) ) return 4;
-        throw Utils.TODO();
+        return 8;
     }
 
     // Delta is from opcode start
     @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) {
         short rpc = enc.reg(this);
         if(opLen == 8 ) {
-            // opstart of add
-            int next = opStart + opLen;
+            // ARM encoding delta is from PC & 0xFFF
+            int target = opStart+delta;
+            int base = opStart & ~0xFFF;
+            delta = target-base;
             int adrp_delta = delta >> 12;
             // patch upper 20 bits via adrp
             enc.patch4(opStart, arm.adrp(1, adrp_delta & 0b11, 0b10000, adrp_delta >> 2, rpc));
             // low 12 bits via add
-            enc.patch4(next, arm.imm_inst_l(arm.OPI_ADD, delta & 0xfff, rpc));
+            enc.patch4(opStart+4, arm.imm_inst_l(arm.OPI_ADD, delta & 0xfff, rpc));
         } else {
             // should not happen as one instruction is 4 byte, and TFP arm encodes 2.
             throw Utils.TODO();
@@ -48,4 +50,5 @@ public class TFPARM extends ConstantNode implements MachNode, RIPRelSize {
         String reg = code.reg(this);
         _con.print(sb.p(reg).p(" #"));
     }
+    @Override public boolean eq(Node n) { return this==n; }
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/TMPARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/TMPARM.java
new file mode 100644
index 0000000..0356677
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/TMPARM.java
@@ -0,0 +1,53 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.arm;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.ConstantNode;
+import com.compilerprogramming.ezlang.compiler.nodes.MachNode;
+import com.compilerprogramming.ezlang.compiler.nodes.Node;
+import com.compilerprogramming.ezlang.compiler.sontypes.TypeMemPtr;
+
+public class TMPARM extends ConstantNode implements MachNode, RIPRelSize {
+    TMPARM( ConstantNode con ) { super(con); }
+    @Override public String op() { return "ldp"; }
+    @Override public RegMask regmap(int i) { return null; }
+    @Override public RegMask outregmap() { return arm.WMASK; }
+    @Override public boolean isClone() { return true; }
+    @Override public TMPARM copy() { return new TMPARM(this); }
+    @Override public void encoding( Encoding enc ) {
+        enc.largeConstant(this,((TypeMemPtr)_con)._obj,0,-1);
+        short dst = enc.reg(this);
+        // adrp    x0, 0
+        enc.add4(arm.adrp(1,0, arm.OP_ADRP, 0,dst));
+        // add     x0, x0, 0
+        arm.imm_inst(enc,arm.OPI_ADD,0, dst);
+    }
+
+    @Override public byte encSize(int delta) {
+        return 8;
+    }
+
+    // Delta is from opcode start
+    @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) {
+        short dst = enc.reg(this);
+        if(opLen == 8 ) {
+            // ARM encoding delta is from PC & 0xFFF
+            int target = opStart+delta;
+            int base = opStart & ~0xFFF;
+            delta = target-base;
+            int adrp_delta = delta >> 12;
+            // patch upper 20 bits via adrp
+            enc.patch4(opStart, arm.adrp(1, adrp_delta & 0b11, 0b10000, adrp_delta >> 2, dst));
+            // low 12 bits via add
+            enc.patch4(opStart+4, arm.imm_inst_l(arm.OPI_ADD, delta & 0xfff, dst));
+        } else {
+            throw Utils.TODO();
+        }
+    }
+
+    @Override public void asm(CodeGen code, SB sb) {
+        String reg = code.reg(this);
+        _con.print(sb.p(reg).p(" #"));
+    }
+    @Override public boolean eq(Node n) { return this==n; }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/arm.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/arm.java
index e56b593..cf541f7 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/arm.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/arm.java
@@ -568,12 +568,14 @@ static RegMask callInMask(TypeFunPtr tfp, int idx, int maxArgSlot ) {
         if( idx==0 ) return CodeGen.CODE._rpcMask;
         if( idx==1 ) return null;
         // Count floats in signature up to index
+        if( idx-2 >= tfp.nargs() ) return null; // Anti-dependence
+        // Count floats in signature up to index
         int fcnt=0;
         for( int i=2; i<idx; i++ )
             if( tfp.arg(i-2) instanceof TypeFloat)
                 fcnt++;
         // Floats up to XMMS in XMM registers
-        if( tfp.arg(idx-2) instanceof TypeFloat) {
+        if( tfp.arg(idx-2) instanceof TypeFloat ) {
             if( fcnt < XMMS.length )
                 return XMMS[fcnt];
         } else {
@@ -611,7 +613,7 @@ public static long decodeImm12(int imm12) {
     @Override public short maxArgSlot( TypeFunPtr tfp ) {
         int icnt=0, fcnt=0;     // Count of ints, floats
         for( int i=0; i<tfp.nargs(); i++ ) {
-            if( tfp.arg(i) instanceof TypeFloat) fcnt++;
+            if( tfp.arg(i) instanceof TypeFloat ) fcnt++;
             else icnt++;
         }
         int nstk = Math.max(icnt-8,0)+Math.max(fcnt-8,0);
@@ -644,44 +646,44 @@ public static long decodeImm12(int imm12) {
     // Instruction selection
     @Override public Node instSelect(Node n ) {
         return switch( n ) {
-        case AddFNode addf  -> new AddFARM(addf);
-        case AddNode add    -> add(add);
-        case AndNode and    -> and(and);
-        case BoolNode bool  -> cmp(bool);
-        case CallNode call  -> call(call);
-        case CastNode cast  -> new CastARM(cast);
-        case CallEndNode cend -> new CallEndARM(cend);
-        case CProjNode c    -> new CProjNode(c);
-        case ConstantNode con -> con(con);
-        case DivFNode divf  -> new DivFARM(divf);
-        case DivNode div    -> new DivARM(div);
-        case FunNode fun    -> new FunARM(fun);
-        case IfNode iff     -> jmp(iff);
-        case LoadNode ld    -> ld(ld);
-        case MemMergeNode mem -> new MemMergeNode(mem);
-        case MinusNode neg  -> new NegARM(neg);
-        case MulFNode mulf  -> new MulFARM(mulf);
-        case MulNode mul    -> new MulARM(mul);
-        case NewNode nnn    -> new NewARM(nnn);
-        case NotNode not    -> new NotARM(not);
-        case OrNode or      -> or(or);
-        case ParmNode parm  -> new ParmARM(parm);
-        case PhiNode phi    -> new PhiNode(phi);
-        case ProjNode prj   -> new ProjARM(prj);
-        case ReadOnlyNode read  -> new ReadOnlyNode(read);
-        case ReturnNode ret -> new RetARM(ret,ret.fun());
-        case SarNode sar    -> asr(sar);
-        case ShlNode shl    -> lsl(shl);
-        case ShrNode shr    -> lsr(shr);
+        case AddFNode addf   -> new AddFARM(addf);
+        case AddNode add     -> add(add);
+        case AndNode and     -> and(and);
+        case BoolNode bool   -> cmp(bool);
+        case CallNode call   -> call(call);
+        case CastNode cast   -> new CastMach(cast);
+        case CallEndNode cend-> new CallEndMach(cend);
+        case CProjNode c     -> new CProjNode(c);
+        case ConstantNode con-> con(con);
+        case DivFNode divf   -> new DivFARM(divf);
+        case DivNode div     -> new DivARM(div);
+        case FunNode fun     -> new FunARM(fun);
+        case IfNode iff      -> jmp(iff);
+        case LoadNode ld     -> ld(ld);
+        case MemMergeNode mem-> new MemMergeNode(mem);
+        case MinusNode neg   -> new NegARM(neg);
+        case MulFNode mulf   -> new MulFARM(mulf);
+        case MulNode mul     -> new MulARM(mul);
+        case NewNode nnn     -> new NewARM(nnn);
+        case NotNode not     -> new NotARM(not);
+        case OrNode or       -> or(or);
+        case ParmNode parm   -> new ParmARM(parm);
+        case PhiNode phi     -> new PhiNode(phi);
+        case ProjNode prj    -> new ProjARM(prj);
+        case ReadOnlyNode read -> new ReadOnlyMach(read);
+        case ReturnNode ret  -> new RetARM(ret,ret.fun());
+        case SarNode sar     -> asr(sar);
+        case ShlNode shl     -> lsl(shl);
+        case ShrNode shr     -> lsr(shr);
         case StartNode start -> new StartNode(start);
-        case StopNode stop  -> new StopNode(stop);
-        case StoreNode st   -> st(st);
-        case SubFNode subf  -> new SubFARM(subf);
-        case SubNode sub    -> sub(sub);
-        case ToFloatNode tfn-> new I2F8ARM(tfn);
-        case XorNode xor    -> xor(xor);
-
-        case LoopNode loop  -> new LoopNode(loop);
+        case StopNode stop   -> new StopNode(stop);
+        case StoreNode st    -> st(st);
+        case SubFNode subf   -> new SubFARM(subf);
+        case SubNode sub     -> sub(sub);
+        case ToFloatNode tfn -> new I2F8ARM(tfn);
+        case XorNode xor     -> xor(xor);
+
+        case LoopNode loop   -> new LoopNode(loop);
         case RegionNode region-> new RegionNode(region);
         default -> throw Utils.TODO();
         };
@@ -729,13 +731,13 @@ private Node sub(SubNode sub) {
     private Node con( ConstantNode con ) {
         if( !con._con.isConstant() ) return new ConstantNode( con ); // Default unknown caller inputs
         return switch( con._con ) {
-            case TypeInteger ti  -> new IntARM(con);
-            case TypeFloat tf  -> new FloatARM(con);
-            case TypeFunPtr tfp -> new TFPARM(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 -> t== Type.NIL ? new IntARM(con) : new ConstantNode(con);
+        case TypeInteger ti -> new IntARM(con);
+        case TypeFloat   tf -> new FloatARM(con);
+        case TypeFunPtr tfp -> new TFPARM(con);
+        case TypeMemPtr tmp -> new TMPARM(con);
+        case TypeNil tn  -> throw Utils.TODO();
+        // TOP, BOTTOM, XCtrl, Ctrl, etc.  Never any executable code.
+        case Type t -> t==Type.NIL ? new IntARM(con) : new ConstantNode(con);
         };
     }
 
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CallEndRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CallEndRISC.java
deleted file mode 100644
index 12f2b81..0000000
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CallEndRISC.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv;
-
-import com.compilerprogramming.ezlang.compiler.nodes.CallEndNode;
-import com.compilerprogramming.ezlang.compiler.nodes.MachNode;
-
-public class CallEndRISC extends CallEndNode implements MachNode {
-    CallEndRISC( CallEndNode cend ) { super(cend); }
-}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CallRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CallRISC.java
index a552e4c..79ab6ca 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CallRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CallRISC.java
@@ -13,23 +13,26 @@ public class CallRISC extends CallNode implements MachNode, RIPRelSize {
         assert tfp.isConstant();
         _inputs.pop(); // Pop constant target
         _tfp = tfp;
-        _name = CodeGen.CODE.link(tfp)._name;
+        FunNode fun = CodeGen.CODE.link(tfp);
+        _name = fun==null ? ((ExternNode)call.fptr())._extern : fun._name; // Can be null for extern calls
     }
 
     @Override public String op() { return "call"; }
     @Override public String label() { return op(); }
-    @Override public RegMask regmap(int i) {
-        return riscv.callInMask(_tfp,i,fun()._maxArgSlot);
-    }
-    @Override public RegMask outregmap() { return null; }
     @Override public String name() { return _name; }
     @Override public TypeFunPtr tfp() { return _tfp; }
+    @Override public RegMask regmap(int i) { return riscv.callInMask(_tfp,i,fun()._maxArgSlot); }
+    @Override public RegMask outregmap() { return riscv.RPC_MASK; }
+    @Override public int nargs() { return nIns()-2; } // Minus control, memory, fptr
 
     @Override public void encoding( Encoding enc ) {
         // Short form +/-4K:  beq r0,r0,imm12
         // Long form:  auipc rX,imm20/32; jal r0,[rX+imm12/32]
-        enc.relo(this);
-        enc.add4(riscv.j_type(riscv.OP_JAL, riscv.RPC, 0));
+        FunNode fun = CodeGen.CODE.link(_tfp);
+        if( fun==null ) enc.external(this,_name);
+        else enc.relo(this);
+        short rpc = enc.reg(this);
+        enc.add4(riscv.j_type(riscv.OP_JAL, rpc, 0));
     }
 
     // Delta is from opcode start
@@ -41,8 +44,9 @@ public class CallRISC extends CallNode implements MachNode, RIPRelSize {
 
     // Delta is from opcode start
     @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) {
+        short rpc = enc.reg(this);
         if( opLen==4 ) {
-            enc.patch4(opStart,riscv.j_type(riscv.OP_JAL, riscv.RPC, delta));
+            enc.patch4(opStart,riscv.j_type(riscv.OP_JAL, rpc, delta));
         } else {
             throw Utils.TODO();
         }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/FltRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/FltRISC.java
index 31d0f1b..4e8ab87 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/FltRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/FltRISC.java
@@ -14,7 +14,7 @@ public class FltRISC extends ConstantNode implements MachNode, RIPRelSize {
     @Override public FltRISC copy() { return new FltRISC(this); }
 
     @Override public void encoding( Encoding enc ) {
-        enc.largeConstant(this,_con, 0, -1/*TODO: RISC5 style patching*/);
+        enc.largeConstant(this,_con, 0, -1);
         short dst = (short)(enc.reg(this) - riscv.F_OFFSET);
         short tmp = (short)riscv.T6;
         // AUIPC dst,#hi20_constant_pool
@@ -25,7 +25,6 @@ public class FltRISC extends ConstantNode implements MachNode, RIPRelSize {
     @Override public RegMask killmap() { return new RegMask(riscv.T6); }
 
     // Delta is from opcode start.
-    // TODO: always size 8?
     @Override public byte encSize(int delta) { return 8;  }
 
     // Delta is from opcode start
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/ImmRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/ImmRISC.java
index 814b984..59f638c 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/ImmRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/ImmRISC.java
@@ -40,8 +40,7 @@ abstract public class ImmRISC extends MachConcreteNode implements MachNode {
     @Override public void encoding( Encoding enc ) {
         short dst = enc.reg(this );
         short src = enc.reg(in(1));
-        int body = riscv.i_type(opcode(), dst, func3(), src, _imm12 & 0xFFF);
-        enc.add4(body);
+        enc.add4(riscv.i_type(opcode(), dst, func3(), src, _imm12 & 0xFFF));
     }
 
     // General form: "addi  rd = rs1 + imm"
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/Int8RISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/Int8RISC.java
new file mode 100644
index 0000000..9b56665
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/Int8RISC.java
@@ -0,0 +1,47 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv;
+
+import com.compilerprogramming.ezlang.compiler.nodes.ConstantNode;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.MachNode;
+
+// Special instruction for loading 8 byte constants from the constant pool.
+
+public class Int8RISC extends ConstantNode implements MachNode, RIPRelSize {
+    public Int8RISC(ConstantNode con) { super(con); }
+
+    @Override public String op() { return "ld8"; }
+    @Override public RegMask regmap(int i) { return null; }
+    @Override public RegMask outregmap() { return riscv.WMASK; }
+    @Override public boolean isClone() { return true; }
+    @Override public Int8RISC copy() { return new Int8RISC(this); }
+
+    @Override public byte encSize(int delta) { return 8;  }
+
+    @Override public void encoding( Encoding enc ) {
+        short dst  = enc.reg(this);
+        short tmp = (short)riscv.T6;
+        enc.largeConstant(this,_con, 0, -1);
+        // AUIPC dst,#hi20_constant_pool
+        enc.add4(riscv.u_type(riscv.OP_AUIPC, tmp, 0));
+        // Load dst,[dst+#low12_constant_pool]
+        enc.add4(riscv.i_type(riscv.OP_LOAD, dst, 0b11, tmp, 0));
+    }
+    @Override public RegMask killmap() { return new RegMask(riscv.T6); }
+
+    // Delta is from opcode start
+    @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) {
+        short dst = enc.reg(this);
+        short tmp = (short)riscv.T6;
+        // AUIPC dst,#hi20_constant_pool
+        enc.patch4(opStart  , riscv.u_type(riscv.OP_AUIPC, tmp, delta>>12));
+        // Load dst,[dst+#low12_constant_pool]
+        enc.patch4(opStart+4, riscv.i_type(riscv.OP_LOAD, dst, 0b11, tmp, delta & 0xFFF));
+    }
+
+    @Override public void asm(CodeGen code, SB sb) {
+        _con.print(sb.p(code.reg(this)).p(" = #"));
+    }
+
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/IntRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/IntRISC.java
index 905800f..5b42b74 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/IntRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/IntRISC.java
@@ -18,7 +18,7 @@ public class IntRISC extends ConstantNode implements MachNode {
     @Override public IntRISC copy() { return new IntRISC(this); }
     @Override public void encoding( Encoding enc ) {
         short dst  = enc.reg(this);
-        int val = _con== Type.NIL ? 0 : (int)(((TypeInteger)_con).value() & 0xFFF);
+        int val = _con==Type.NIL ? 0 : (int)(((TypeInteger)_con).value() & 0xFFF);
         // Explicit truncation of larger immediates; this will sign-extend on
         // load and this is handled during instruction selection.
         enc.add4(riscv.i_type(riscv.OP_IMM, dst, 0, riscv.ZERO, val));
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/LUI.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/LUI.java
index a804ba8..435482e 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/LUI.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/LUI.java
@@ -10,7 +10,7 @@
 public class LUI extends ConstantNode implements MachNode {
     public LUI( int imm20 ) {
         super(TypeInteger.constant(imm20));
-        assert riscv.imm20Exact((TypeInteger)_con);
+        assert (imm20 & 0xFFF)==0;
     }
     @Override public RegMask regmap(int i) { return null; }
     @Override public RegMask outregmap() { return riscv.WMASK; }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/MemOpRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/MemOpRISC.java
index f8d1ddf..a74ef63 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/MemOpRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/MemOpRISC.java
@@ -35,8 +35,9 @@ int func3() {
         if(this instanceof StoreRISC) {
             if( _declaredType == TypeInteger. I8 || _declaredType == TypeInteger.U8  || _declaredType == TypeInteger.BOOL) func3=0; //   SB
             if( _declaredType == TypeInteger.I16 || _declaredType == TypeInteger.U16 ) func3=1; // SH
-            if( _declaredType == TypeInteger.I32 || _declaredType == TypeInteger.U32 || _declaredType instanceof TypeMemPtr) func3=2; //  SW
-            if( _declaredType == TypeInteger.BOT ) func3=3; //   SD
+            if( _declaredType == TypeInteger.I32 || _declaredType == TypeInteger.U32 ) func3=2; //  SW
+            if( _declaredType instanceof TypeMemPtr) func3=3; //  SD
+            if( _declaredType == TypeInteger.BOT   ) func3=3; //   SD
             if( func3 == -1 ) throw Utils.TODO();
             return func3;
         }
@@ -50,9 +51,10 @@ int func3() {
         if( _declaredType == TypeInteger.U32 ) func3=6; // LWU
 
         // float
-        if(_declaredType == TypeFloat.F32) func3 = 2; // fLW   fSW
-        if(_declaredType == TypeFloat.F64) func3 = 3; // fLD   fSD
-        if( _declaredType instanceof TypeMemPtr) func3=3; // 8 byte pointers
+        if( _declaredType == TypeFloat.F32) func3 = 2; // fLW   fSW
+        if( _declaredType == TypeFloat.F64) func3 = 3; // fLD   fSD
+
+        if( _declaredType instanceof TypeMemPtr) func3 = 3; // 8 byte pointers (pick ld)
         if( func3 == -1 ) throw Utils.TODO();
         return func3;
     }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SraIRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SraIRISC.java
index 2ccf60a..b8842ff 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SraIRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/SraIRISC.java
@@ -4,7 +4,9 @@
 import com.compilerprogramming.ezlang.compiler.nodes.Node;
 
 public class SraIRISC extends ImmRISC {
-    public SraIRISC( Node and, int imm) { super(and,imm); }
+    public SraIRISC( Node and, int imm) {
+        super(and,imm | (0x20 << 5));
+    }
     @Override int opcode() {  return riscv.OP_IMM; }
     @Override int func3() {  return 5;}
     @Override public String glabel() { return ">>"; }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/TFPRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/TFPRISC.java
index fcdd904..599fbed 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/TFPRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/TFPRISC.java
@@ -4,6 +4,7 @@
 import com.compilerprogramming.ezlang.compiler.codegen.*;
 import com.compilerprogramming.ezlang.compiler.nodes.ConstantNode;
 import com.compilerprogramming.ezlang.compiler.nodes.MachNode;
+import com.compilerprogramming.ezlang.compiler.nodes.Node;
 import com.compilerprogramming.ezlang.compiler.sontypes.TypeFunPtr;
 
 public class TFPRISC extends ConstantNode implements MachNode, RIPRelSize {
@@ -19,11 +20,9 @@ public class TFPRISC extends ConstantNode implements MachNode, RIPRelSize {
         short dst = enc.reg(this);
         TypeFunPtr tfp = (TypeFunPtr)_con;
         // auipc  t0,0
-        int auipc = riscv.u_type(riscv.OP_AUIPC, dst, 0);
+        enc.add4(riscv.u_type(riscv.OP_AUIPC, dst, 0));
         // addi   t1,t0 + #0
-        int addi = riscv.i_type(riscv.OP_IMM, dst, 0, dst, 0);
-        enc.add4(auipc);
-        enc.add4(addi);
+        enc.add4(riscv.i_type(riscv.OP_IMM, dst, 0, dst, 0));
     }
 
     @Override public byte encSize(int delta) {
@@ -38,7 +37,7 @@ public class TFPRISC extends ConstantNode implements MachNode, RIPRelSize {
             // AUIPC (upper 20 bits)
             // opstart of add
             int next = opStart + opLen;
-            enc.patch4(opStart,riscv.u_type(riscv.OP_AUIPC, rpc, delta));
+            enc.patch4(opStart,riscv.u_type(riscv.OP_AUIPC, rpc, delta>>12));
             // addi(low 12 bits)
             enc.patch4(next,riscv.i_type(riscv.OP_IMM, rpc, 0, rpc, delta & 0xFFF));
             // addi
@@ -51,5 +50,5 @@ public class TFPRISC extends ConstantNode implements MachNode, RIPRelSize {
     @Override public void asm(CodeGen code, SB sb) {
         _con.print(sb.p(code.reg(this)).p(" #"));
     }
-
+    @Override public boolean eq(Node n) { return this==n; }
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/TMPRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/TMPRISC.java
new file mode 100644
index 0000000..6079e6c
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/TMPRISC.java
@@ -0,0 +1,41 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv;
+
+import com.compilerprogramming.ezlang.compiler.*;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.ConstantNode;
+import com.compilerprogramming.ezlang.compiler.nodes.MachNode;
+import com.compilerprogramming.ezlang.compiler.nodes.Node;
+import com.compilerprogramming.ezlang.compiler.sontypes.TypeMemPtr;
+
+public class TMPRISC extends ConstantNode implements MachNode, RIPRelSize {
+    TMPRISC(ConstantNode con) { super(con); }
+    @Override public String op() { return "ldp"; }
+    @Override public RegMask regmap(int i) { return null; }
+    @Override public RegMask outregmap() { return riscv.WMASK; }
+    @Override public boolean isClone() { return true; }
+    @Override public TMPRISC copy() { return new TMPRISC(this); }
+    @Override public void encoding( Encoding enc ) {
+        enc.largeConstant(this,((TypeMemPtr)_con)._obj,0,-1);
+        short dst = enc.reg(this);
+        // AUIPC dst,#hi20_constant_pool
+        enc.add4(riscv.u_type(riscv.OP_AUIPC, dst, 0));
+        // addi dst,[dst+#low12_constant_pool]
+        enc.add4(riscv.i_type(riscv.OP_IMM, dst, 0, dst, 0));
+    }
+
+    @Override public byte encSize(int delta) { return 8; }
+
+    // Delta is from opcode start
+    @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) {
+        short dst = enc.reg(this);
+        // AUIPC dst,#hi20_constant_pool
+        enc.patch4(opStart  , riscv.u_type(riscv.OP_AUIPC, dst, delta>>12));
+        // Load dst,[dst+#low12_constant_pool]
+        enc.patch4(opStart+4, riscv.i_type(riscv.OP_IMM, dst, 0, dst, delta & 0xFFF));
+    }
+
+    @Override public void asm(CodeGen code, SB sb) {
+        _con.print(sb.p(code.reg(this)).p(" #"));
+    }
+    @Override public boolean eq(Node n) { return this==n; }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/XorIRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/XorIRISC.java
index b7c03b0..9672291 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/XorIRISC.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/XorIRISC.java
@@ -5,6 +5,7 @@
 
 public class XorIRISC extends ImmRISC {
     public XorIRISC( Node and, int imm) { super(and,imm); }
+    public XorIRISC( Node and, int imm, boolean pop) { super(and,imm,pop); }
     @Override int opcode() {  return riscv.OP_IMM; }
     @Override int func3() {  return 4; }
     @Override public String glabel() { return "^"; }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/riscv.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/riscv.java
index 854112f..6018b9d 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/riscv.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/riscv.java
@@ -188,7 +188,6 @@ public static int i_type(int opcode, int rd, int func3, int rs1, int imm12) {
         return  (imm12 << 20) | (rs1 << 15) | (func3 << 12) | (rd << 7) | opcode;
     }
 
-
     // S-type instructions(store)
     public static int s_type(int opcode, int func3, int rs1, int rs2, int imm12) {
         assert 0 <= rs1 &&  rs1 < 32;
@@ -253,17 +252,18 @@ static public int fsetop(String op) {
         };
     }
 
-    @Override public RegMask callArgMask(TypeFunPtr tfp, int idx, int maxArgSlot ) { return callInMask(tfp,idx,maxArgSlot); }
-    static RegMask callInMask(TypeFunPtr tfp, int idx, int maxArgSlot ) {
+    @Override public RegMask callArgMask( TypeFunPtr tfp, int idx, int maxArgSlot ) { return callInMask(tfp,idx,maxArgSlot); }
+    static RegMask callInMask( TypeFunPtr tfp, int idx, int maxArgSlot ) {
         if( idx==0 ) return RPC_MASK;
         if( idx==1 ) return null;
+        if( idx-2 >= tfp.nargs() ) return null; // Anti-dependence
         // Count floats in signature up to index
         int fcnt=0;
         for( int i=2; i<idx; i++ )
-            if( tfp.arg(i-2) instanceof TypeFloat)
+            if( tfp.arg(i-2) instanceof TypeFloat )
                 fcnt++;
         // Floats up to XMMS in XMM registers
-        if( tfp.arg(idx-2) instanceof TypeFloat) {
+        if( tfp.arg(idx-2) instanceof TypeFloat ) {
             if( fcnt < XMMS.length )
                 return XMMS[fcnt];
         } else {
@@ -279,7 +279,7 @@ static RegMask callInMask(TypeFunPtr tfp, int idx, int maxArgSlot ) {
     @Override public short maxArgSlot( TypeFunPtr tfp ) {
         int icnt=0, fcnt=0;     // Count of ints, floats
         for( int i=0; i<tfp.nargs(); i++ ) {
-            if( tfp.arg(i) instanceof TypeFloat) fcnt++;
+            if( tfp.arg(i) instanceof TypeFloat ) fcnt++;
             else icnt++;
         }
         int nstk = Math.max(icnt-8,0)+Math.max(fcnt-8,0);
@@ -318,7 +318,7 @@ public static boolean imm20Exact(TypeInteger ti) {
         // shift left 32 to clear out the upper 32 bits.
         // shift right SIGNED to sign-extend upper 32 bits; then shift 12 more to clear out lower 12 bits.
         // shift left 12 to re-center the bits.
-        return ti.isConstant() && (((ti.value()<<32)>>>44)<<12) == ti.value();
+        return ti.isConstant() && (((ti.value()<<32)>>44)<<12) == ti.value();
     }
 
     @Override public Node instSelect( Node n ) {
@@ -328,8 +328,8 @@ public static boolean imm20Exact(TypeInteger ti) {
         case AndNode      and -> and(and);
         case BoolNode    bool -> cmp(bool);
         case CallNode    call -> call(call);
-        case CastNode   cast  -> new CastRISC(cast);
-        case CallEndNode cend -> new CallEndRISC(cend);
+        case CastNode   cast  -> new CastMach(cast);
+        case CallEndNode cend -> new CallEndMach(cend);
         case CProjNode      c -> new CProjNode(c);
         case ConstantNode con -> con(con);
         case DivFNode    divf -> new DivFRISC(divf);
@@ -347,7 +347,7 @@ public static boolean imm20Exact(TypeInteger ti) {
         case ParmNode    parm -> new ParmRISC(parm);
         case PhiNode      phi -> new PhiNode(phi);
         case ProjNode     prj -> prj(prj);
-        case ReadOnlyNode read-> new ReadOnlyNode(read);
+        case ReadOnlyNode read-> new ReadOnlyMach(read);
         case ReturnNode   ret -> new RetRISC(ret, ret.fun());
         case SarNode      sar -> sra(sar);
         case ShlNode      shl -> sll(shl);
@@ -420,7 +420,7 @@ private Node cmp(BoolNode bool) {
             ? new SetIRISC(bool, (int)ti.value(),false)
             : new  SetRISC(bool, false);
         // x <= y - flip and negate; !(y < x); `slt tmp=y,x; xori dst=tmp,#1`
-        case "<=" -> new XorIRISC(new SetRISC(bool.swap12(), false),1);
+        case "<=" -> new XorIRISC(new SetRISC(bool.swap12(), false),1,false);
         // x == y - sub and vs0 == `sub tmp=x-y; sltu dst=tmp,#1`
         case "==" -> new SetIRISC(new SubRISC(bool),1,true);
         default -> throw Utils.TODO();
@@ -443,15 +443,15 @@ private Node con( ConstantNode con ) {
             }
             // Need more complex sequence for larger constants... or a load
             // from a constant pool, which does not need an extra register
-            throw Utils.TODO();
+            yield new Int8RISC(con);
         }
         // Load from constant pool
-        case TypeFloat tf  -> new FltRISC(con);
-        case TypeFunPtr tfp -> new TFPRISC(con);
-        case TypeMemPtr tmp -> throw Utils.TODO();
-        case TypeNil tn  -> throw Utils.TODO();
+        case TypeFloat   tf  -> new FltRISC(con);
+        case TypeFunPtr  tfp -> new TFPRISC(con);
+        case TypeMemPtr  tmp -> new TMPRISC(con);
+        case TypeNil     tn  -> throw Utils.TODO();
         // TOP, BOTTOM, XCtrl, Ctrl, etc.  Never any executable code.
-        case Type t -> t== Type.NIL ? new IntRISC(con) : new ConstantNode(con);
+        case Type t -> t==Type.NIL ? new IntRISC(con) : new ConstantNode(con);
         };
     }
 
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/AddIX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/AddIX86.java
index 7659e35..b3f3f9f 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/AddIX86.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/AddIX86.java
@@ -1,5 +1,6 @@
 package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2;
 
+import com.compilerprogramming.ezlang.compiler.codegen.Encoding;
 import com.compilerprogramming.ezlang.compiler.nodes.Node;
 
 public class AddIX86 extends ImmX86 {
@@ -10,4 +11,12 @@ public class AddIX86 extends ImmX86 {
     @Override public String glabel() { return "+"; }
     @Override int opcode() { return 0x81; }
     @Override int mod() { return 0; }
+    @Override public final void encoding( Encoding enc ) {
+        if( _imm== 1 || _imm== -1 ) {
+            short dst = enc.reg(this); // Also src1
+            enc.add1( x86_64_v2.rex(0,dst,0));
+            enc.add1(0xFF);
+            enc.add1( x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, _imm==1 ? 0 : 1, dst) );
+        } else super.encoding(enc);
+    }
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CallEndX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CallEndX86.java
deleted file mode 100644
index 1dfff37..0000000
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CallEndX86.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2;
-
-import com.compilerprogramming.ezlang.compiler.nodes.CallEndNode;
-import com.compilerprogramming.ezlang.compiler.nodes.MachNode;
-
-public class CallEndX86 extends CallEndNode implements MachNode {
-    CallEndX86( CallEndNode cend ) { super(cend); }
-}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CallX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CallX86.java
index ead9f8b..b43ea38 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CallX86.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CallX86.java
@@ -3,6 +3,8 @@
 import com.compilerprogramming.ezlang.compiler.SB;
 import com.compilerprogramming.ezlang.compiler.codegen.*;
 import com.compilerprogramming.ezlang.compiler.nodes.CallNode;
+import com.compilerprogramming.ezlang.compiler.nodes.ExternNode;
+import com.compilerprogramming.ezlang.compiler.nodes.FunNode;
 import com.compilerprogramming.ezlang.compiler.nodes.MachNode;
 import com.compilerprogramming.ezlang.compiler.sontypes.TypeFunPtr;
 
@@ -14,7 +16,8 @@ public class CallX86 extends CallNode implements MachNode, RIPRelSize {
         _inputs.pop(); // Pop constant target
         assert tfp.isConstant();
         _tfp = tfp;
-        _name = CodeGen.CODE.link(tfp)._name;
+        FunNode fun = CodeGen.CODE.link(tfp);
+        _name = fun==null ? ((ExternNode)call.fptr())._extern : fun._name; // Can be null for extern calls
     }
     @Override public String op() { return "call"; }
     @Override public String label() { return op(); }
@@ -25,7 +28,10 @@ public class CallX86 extends CallNode implements MachNode, RIPRelSize {
     @Override public int nargs() { return nIns()-2; } // Minus control, memory, fptr
 
     @Override public void encoding( Encoding enc ) {
-        enc.relo(this).add1(0xe8).add4(0);
+        FunNode fun = CodeGen.CODE.link(_tfp);
+        if( fun==null ) enc.external(this,_name);
+        else enc.relo(this);
+        enc.add1(0xe8).add4(0);
     }
 
     // Delta is from opcode start, but X86 measures from the end of the 5-byte encoding
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CastX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CastX86.java
deleted file mode 100644
index f494e79..0000000
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CastX86.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2;
-
-import com.compilerprogramming.ezlang.compiler.SB;
-import com.compilerprogramming.ezlang.compiler.codegen.*;
-import com.compilerprogramming.ezlang.compiler.nodes.CastNode;
-import com.compilerprogramming.ezlang.compiler.nodes.MachNode;
-
-public class CastX86 extends CastNode implements MachNode {
-    CastX86( CastNode cast ) { super(cast); }
-    @Override public String op() { return label(); }
-    @Override public RegMask regmap(int i) { assert i==1; return RegMask.FULL; }
-    @Override public RegMask outregmap() { return RegMask.FULL; }
-    @Override public int twoAddress( ) { return 1; }
-    @Override public void encoding( Encoding enc ) { }
-    @Override public void asm(CodeGen code, SB sb) { _t.print(sb.p(code.reg(in(1))).p(" isa ")); }
-}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CmpMemX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CmpMemX86.java
index 35c0efe..308e0f5 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CmpMemX86.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CmpMemX86.java
@@ -11,7 +11,7 @@ public class CmpMemX86 extends MemOpX86 {
     CmpMemX86( BoolNode bool, LoadNode ld, Node base, Node idx, int off, int scale, int imm, Node val, boolean swap ) {
         super(bool,ld, base, idx, off, scale, imm, val );
         _swap = swap;
-        assert !_swap || val() == null || bool.op()=="==" || bool.op()=="!="; // Cannot swap with immediate
+        assert !_swap || val() != null || bool.op()=="==" || bool.op()=="!="; // Cannot swap with immediate
     }
     @Override public String op() { return ((val()==null && _imm==0) ? "test" : "cmp") + _sz; }
     @Override public RegMask outregmap() { return x86_64_v2.FLAGS_MASK; }
@@ -48,7 +48,7 @@ public class CmpMemX86 extends MemOpX86 {
     static void encVal(Encoding enc, Type decl, short ptr, short idx, short src, int off, int scale, boolean _swap) {
         if(decl instanceof TypeFloat) {
             src -= (short)x86_64_v2.XMM_OFFSET;
-            enc.add1(decl== TypeFloat.F32 ? 0xF3 : 0xF2).add1(0x0F).add1(0xC2);
+            enc.add1(decl==TypeFloat.F32 ? 0xF3 : 0xF2).add1(0x0F).add1(0xC2);
         } else {
             int log = decl.log_size();
             x86_64_v2.rexF(src, ptr, idx, log == 3, enc);
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CmpX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CmpX86.java
index b238ed2..563cb9d 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CmpX86.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CmpX86.java
@@ -9,6 +9,12 @@ public class CmpX86 extends MachConcreteNode implements MachNode {
     @Override public String op() { return "cmp"; }
     @Override public RegMask regmap(int i) { return x86_64_v2.RMASK; }
     @Override public RegMask outregmap() { return x86_64_v2.FLAGS_MASK; }
+    // This one is marginal: cloning a Cmp[reg,reg] means stretching the
+    // lifetimes of 2 normal registers in exchange for shortening a flags
+    // register lifetime.  Spilling flags is (probably) relatively expensive
+    // compared to some normal registers - and those normal registers might not
+    // be spilling!
+    @Override public boolean isClone() { return true; }
 
     @Override public void encoding( Encoding enc ) {
         short dst = enc.reg(in(1));
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/DivX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/DivX86.java
index 301cb2c..83a5dda 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/DivX86.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/DivX86.java
@@ -8,7 +8,7 @@ public class DivX86 extends MachConcreteNode implements MachNode {
     DivX86( Node div ) { super(div); }
     @Override public String op() { return "div"; }
     @Override public RegMask regmap(int i) {
-        return (i==1) ? x86_64_v2.RAX_MASK : x86_64_v2.RMASK;
+        return (i==1) ? x86_64_v2.RAX_MASK : x86_64_v2.DIV_MASK;
     }
     @Override public RegMask outregmap() { return x86_64_v2.RAX_MASK; }
     @Override public RegMask killmap() { return x86_64_v2.RDX_MASK; }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/ImmX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/ImmX86.java
index 7b45ec3..4b33b82 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/ImmX86.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/ImmX86.java
@@ -14,11 +14,12 @@ public abstract class ImmX86 extends MachConcreteNode implements MachNode {
     @Override public RegMask regmap(int i) { return x86_64_v2.RMASK; }
     @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
     @Override public int twoAddress() { return 1; }
+    @Override public RegMask killmap() { return x86_64_v2.FLAGS_MASK; }
 
     abstract int opcode();
     abstract int mod();
 
-    @Override public final void encoding( Encoding enc ) {
+    @Override public void encoding( Encoding enc ) {
         short dst = enc.reg(this); // Also src1
         enc.add1(x86_64_v2.rex(0, dst, 0));
         // opcode; 0x81 or 0x83; 0x69 or 0x6B
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/IntX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/IntX86.java
index cc68d2a..243d0e1 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/IntX86.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/IntX86.java
@@ -24,7 +24,7 @@ public class IntX86 extends ConstantNode implements MachNode {
     @Override public void encoding( Encoding enc ) {
         short dst = enc.reg(this);
         // Short form for zero
-        if( _con== Type.NIL || _con== TypeInteger.ZERO ) {
+        if( _con==Type.NIL || _con==TypeInteger.ZERO ) {
             // XOR dst,dst.  Can skip REX is dst is low 8, makes this a 32b
             // xor, which will also zero the high bits.
             if( dst >= 8 ) enc.add1(x86_64_v2.rex(dst, dst, 0));
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/LoadX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/LoadX86.java
index c7f6624..6b308c2 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/LoadX86.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/LoadX86.java
@@ -25,7 +25,7 @@ public class LoadX86 extends MemOpX86 {
         enc(enc, _declaredType, dst, ptr, idx, _off, _scale);
     }
 
-    static void enc(Encoding enc, Type decl, short dst, short ptr, short idx, int off, int scale ) {
+    static void enc( Encoding enc, Type decl, short dst, short ptr, short idx, int off, int scale ) {
         if( decl == TypeFloat.F32) enc.add1(0xF3);
         if( decl == TypeFloat.F64) enc.add1(0xF2);
 
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/MemAddX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/MemAddX86.java
index a0edf38..205ad36 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/MemAddX86.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/MemAddX86.java
@@ -13,6 +13,7 @@ public class MemAddX86 extends MemOpX86 {
     }
     // Register mask allowed as a result.  0 for no register.
     @Override public RegMask outregmap() { return null; }
+    @Override public RegMask killmap() { return x86_64_v2.FLAGS_MASK; }
     @Override public void encoding( Encoding enc ) {
         // add something to memory
         // REX.W + 01 /r | REX.W + 81 /0 id
@@ -23,11 +24,11 @@ public class MemAddX86 extends MemOpX86 {
 
         enc.add1(x86_64_v2.rex(src, ptr, idx));
         // opcode
-        enc.add1( src == -1 ? 0x81: 0x01);
+        enc.add1( src == -1 ? (_imm==1 ? 0xFF : 0x81): 0x01);
 
         // includes modrm
         x86_64_v2.indirectAdr(_scale, idx, ptr, _off, src == -1 ? 0 : src, enc);
-        if( src == -1 ) enc.add4(_imm);
+        if( src == -1 && _imm!=1 ) enc.add4(_imm);
     }
     // General form: "add  [base + idx<<2 + 12] += src"
     @Override public void asm(CodeGen code, SB sb) {
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/MulIX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/MulIX86.java
index c53599d..8f0a90c 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/MulIX86.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/MulIX86.java
@@ -1,11 +1,38 @@
 package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2;
 
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.codegen.CodeGen;
+import com.compilerprogramming.ezlang.compiler.codegen.Encoding;
+import com.compilerprogramming.ezlang.compiler.codegen.RegMask;
+import com.compilerprogramming.ezlang.compiler.nodes.MachConcreteNode;
+import com.compilerprogramming.ezlang.compiler.nodes.MachNode;
 import com.compilerprogramming.ezlang.compiler.nodes.Node;
 
-public class MulIX86 extends ImmX86 {
-    MulIX86( Node add, int imm ) { super(add,imm); }
+public class MulIX86 extends MachConcreteNode implements MachNode {
+    final int _imm;
+    MulIX86( Node add, int imm ) {
+        super(add);
+        _inputs.pop();          // Pop the constant input
+        _imm = imm;             // Record the constant
+    }
     @Override public String op() { return "muli"; }
     @Override public String glabel() { return "*"; }
-    @Override int opcode() { return 0x69; }
-    @Override int mod() { return 0; }
+    @Override public RegMask regmap(int i) { return x86_64_v2.RMASK; }
+    @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
+    // Rare 3-address form
+    @Override public final void encoding( Encoding enc ) {
+        short dst = enc.reg(this ); // Also src1
+        short src = enc.reg(in(1));
+        enc.add1(x86_64_v2.rex(src, dst, 0));
+        // opcode; 0x69 or 0x6B
+        enc.add1( 0x69 + (x86_64_v2.imm8(_imm) ? 2 : 0) );
+        enc.add1( x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, src) );
+        // immediate(4 bytes) 32 bits or (1 byte)8 bits
+        if( x86_64_v2.imm8(_imm) ) enc.add1(_imm);
+        else                       enc.add4(_imm);
+    }
+    // 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(" * #").p(_imm);
+    }
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/RegX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/RegX86.java
index 8a5e305..6a3838b 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/RegX86.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/RegX86.java
@@ -8,6 +8,7 @@ public abstract class RegX86 extends MachConcreteNode {
     RegX86( Node add ) { super(add); }
     @Override public RegMask regmap(int i) { return x86_64_v2.RMASK; }
     @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
+    @Override public RegMask killmap() { return x86_64_v2.FLAGS_MASK; }
     @Override public int twoAddress() { return 1; }
     abstract int opcode();
     @Override public void encoding( Encoding enc ) {
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/SetX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/SetX86.java
index ae9ff53..62126e8 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/SetX86.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/SetX86.java
@@ -27,7 +27,7 @@ public class SetX86 extends MachConcreteNode implements MachNode {
         short dst = enc.reg(this );
 
         // Optional rex, for dst
-        if( dst >= 8 ) enc.add1(x86_64_v2.rex(0, dst, 0));
+        if( dst >= 4 ) enc.add1(x86_64_v2.rex(0, dst, 0, false));
         enc.add1(0x0F);         // opcode
         enc.add1(switch (_bop) {
             case "==" -> 0x94;  // SETE
@@ -39,7 +39,7 @@ public class SetX86 extends MachConcreteNode implements MachNode {
         enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, 0, dst));
 
         // low 8 bites are set, now zero extend for next instruction
-        if( dst >= 8 ) enc.add1(x86_64_v2.rex(0, dst, 0));
+        if( dst >= 4 ) enc.add1(x86_64_v2.rex(dst, dst, 0, false));
         enc.add1(0x0F); // opcode
         enc.add1(0xB6); // opcode
         enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, dst));
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/StoreX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/StoreX86.java
index aa641c0..1fb8cd9 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/StoreX86.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/StoreX86.java
@@ -51,7 +51,7 @@ public class StoreX86 extends MemOpX86 {
     }
 
     // Non-immediate encoding
-    static void encVal(Encoding enc, Type decl, short ptr, short idx, short src, int off, int scale ) {
+    static void encVal( Encoding enc, Type decl, short ptr, short idx, short src, int off, int scale ) {
         int log = decl.log_size();
         // Float reg being stored
         if( src >= x86_64_v2.XMM_OFFSET ) {
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/TFPX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/TFPX86.java
index 19034fb..46e9e9c 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/TFPX86.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/TFPX86.java
@@ -23,7 +23,7 @@ public class TFPX86 extends ConstantNode implements MachNode, RIPRelSize {
     @Override public void encoding( Encoding enc ) {
         short dst = enc.reg(this);
         // Short form for zero
-        if( _con== Type.NIL ) {
+        if( _con==Type.NIL ) {
             // XOR dst,dst.  Can skip REX is dst is low 8, makes this a 32b
             // xor, which will also zero the high bits.
             if( dst >= 8 ) enc.add1(x86_64_v2.rex(dst, dst, 0));
@@ -61,4 +61,5 @@ public class TFPX86 extends ConstantNode implements MachNode, RIPRelSize {
         else
             _con.print(sb.p(reg).p(" = #"));
     }
+    @Override public boolean eq(Node n) { return this==n; }
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/TMPX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/TMPX86.java
new file mode 100644
index 0000000..746379b
--- /dev/null
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/TMPX86.java
@@ -0,0 +1,35 @@
+package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2;
+
+import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.codegen.*;
+import com.compilerprogramming.ezlang.compiler.nodes.*;
+import com.compilerprogramming.ezlang.compiler.sontypes.TypeMemPtr;
+
+public class TMPX86 extends ConstantNode implements MachNode, RIPRelSize{
+    TMPX86(ConstantNode con ) { super(con); }
+    @Override public String op() { return "ldp"; }
+    @Override public RegMask regmap(int i) { return null; }
+    @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
+    @Override public boolean isClone() { return true; }
+    @Override public Node copy() { return new TMPX86(this); }
+
+    @Override public void encoding( Encoding enc ) {
+        short dst = enc.reg(this);
+        x86_64_v2.rexF(dst, 0, 0, true, enc);
+        enc.add1(0x8D).add1(x86_64_v2.modrm(x86_64_v2.MOD.INDIRECT, dst, 0b101)).add4(0);
+        enc.largeConstant(this,((TypeMemPtr)_con)._obj,7-4,2/*ELF encoding PC32*/);
+    }
+
+    // Delta is from opcode start
+    @Override public byte encSize(int delta) { return (byte)7; }
+
+    // Delta is from opcode start
+    @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) {
+        enc.patch4(opStart+opLen-4,delta-opLen);
+    }
+
+    @Override public void asm(CodeGen code, SB sb) {
+        _con.print(sb.p(code.reg(this)).p(" = #"));
+    }
+    @Override public boolean eq(Node n) { return this==n; }
+}
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/x86_64_v2.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/x86_64_v2.java
index ad134b5..1133035 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/x86_64_v2.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/x86_64_v2.java
@@ -43,6 +43,8 @@ public x86_64_v2( CodeGen code ) {}
     static RegMask XMASK = new RegMask(FP_BITS);
     static RegMask FLAGS_MASK = new RegMask(FLAGS);
     static RegMask RPC_MASK = new RegMask(RPC);
+    // Divide mask: exclude RDX:RAX
+    static RegMask DIV_MASK = new RegMask(RD_BITS & ~(1L<<RAX) & ~(1L<<RDX));
 
     static final long SPILLS = -(1L << MAX_REG);
     static final RegMask SPLIT_MASK = new RegMask(WR_BITS | FP_BITS /* | (1L<<FLAGS)*/ | SPILLS, -1L );
@@ -61,7 +63,7 @@ public x86_64_v2( CodeGen code ) {}
     public static RegMask XMM0_MASK = new RegMask(XMM0);
 
     // Encoding
-    public static int REX = 0x40;
+    public static int REX    = 0x40;
     public static int REX_W  = 0x48;
     public static int REX_WR = 0x4C;
     public static int REX_WRB= 0x4D;
@@ -185,10 +187,11 @@ public static void indirectAdr( int scale, short index, short base, int offset,
 
     // Map from function signature and argument index to register.
     // Used to set input registers to CallNodes, and ParmNode outputs.
-    @Override public RegMask callArgMask(TypeFunPtr tfp, int idx, int maxArgSlot ) { return callInMask(tfp,idx,maxArgSlot); }
-    static RegMask callInMask(TypeFunPtr tfp, int idx, int maxArgSlot ) {
+    @Override public RegMask callArgMask( TypeFunPtr tfp, int idx, int maxArgSlot ) { return callInMask(tfp,idx,maxArgSlot); }
+    static RegMask callInMask( TypeFunPtr tfp, int idx, int maxArgSlot ) {
         if( idx==0 ) return RPC_MASK;
         if( idx==1 ) return null;
+        if( idx-2 >= tfp.nargs() ) return null; // Anti-dependence
         return switch( CodeGen.CODE._callingConv ) {
         case "SystemV" -> callSys5 (tfp,idx,maxArgSlot);
         case "Win64"   -> callWin64(tfp,idx,maxArgSlot);
@@ -276,7 +279,7 @@ static RegMask callSys5(TypeFunPtr tfp, int idx, int maxArgSlot ) {
         // First 8 floats passed in registers: xmm0-xmm7
         int icnt=0, fcnt=0;     // Count of ints, floats
         for( int i=2; i<idx; i++ ) {
-            if( tfp.arg(i-2) instanceof TypeFloat) fcnt++;
+            if( tfp.arg(i-2) instanceof TypeFloat ) fcnt++;
             else icnt++;
         }
         int nstk = Math.max(icnt-6,0)+Math.max(fcnt-8,0);
@@ -287,7 +290,7 @@ static RegMask callSys5(TypeFunPtr tfp, int idx, int maxArgSlot ) {
     static short maxArgSlotSys5(TypeFunPtr tfp) {
         int icnt=0, fcnt=0;     // Count of ints, floats
         for( int i=0; i<tfp.nargs(); i++ ) {
-            if( tfp.arg(i) instanceof TypeFloat) fcnt++;
+            if( tfp.arg(i) instanceof TypeFloat ) fcnt++;
             else icnt++;
         }
         int nstk = Math.max(icnt-6,0)+Math.max(fcnt-8,0);
@@ -343,9 +346,9 @@ public Node instSelect(Node n) {
         case AndNode      and -> and(and);
         case BoolNode    bool -> cmp(bool);
         case CProjNode      c -> new CProjNode(c);
-        case CallEndNode cend -> new CallEndX86(cend);
+        case CallEndNode cend -> new CallEndMach(cend);
         case CallNode    call -> call(call);
-        case CastNode    cast -> new CastX86(cast);
+        case CastNode    cast -> new CastMach(cast);
         case ConstantNode con -> con(con);
         case DivFNode    divf -> new DivFX86(divf);
         case DivNode      div -> new DivX86(div);
@@ -362,7 +365,7 @@ public Node instSelect(Node n) {
         case ParmNode    parm -> new ParmX86(parm);
         case PhiNode      phi -> new PhiNode(phi);
         case ProjNode     prj -> prj(prj);
-        case ReadOnlyNode read-> new ReadOnlyNode(read);
+        case ReadOnlyNode read-> new ReadOnlyMach(read);
         case ReturnNode   ret -> new RetX86(ret, ret.fun());
         case SarNode      sar -> sar(sar);
         case ShlNode      shl -> shl(shl);
@@ -499,15 +502,16 @@ private Node _cmp(BoolNode bool) {
     }
 
     private Node con( ConstantNode con ) {
-        if(!con._con.isConstant()) return new ConstantNode(con); // Default unknown caller inputs
+        if( !con._con.isConstant() )
+            return new ConstantNode(con); // Default unknown caller inputs
         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 -> throw Utils.TODO();
-            case TypeNil tn -> throw Utils.TODO();
-            // TOP, BOTTOM, XCtrl, Ctrl, etc.  Never any executable code.
-            case Type t -> t == Type.NIL ? new IntX86(con) : new ConstantNode(con);
+        case TypeInteger ti -> new IntX86(con);
+        case TypeFloat   tf -> new FltX86(con);
+        case TypeFunPtr tfp -> new TFPX86(con);
+        case TypeMemPtr tmp -> new TMPX86(con);
+        case TypeNil tn -> throw Utils.TODO();
+        // TOP, BOTTOM, XCtrl, Ctrl, etc.  Never any executable code.
+        case Type t -> t == Type.NIL ? new IntX86(con) : new ConstantNode(con);
         };
     }
 
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/print/ASMPrinter.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/print/ASMPrinter.java
index 20dd43d..0c5c680 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/print/ASMPrinter.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/print/ASMPrinter.java
@@ -1,5 +1,6 @@
 package com.compilerprogramming.ezlang.compiler.print;
 
+import com.compilerprogramming.ezlang.compiler.Ary;
 import com.compilerprogramming.ezlang.compiler.codegen.CodeGen;
 import com.compilerprogramming.ezlang.compiler.SB;
 import com.compilerprogramming.ezlang.compiler.codegen.Encoding;
@@ -20,54 +21,71 @@ public static SB print(SB sb, CodeGen code) {
             if( code._cfg.at(i) instanceof FunNode fun )
                 iadr = print(iadr,sb,code,fun,i);
 
-        // Skip padding
-        while( ((iadr+7) & -8) > iadr )
-            iadr++;
-
         // constant pool
+        iadr = (iadr+15)&-16; // pad to 16
         Encoding enc = code._encoding;
-        if(  enc!=null && !enc._bigCons.isEmpty() ) {
-            iadr = (iadr+15)&-16; // pad to 16
+        if(  enc!=null && !enc._bigCons.isEmpty() && iadr < enc._bits.size() ) {
+            // radix sort the big constants by alignment
+            Ary<Encoding.Relo>[] raligns = new Ary[5];
+            for( Node op : enc._bigCons.keySet() ) {
+                Encoding.Relo relo = enc._bigCons.get(op);
+                int align = relo._t.alignment();
+                Ary<Encoding.Relo> relos = raligns[align]==null ? (raligns[align]=new Ary<>( Encoding.Relo.class)) : raligns[align];
+                relos.add(relo);
+            }
+
             HashSet<Type> targets = new HashSet<>();
             sb.p("--- Constant Pool ------").nl();
-            // By log size
-            for( int log = 3; log >= 0; log-- ) {
-                for( Node op : enc._bigCons.keySet() ) {
-                    Encoding.Relo relo = enc._bigCons.get(op);
+
+            // By alignment
+            for( int align = 4; align >= 0; align-- ) {
+                Ary<Encoding.Relo> relos = raligns[align];
+                if( relos == null ) continue;
+                for( Encoding.Relo relo : relos ) {
                     if( targets.contains(relo._t) ) continue;
                     targets.add(relo._t);
-                    if( relo._t.log_size()==log ) {
-                        sb.hex2(iadr).p("  ");
-                        if( relo._t instanceof TypeTuple tt ) {
-                            for( Type tx : tt._types ) {
-                                switch( log ) {
-                                case 0: sb.hex1(enc.read1(iadr)); break;
-                                case 1: sb.hex2(enc.read2(iadr)); break;
-                                case 2: sb.hex4(enc.read4(iadr)); break;
-                                case 3: sb.hex8(enc.read8(iadr)); break;
-                                }
-                                iadr += (1<<log);
-                                sb.p(" ");
-                            }
-                        } else {
-                            switch( log ) {
-                            case 0: sb.hex1(enc.read1(iadr)).fix(9-1,""); break;
-                            case 1: sb.hex2(enc.read2(iadr)).fix(9-2,""); break;
-                            case 2: sb.hex4(enc.read4(iadr)).fix(9-4,""); break;
-                            case 3: sb.hex8(enc.read8(iadr)).p(" "); break;
-                            }
-                            iadr += (1<<log);
+                    sb.hex2(iadr).p("  ");
+                    switch( relo._t ) {
+                    case TypeTuple  tt -> {
+                        for( Type tx : tt._types ) {
+                            pN(enc,sb,iadr,align).p(" ");
+                            iadr += (1<<align);
                         }
-                        relo._t.print(sb).nl();
                     }
+
+                    case TypeStruct ts -> {
+                        int sz = ts.size();
+                        int log = 1<<align;
+                        sz = (sz + (log -1)) & -log; // Round up final padding
+                        for( int i=0; i<sz; i++ )
+                            sb.hex1(enc.read1(iadr++));
+                    }
+
+                    // Simple primitive (e.g. larger int, float)
+                    default -> {
+                        pN(enc,sb,iadr,align).fix(9-(1<<align),"");
+                        iadr += (1<<align);
+                    }
+                    }
+                    relo._t.print(sb).nl();
                 }
             }
         }
 
+        return sb;
+    }
 
+    private static SB pN( Encoding enc, SB sb, int iadr, int log ) {
+        switch( log ) {
+        case 0: sb.hex1(enc.read1(iadr)); break;
+        case 1: sb.hex2(enc.read2(iadr)); break;
+        case 2: sb.hex4(enc.read4(iadr)); break;
+        case 3: sb.hex8(enc.read8(iadr)); break;
+        }
         return sb;
     }
 
+
     private static int print(int iadr, SB sb, CodeGen code, FunNode fun, int cfgidx) {
         FunNode old=null;
         if( code._encoding!=null ) {
@@ -109,7 +127,7 @@ static int doBlock(int iadr, SB sb, CodeGen code, FunNode fun, int cfgidx) {
         boolean once=false;
         for( Node n : bb.outs() ) {
             if( !(n instanceof PhiNode phi) ) continue;
-            if( phi._type instanceof TypeMem || phi._type instanceof TypeRPC) continue; // Nothing for the hidden ones
+            if( phi._type instanceof TypeMem || phi._type instanceof TypeRPC ) continue; // Nothing for the hidden ones
             // Post-RegAlloc phi prints all on one line
             if( postAlloc ) {
                 if( !once ) { once=true; sb.fix(4," ").p(" ").fix(encWidth,"").p("  "); }
@@ -164,7 +182,7 @@ static int doInst( int iadr, SB sb, CodeGen code, FunNode fun, int cfgidx, Node
         // ProjNodes following a multi (e.g. Call or New results),
         // get indent slightly and just print their index & node#
         if( n instanceof ProjNode proj ) {
-            if( proj._type instanceof TypeMem) return iadr; // Nothing for the hidden ones
+            if( proj._type instanceof TypeMem ) return iadr; // Nothing for the hidden ones
             sb.fix(4," ").p(" ").fix(encWidth,"").p("    ").fix(opWidth,proj._label==null ? "---" : proj._label).p(" ").p(code.reg(n,fun)).nl();
             return iadr;
         }
@@ -175,7 +193,7 @@ static int doInst( int iadr, SB sb, CodeGen code, FunNode fun, int cfgidx, Node
 
         // Encoding
         int fatEncoding = 0;
-        if( code._encoding != null ) {
+        if( code._encoding != null && code._encoding._opLen!=null ) {
             int size = code._encoding._opLen[n._nid];
             if( code._asmLittle )
                 for( int i=0; i<Math.min(size,dopz); i++ )
@@ -224,7 +242,7 @@ static int doInst( int iadr, SB sb, CodeGen code, FunNode fun, int cfgidx, Node
         sb.nl();
 
         // Printing more op bits than fit
-        if( isMultiOp != null && code._encoding != null ) {
+        if( isMultiOp != null && code._encoding != null && code._encoding._opLen!=null ) {
             // Multiple ops, template style, no RA, no scheduling.  Print out
             // one-line-per-newline, with encoding bits up front.
             int size = code._encoding._opLen[n._nid];
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/Field.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/Field.java
index 70cee5e..b1d7168 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/Field.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/Field.java
@@ -28,7 +28,7 @@ public Field(String fname, Type type, int alias, boolean xfinal ) {
         _final = xfinal;
     }
     // Make with existing alias
-    public static Field make(String fname, Type type, int alias, boolean xfinal ) {
+    public static Field make( String fname, Type type, int alias, boolean xfinal ) {
         return new Field(fname,type,alias,xfinal).intern();
     }
     public Field makeFrom( Type type ) {
@@ -37,8 +37,8 @@ public Field makeFrom( Type type ) {
     @Override public Field makeRO() { return _final ? this : make(_fname,_type.makeRO(),_alias,true);  }
     @Override public boolean isFinal() { return _final && _type.isFinal(); }
 
-    public static final Field TEST = make("test", Type.NIL,-2,false);
-    public static final Field TEST2= make("test", Type.NIL,-2,true);
+    public static final Field TEST = make("test",Type.NIL,-2,false);
+    public static final Field TEST2= make("test",Type.NIL,-2,true);
     public static void gather(ArrayList<Type> ts) { ts.add(TEST); ts.add(TEST2); }
 
     @Override Field xmeet( Type that ) {
@@ -51,8 +51,10 @@ public Field makeFrom( Type type ) {
     @Override
     public Field dual() { return make(_fname,_type.dual(),_alias,!_final); }
 
-    @Override public Field glb() {
-        Type glb = _type.glb();
+    @Override public boolean isConstant() { return _type.isConstant(); }
+
+    @Override public Field glb(boolean mem) {
+        Type glb = _type.glb(mem);
         return (glb==_type && _final) ? this : make(_fname,glb,_alias,true);
     }
 
@@ -70,5 +72,5 @@ public SB print( SB sb ) {
         return _type.print(sb.p(_final?"":"!").p(_fname).p(":").p(_alias).p(" : "));
     }
 
-    @Override public String str() { return _fname; }
+    @Override public String str() { return (_final?"":"!")+_fname; }
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/Type.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/Type.java
index 5c3e392..af87c9a 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/Type.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/Type.java
@@ -160,6 +160,7 @@ Type xmeet(Type t) {
         // RHS TypeNil vs NIL/XNIL
         if( _type==  TNIL ) return t instanceof TypeNil ptr ? ptr.meet0() : (t._type==TXNIL ? TypePtr.PTR : BOTTOM);
         if( _type== TXNIL ) return t instanceof TypeNil ptr ? ptr.meetX() : (t._type== TNIL ? TypePtr.PTR : BOTTOM);
+
         // 'this' is only {TCTRL,TXCTRL}
         // Other non-simple RHS things bottom out
         if( !t.is_simple() ) return BOTTOM;
@@ -200,8 +201,9 @@ public final Type join(Type t) {
     // Make a zero version of this type, 0 for integers and null for pointers.
     public Type makeZero() { return Type.NIL; }
 
-    /** Compute greatest lower bound in the lattice */
-    public Type glb() { return Type.BOTTOM; }
+    /** Compute greatest lower bound in the lattice.  If values are in memory,
+     *  ints and floats cannot widen. */
+    public Type glb(boolean mem) { return Type.BOTTOM; }
 
     // Is forward-reference
     public boolean isFRef() { return false; }
@@ -218,7 +220,9 @@ public final Type join(Type t) {
     // Sizes are expected to be between 1 and 64 bits.
     // Size 0 means this either takes no space (such as a known-zero field)
     // or isn't a scalar to be stored in memory.
-    public int log_size() { return 3; }
+    public int log_size () { return 3; } // log-size of a type; log_size for a struct is usually undefined
+    public int size() { return 1<<log_size(); }
+    public int alignment() { return log_size(); } // alignment; for structs, max align of Fields
 
     // ----------------------------------------------------------
     // Useful in the debugger, which calls toString everywhere.
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/TypeFloat.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/TypeFloat.java
index 60d8a32..89d68a6 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/TypeFloat.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/TypeFloat.java
@@ -80,7 +80,7 @@ public Type dual() {
     }
 
     @Override public Type makeZero() { return FZERO; }
-    @Override public Type glb() { return F64; }
+    @Override public Type glb(boolean mem) { return mem ? (isHigh() ? dual() : this)  : F64; }
 
     @Override
     int hash() { return (int)(Double.hashCode(_con) ^ _sz ^ (1<<17)); }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/TypeFunPtr.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/TypeFunPtr.java
index 1069de5..65b1272 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/TypeFunPtr.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/TypeFunPtr.java
@@ -33,18 +33,18 @@ public TypeFunPtr(byte nil, TypeTuple sig, Type ret, long fidxs) {
         _fidxs = fidxs;
     }
 
-    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); }
-    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 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); }
+    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 BOT   = make((byte)3,TypeTuple.BOT,Type.BOTTOM,-1);
 //    public static SONTypeFunPtr TEST  = make((byte)2, SONTypeTuple.TEST, SONTypeInteger.BOT,1);
 //    public static SONTypeFunPtr TEST0 = make((byte)3, SONTypeTuple.TEST, SONTypeInteger.BOT,3);
-    public static TypeFunPtr MAIN  = make((byte)3, TypeTuple.MAIN, TypeInteger.BOT,-1);
-    public static TypeFunPtr CALLOC= make((byte)3, TypeTuple.CALLOC, TypeMemPtr.BOT,-1);
+    public static TypeFunPtr MAIN  = make((byte)3,TypeTuple.MAIN,TypeInteger.BOT,-1);
+    public static TypeFunPtr CALLOC= make((byte)3,TypeTuple.CALLOC,TypeMemPtr.BOT,-1);
+
     public static void gather(ArrayList<Type> ts) { /* ts.add(TEST); ts.add(TEST0); */ ts.add(BOT); ts.add(MAIN); }
 
     @Override
@@ -59,7 +59,7 @@ Type xmeet(Type t) {
     // RHS is NIL; do not deep-dual when crossing the centerline
     @Override public Type meet0() { return _nil==3 ? this : make((byte)3,_sig,_ret,_fidxs); }
 
-    @Override public TypeFunPtr glb() { return make((byte)3,_sig,_ret,-1L); }
+    @Override public TypeFunPtr glb(boolean mem) { return make((byte)3,_sig,_ret,-1L); }
 
     @Override public boolean isHigh    () { return _nil <= 1 || (_nil==2 && _fidxs==0); }
     @Override public boolean isConstant() { return (_nil==2 && Long.bitCount(_fidxs)==1) || (_nil==3 && _fidxs==0); }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/TypeInteger.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/TypeInteger.java
index 3026653..6054cae 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/TypeInteger.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/TypeInteger.java
@@ -74,11 +74,17 @@ private static String _str(long lo, long hi) {
     @Override public boolean isConstant() { return _min == _max; }
 
     @Override public int log_size() {
+        if( isHigh() ) return 0; // High types are dead, and should never hit code emission.
         if( this==I8  || this==U8 || this==BOOL ) return 0; // 1<<0 == 1 bytes
         if( this==I16 || this==U16              ) return 1; // 1<<1 == 2 bytes
         if( this==I32 || this==U32              ) return 2; // 1<<2 == 4 bytes
         if( this==BOT                           ) return 3; // 1<<3 == 8 bytes
-        if( isHighOrConst() ) return 0;
+        if( isConstant() ) {                                // just const here
+            if (-0xFF <= _min && _min <= 0xFF)                    return 0;
+            else if (-0xFFFF <= _min && _min <= 0xFFFF)           return 1;
+            else if (-0xFFFFFFFFL <= _min && _min <= 0xFFFFFFFFL) return 2;
+            else return 3;
+        }
         throw Utils.TODO();
     }
 
@@ -118,7 +124,7 @@ public Type xmeet(Type other) {
         return this;
     }
     @Override public Type makeZero() { return ZERO; }
-    @Override public TypeInteger glb() { return BOT; }
+    @Override public Type glb(boolean mem) { return mem ? (isHigh() ? dual() : this) : BOT; }
     @Override int hash() { return Utils.fold(_min) * Utils.fold(_max); }
     @Override public boolean eq( Type t ) {
         TypeInteger i = (TypeInteger)t; // Contract
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/TypeMem.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/TypeMem.java
index 6fd87d7..455fc01 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/TypeMem.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/TypeMem.java
@@ -1,6 +1,7 @@
 package com.compilerprogramming.ezlang.compiler.sontypes;
 
 import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.Utils;
 
 import java.util.ArrayList;
 
@@ -19,7 +20,7 @@ public class TypeMem extends Type {
 
     private TypeMem(int alias, Type t) {
         super(TMEM);
-        assert alias!=0 || (t== Type.TOP || t== Type.BOTTOM);
+        assert alias!=0 || (t==Type.TOP || t==Type.BOTTOM);
         _alias = alias;
         _t = t;
     }
@@ -28,7 +29,7 @@ private TypeMem(int alias, Type t) {
     public static final TypeMem TOP = make(0, Type.TOP   );
     public static final TypeMem BOT = make(0, Type.BOTTOM);
 
-    public static void gather(ArrayList<Type> ts) { ts.add(make(1, Type.NIL)); ts.add(make(1, TypeInteger.ZERO)); ts.add(BOT); }
+    public static void gather(ArrayList<Type> ts) { ts.add(make(1,Type.NIL)); ts.add(make(1,TypeInteger.ZERO)); ts.add(BOT); }
 
     @Override
     TypeMem xmeet(Type t) {
@@ -48,7 +49,8 @@ public Type dual() {
     }
 
     @Override public boolean isHigh() { return _t.isHigh(); }
-    @Override public Type glb() { return make(_alias,_t.glb()); }
+    @Override public int log_size() { throw Utils.TODO(); }
+    @Override public Type glb(boolean mem) { return make(_alias,_t.glb(true)); }
 
     @Override int hash() { return 9876543 + _alias + _t.hashCode(); }
 
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/TypeMemPtr.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/TypeMemPtr.java
index c213e07..0b755cf 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/TypeMemPtr.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/TypeMemPtr.java
@@ -60,8 +60,7 @@ public TypeNil xmeet(Type t) {
     public TypeMemPtr dual() { return TypeMemPtr.make( dual0(), _obj.dual()); }
 
     // RHS is NIL; do not deep-dual when crossing the centerline
-    @Override
-    Type meet0() { return _nil==3 ? this : make((byte)3,_obj); }
+    @Override Type meet0() { return _nil==3 ? this : make((byte)3,_obj); }
 
 
     // True if this "isa" t up to named structures
@@ -75,7 +74,7 @@ public TypeNil xmeet(Type t) {
         throw Utils.TODO(); // return _obj.shallowISA(that._obj);
     }
 
-    @Override public TypeMemPtr glb() { return make((byte)3,_obj.glb()); }
+    @Override public TypeMemPtr glb(boolean mem) { return make((byte)3,_obj.glb(true)); }
     // Is forward-reference
     @Override public boolean isFRef() { return _obj.isFRef(); }
 
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/TypeNil.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/TypeNil.java
index 7ee19bb..48d0e57 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/TypeNil.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/TypeNil.java
@@ -46,7 +46,7 @@ Type nmeet(TypeNil tn) {
     @Override public boolean isConstant   () { return false; }
     @Override public boolean isHighOrConst() { return isHigh() || isConstant(); }
 
-    @Override public Type glb() { return Type.NIL; }
+    @Override public Type glb(boolean mem) { return Type.NIL; }
 
     public boolean notNull() { return _nil==1 || _nil==2; }
     public boolean nullable() { return _nil==3; }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/TypePtr.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/TypePtr.java
index 714271c..029f315 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/TypePtr.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/TypePtr.java
@@ -31,8 +31,7 @@ public class TypePtr extends TypeNil {
     @Override public TypePtr dual() { return PTRS[dual0()]; }
 
     // High scalar loses, low scalar wins
-    @Override
-    TypeNil nmeet(TypeNil tn) {
+    @Override TypeNil nmeet(TypeNil tn) {
         if( _nil==0 ) return tn; // High scalar loses
         if( _nil==1 ) return tn.makeFrom(xmeet0(tn)); // High scalar loses
         if( _nil==2 ) return tn._nil==3 ? PTR : NPTR; // Low scalar wins
@@ -41,14 +40,12 @@ TypeNil nmeet(TypeNil tn) {
 
 
     // RHS is  NIL
-    @Override
-    Type meet0() { return isHigh() ? NIL : PTR; }
+    @Override Type meet0() { return isHigh() ? NIL : PTR; }
     // RHS is XNIL
     // 0->xscalar, 1->nscalar, 2->nscalar, 3->scalar
-    @Override
-    Type meetX() { return _nil==0 ? XNIL : (_nil==3 ? PTR : NPTR); }
+    @Override Type meetX() { return _nil==0 ? XNIL : (_nil==3 ? PTR : NPTR); }
 
-    @Override public TypePtr glb() { return PTR; }
+    @Override public TypePtr glb(boolean mem) { return PTR; }
 
     private static final String[] STRS = new String[]{"~ptr","~nptr","nptr","ptr"};
     @Override public String str() { return STRS[_nil]; }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/TypeStruct.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/TypeStruct.java
index fa44da4..e0a7853 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/TypeStruct.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/TypeStruct.java
@@ -138,18 +138,18 @@ public TypeStruct dual() {
     }
 
     // Keeps the same struct, but lower-bounds all fields.
-    @Override public TypeStruct glb() {
-        if( _glb() ) return this;
+    @Override public TypeStruct glb(boolean mem) {
+        if( _glb(mem) ) return this;
         // Need to glb each field
         Field[] flds = new Field[_fields.length];
         for( int i=0; i<_fields.length; i++ )
-            flds[i] = _fields[i].glb();
+            flds[i] = _fields[i].glb(mem);
         return make(_name,flds);
     }
-    private boolean _glb() {
+    private boolean _glb(boolean mem) {
         if( _fields!=null )
           for( Field f : _fields )
-              if( f.glb() != f )
+              if( f.glb(mem) != f )
                  return false;
         return true;
     }
@@ -231,14 +231,12 @@ 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 )
-            cnts[f._type.log_size()]++; // Log size is 0(byte), 1(i16/u16), 2(i32/f32), 3(i64/dbl)
+        int[] cnts = new int[5]; // Count of fields at log field size
+        int flen = _fields.length;
+        if( isAry() ) flen--;   // Array field is aligned differently
+        for( int i=0; i<flen; i++ )
+            cnts[_fields[i]._type.log_size()]++; // Log size is 0(byte), 1(i16/u16), 2(i32/f32), 3(i64/dbl)
         int off = 0, idx = 0; // Base common struct fields go here, e.g. Mark/Klass
         // Compute offsets to the start of each power-of-2 aligned fields.
         int[] offs = new int[4];
@@ -249,14 +247,36 @@ private int[] offsets() {    // Field byte offsets
         // Assign offsets to all fields.
         // Really a hidden radix sort.
         _offs = new int[_fields.length+1];
-        for( Field f : _fields ) {
-            int log = f._type.log_size();
+        for( int i=0; i<flen; i++ ) {
+            int log = _fields[i]._type.log_size();
             _offs[idx++] = offs[log]; // Field offset
             offs[log] += 1<<log;      // Next field offset at same alignment
             cnts[log]--;              // Count down, should be all zero at end
         }
-        _offs[_fields.length] = (off+7)& ~7; // Round out to max alignment
+
+        // Array bits
+        if( isAry() ) {
+            // Pad out to the element alignment
+            int align = _fields[flen]._type.alignment();
+            off = (off + ((1<<align)-1)) & -(1<<align);
+            _offs[flen] = off;
+        } else
+            _offs[flen] = off; // Max struct size, no trailing padding
         return _offs;
     }
 
+    // Field order with increasing offset
+    public int[] layout() {
+        offset(0);
+        int len = _offs.length;
+        int[] is = new int[len];
+        for( int i=0; i<len; i++ ) is[i] = i;
+        // Stoopid hand rolled bubble sort
+        for( int i=0; i<len; i++ )
+            for( int j=i+1; j<len; j++ )
+                if( _offs[is[j]] < _offs[is[i]] )
+                    { int tmp = is[i]; is[i] = is[j]; is[j] = tmp; }
+        return is;
+    }
+
 }
diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/TypeTuple.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/TypeTuple.java
index 70f2762..b088790 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/TypeTuple.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/TypeTuple.java
@@ -1,6 +1,8 @@
 package com.compilerprogramming.ezlang.compiler.sontypes;
 
 import com.compilerprogramming.ezlang.compiler.SB;
+import com.compilerprogramming.ezlang.compiler.Utils;
+
 import java.util.ArrayList;
 
 public class TypeTuple extends Type {
@@ -48,11 +50,10 @@ Type xmeet(Type other) {
         return make(ts);
     }
 
-    @Override
-    public Type glb() {
+    @Override public Type glb(boolean mem) {
         Type[] ts = new Type[_types.length];
         for( int i=0; i<_types.length; i++ )
-            ts[i] = _types[i].glb();
+            ts[i] = _types[i].glb(mem);
         return make(ts);
     }
 
@@ -63,12 +64,13 @@ public Type glb() {
         return true;
     }
 
-    @Override public int log_size() {
+    @Override public int log_size() { throw Utils.TODO(); }
+    @Override public int alignment() {
         assert isConstant();
-        int log_size = 0;
+        int align = 0;
         for( Type t : _types )
-            log_size = Math.max(log_size,t.log_size());
-        return log_size;
+            align = Math.max(align,t.alignment());
+        return align;
     }
 
     public Type ret() { assert _types.length==3; return _types[2]; }

From acfe6e006ef7a2c5002cfee9105c80962f6c9cb9 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Sun, 27 Jul 2025 22:28:54 +0100
Subject: [PATCH 095/119] WIP fix missing null input as ctrl

---
 .../compilerprogramming/ezlang/compiler/nodes/ArithNode.java    | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ArithNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ArithNode.java
index 8cc3854..acac980 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ArithNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ArithNode.java
@@ -10,7 +10,7 @@
 public abstract class ArithNode extends Node {
     // Source location for late reported errors
 
-    public ArithNode(Node lhs, Node rhs) { super(lhs, rhs); }
+    public ArithNode(Node lhs, Node rhs) { super(null, lhs, rhs); }
     @Override public String glabel() { return op(); }
     abstract String op();
 

From 07675f647d012ca26d386117ab21d532929d8c1d Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Sun, 27 Jul 2025 22:29:17 +0100
Subject: [PATCH 096/119] WIP update

---
 .../ezlang/compiler/Simple.java               | 210 ++++++++----------
 1 file changed, 97 insertions(+), 113 deletions(-)

diff --git a/seaofnodes/src/test/java/com/compilerprogramming/ezlang/compiler/Simple.java b/seaofnodes/src/test/java/com/compilerprogramming/ezlang/compiler/Simple.java
index 712b539..bc6819b 100644
--- a/seaofnodes/src/test/java/com/compilerprogramming/ezlang/compiler/Simple.java
+++ b/seaofnodes/src/test/java/com/compilerprogramming/ezlang/compiler/Simple.java
@@ -17,7 +17,7 @@ public class Simple {
     static final int DUMP_AFTER_OPTO         = 1<<1;
     static final int DUMP_AFTER_TYPE_CHECK   = 1<<2;
     static final int DUMP_AFTER_LOOP_TREE    = 1<<3;
-    static final int DUMP_AFTER_INSTR_SELECT = 1<<4;
+    static final int DUMP_AFTER_SELECT       = 1<<4;
     static final int DUMP_AFTER_GCM          = 1<<5;
     static final int DUMP_AFTER_LOCAL_SCHED  = 1<<6;
     static final int DUMP_AFTER_REG_ALLOC    = 1<<7;
@@ -38,27 +38,27 @@ public class Simple {
     static void usage() {
         System.out.println(
 """
-simple <input-file> [options] [--eval|--run ...]
+simple [options] <input-file> ...args...
 Options:
-  --dump                     - dump final intermediate representation
-  --dump-after-parse         - dump intermediate representation after parse
-  --dump-after-opto          - dump intermediate representation after opto pass
-  --dump-after-type-check    - dump intermediate representation after type check pass
-  --dump-after-instr-select  - dump intermediate representation after instrution selection pass
-  --dump-after-gcm           - dump intermediate representation after GCM pass
-  --dump-after-local-sched   - dump intermediate representation after local scheduling pass
-  --dump-after-reg-alloc     - dump intermediate representation after register allocation pass
-  --dump-after-encode        - dump intermediate representation after encoding pass
-  --dump-after-all           - dump intermediate representation after all passes
-  --dot                      - dump grapical representation of intermediate code into *.dot file(s)
-  -S                         - dump generated assembler code
-  --eval ...                 - evaluate the compiled code in emulator
-  --run ...                  - run the compiled code natively
-  --dump-size                - print the size of generated code
-  --dump-time                - print compilation and execution times
-  --cpu <cpu-name>           - use specific CPU (x86_64_v2, riscv, arm)
-  --abi <abi-name>           - use speific ABI variant (SystemV)
-  --target                   - print native CPU and ABI
+  --dump                   - dump final intermediate representation
+  --dump-after-parse       - dump intermediate representation after parse
+  --dump-after-opto        - dump intermediate representation after opto pass
+  --dump-after-type-check  - dump intermediate representation after type check pass
+  --dump-after-select      - dump intermediate representation after instrution selection pass
+  --dump-after-gcm         - dump intermediate representation after GCM pass
+  --dump-after-local-sched - dump intermediate representation after local scheduling pass
+  --dump-after-reg-alloc   - dump intermediate representation after register allocation pass
+  --dump-after-encode      - dump intermediate representation after encoding pass
+  --dump-after-all         - dump intermediate representation after all passes
+  --dot                    - dump grapical representation of intermediate code into *.dot file(s)
+  -S                       - dump generated assembler code
+  --eval                   - evaluate the compiled code in emulator
+  --run                    - run the compiled code natively; this is the default
+  --dump-size              - print the size of generated code
+  --dump-time              - print compilation and execution times
+  --cpu <cpu-name>         - use specific CPU (x86_64_v2, riscv, arm)
+  --abi <abi-name>         - use speific ABI variant (SystemV)
+  --target                 - print native CPU and ABI
   --version
   --help
 """
@@ -66,16 +66,14 @@ static void usage() {
         System.exit(0);
     }
 
-    static void bad_usage() {
-        System.err.println("ERROR: Invalid usage (use --help)");
+    static void bad_usage() { bad("Invalid usage (use --help)"); }
+    static RuntimeException bad(String err) {
+        System.err.println("ERROR: "+err);
         System.exit(1);
+        return new RuntimeException(err);
     }
 
     static void target() {
-        if (system_cpu == null || system_abi == null) {
-            System.err.println("ERROR: Unknown target");
-            System.exit(1);
-        }
         System.out.print(system_cpu);
         System.out.print("-");
         System.out.println(system_abi);
@@ -86,26 +84,23 @@ static void dump(CodeGen code, int dump, int pass) {
         if ((dump & pass) != 0) {
             if ((dump & DUMP_DOT) != 0) {
                 String fn = switch (pass) {
-                    case DUMP_AFTER_PARSE        -> "01-parse.dot";
-                    case DUMP_AFTER_OPTO         -> "02-opto.dot";
-                    case DUMP_AFTER_TYPE_CHECK   -> "03-type_check.dot";
-                    case DUMP_AFTER_LOOP_TREE    -> "04-loop_tree.dot";
-                    case DUMP_AFTER_INSTR_SELECT -> "05-instr_select.dot";
-                    case DUMP_AFTER_GCM          -> "06-gcm.dot";
-                    case DUMP_AFTER_LOCAL_SCHED  -> "07-local_sched.dot";
-                    case DUMP_AFTER_REG_ALLOC    -> "08-reg_allos.dot";
-                    case DUMP_AFTER_ENCODE       -> "09-local_sched.dot";
-                    case DUMP_FINAL              -> "10-final.dot";
-                    default                      -> throw Utils.TODO();
+                case DUMP_AFTER_PARSE        -> "01-parse.dot";
+                case DUMP_AFTER_OPTO         -> "02-opto.dot";
+                case DUMP_AFTER_TYPE_CHECK   -> "03-type_check.dot";
+                case DUMP_AFTER_LOOP_TREE    -> "04-loop_tree.dot";
+                case DUMP_AFTER_SELECT       -> "05-instr_select.dot";
+                case DUMP_AFTER_GCM          -> "06-gcm.dot";
+                case DUMP_AFTER_LOCAL_SCHED  -> "07-local_sched.dot";
+                case DUMP_AFTER_REG_ALLOC    -> "08-reg_allos.dot";
+                case DUMP_AFTER_ENCODE       -> "09-local_sched.dot";
+                case DUMP_FINAL              -> "10-final.dot";
+                default                      -> throw Utils.TODO();
                 };
 
                 try {
                     Files.writeString(Path.of(fn),
-                        new GraphVisualizer().generateDotOutput(code._stop, null, null));
-                } catch(IOException e) {
-                    System.err.println("ERROR: Cannot write DOT file");
-                    System.exit(1);
-                }
+                                      new GraphVisualizer().generateDotOutput(code._stop, null, null));
+                } catch(IOException e) { throw bad("Cannot write DOT file"); }
             } else {
                 if ((dump & DUMP_PASS_NAME) != 0) {
                     System.err.println(switch (pass) {
@@ -113,11 +108,11 @@ static void dump(CodeGen code, int dump, int pass) {
                         case DUMP_AFTER_OPTO         -> "After OPTO:";
                         case DUMP_AFTER_TYPE_CHECK   -> "After Type Check:";
                         case DUMP_AFTER_LOOP_TREE    -> "After Loop Tree:";
-                        case DUMP_AFTER_INSTR_SELECT -> "After Instruction Selection:";
+                        case DUMP_AFTER_SELECT       -> "After Code Selection:";
                         case DUMP_AFTER_GCM          -> "After GCM:";
                         case DUMP_AFTER_LOCAL_SCHED  -> "After Local Scheduling:";
                         case DUMP_AFTER_REG_ALLOC    -> "After Register Allocation:";
-                        case DUMP_AFTER_ENCODE       -> "After Instruction Encoding:";
+                        case DUMP_AFTER_ENCODE       -> "After Encoding:";
                         case DUMP_FINAL              -> "Final:";
                         default                      -> throw Utils.TODO();
                     });
@@ -132,24 +127,24 @@ static void print_compilation_times(CodeGen code) {
         double t, total = 0;
 
         total += t = code._tParse / 1e3;
-        System.out.println(String.format("Parsing Time:               %.3f sec", t));
+        System.out.printf( "Parsing Time:             %.3f sec%n", t);
         total += t = code._tOpto / 1e3;
-        System.out.println(String.format("Optimization Time:          %.3f sec", t));
+        System.out.printf( "Optimization Time:        %.3f sec%n", t);
         total += t = code._tTypeCheck / 1e3;
-        System.out.println(String.format("Type Checking Time:         %.3f sec", t));
+        System.out.printf( "Type Checking Time:       %.3f sec%n", t);
         total += t = code._tLoopTree / 1e3;
-        System.out.println(String.format("Loop Tree Time:             %.3f sec", t));
+        System.out.printf( "Loop Tree Time:           %.3f sec%n", t);
         total += t = code._tInsSel / 1e3;
-        System.out.println(String.format("Instruction Selection Time: %.3f sec", t));
+        System.out.printf( "Code Selection Time:      %.3f sec%n", t);
         total += t = code._tGCM / 1e3;
-        System.out.println(String.format("GCM Time:                   %.3f sec", t));
+        System.out.printf( "GCM Time:                 %.3f sec%n", t);
         total += t = code._tLocal / 1e3;
-        System.out.println(String.format("Local Scheduling Time:      %.3f sec", t));
+        System.out.printf( "Local Scheduling Time:    %.3f sec%n", t);
         total += t = code._tRegAlloc / 1e3;
-        System.out.println(String.format("Register Allocation Time:   %.3f sec", t));
+        System.out.printf( "Register Allocation Time: %.3f sec%n", t);
         total += t = code._tEncode / 1e3;
-        System.out.println(String.format("Instruction Encoding Time:  %.3f sec", t));
-        System.out.println(String.format("TOTAL COMPILATION TIME:     %.3f sec", total));
+        System.out.printf( "Encoding Time:            %.3f sec%n", t);
+        System.out.printf( "TOTAL COMPILATION TIME:   %.3f sec%n", total);
     }
 
     static String getInput() {
@@ -159,9 +154,8 @@ static String getInput() {
 
     public static void main(String[] args) throws Exception {
         String input_filename = null;
-        boolean use_stdin = false;
         boolean do_eval = false;
-        boolean do_run = false;
+        boolean do_run = true;
         boolean do_codegen = false;
         boolean do_print_size = false;
         boolean do_print_time = false;
@@ -176,73 +170,61 @@ public static void main(String[] args) throws Exception {
         system_abi = "SystemV";
 
         // Parse command line
-loop:   for (int i = 0; i < args.length; i++) {
+        int i; for( i = 0; i < args.length; i++ ) {
             String arg = args[i];
-            if (arg.charAt(0) == '-') {
-                switch (arg) {
-                    case "-":                         use_stdin = true; break;
-                    case "--dump":                    dump |= DUMP_FINAL; break;
-                    case "--dump-after-parse":        dump |= DUMP_AFTER_PARSE; break;
-                    case "--dump-after-opto":         dump |= DUMP_AFTER_OPTO; break;
-                    case "--dump-after-type-check":   dump |= DUMP_AFTER_TYPE_CHECK; break;
-                    case "--dump-after-loop-tree":    dump |= DUMP_AFTER_LOOP_TREE; break;
-                    case "--dump-after-instr-select": dump |= DUMP_AFTER_INSTR_SELECT; break;
-                    case "--dump-after-gcm":          dump |= DUMP_AFTER_GCM; break;
-                    case "--dump-after-local-sched":  dump |= DUMP_AFTER_LOCAL_SCHED; break;
-                    case "--dump-after-reg-alloc":    dump |= DUMP_AFTER_REG_ALLOC; break;
-                    case "--dump-after-encode":       dump |= DUMP_AFTER_ENCODE; break;
-                    case "--dump-after-all":          dump |= DUMP_AFTER_ALL; break;
-                    case "--dot":                     dump |= DUMP_DOT; break;
-                    case "-S":                        dump |= DUMP_DISASSEMBLE; break;
-                    case "--eval":                    do_eval = true; first_arg = i + 1; break loop;
-                    case "--run":                     do_run = true; first_arg = i + 1; break loop;
-                    case "--dump-size":               do_print_size = true; break;
-                    case "--dump-time":               do_print_time = true; break;
-                    case "--cpu":                     if (cpu != null
-                                                       || i + 1 >= args.length
-                                                       || args[i + 1].charAt(0) == '-') bad_usage();
-                                                      cpu = args[i + 1];
-                                                      i++;
-                                                      break;
-                    case "--abi":                     if (abi != null
-                                                       || i + 1 >= args.length
-                                                       || args[i + 1].charAt(0) == '-') bad_usage();
-                                                      abi = args[i + 1];
-                                                      i++;
-                                                      break;
-                    case "--target":                  target();
-                    case "--version":                 throw Utils.TODO();
-                    case "--help":                    usage();
-                    default:                          bad_usage();
-                }
-            } else {
-                if (input_filename != null || use_stdin) bad_usage();
+            if( arg.charAt(0) != '-' ) {
                 input_filename = arg;
+                break;
             }
-        }
 
-        if (input_filename == null && !use_stdin) {
-            System.err.println("ERROR: no input file' (use --help)");
-            System.exit(1);
+            switch (arg) {
+            case "-":                         input_filename=""; break;
+            case "--dump":                    dump |= DUMP_FINAL; break;
+            case "--dump-after-parse":        dump |= DUMP_AFTER_PARSE; break;
+            case "--dump-after-opto":         dump |= DUMP_AFTER_OPTO; break;
+            case "--dump-after-type-check":   dump |= DUMP_AFTER_TYPE_CHECK; break;
+            case "--dump-after-loop-tree":    dump |= DUMP_AFTER_LOOP_TREE; break;
+            case "--dump-after-select":       dump |= DUMP_AFTER_SELECT; break;
+            case "--dump-after-gcm":          dump |= DUMP_AFTER_GCM; break;
+            case "--dump-after-local-sched":  dump |= DUMP_AFTER_LOCAL_SCHED; break;
+            case "--dump-after-reg-alloc":    dump |= DUMP_AFTER_REG_ALLOC; break;
+            case "--dump-after-encode":       dump |= DUMP_AFTER_ENCODE; break;
+            case "--dump-after-all":          dump |= DUMP_AFTER_ALL; break;
+            case "--dot":                     dump |= DUMP_DOT; break;
+            case "-S":                        dump |= DUMP_DISASSEMBLE; break;
+            case "--eval":                    do_eval = true ; do_run = false; break;
+            case "--run":                     do_run  = true ; break;
+            case "--norun":                   do_run  = false; break;
+            case "--dump-size":               do_print_size = true; break;
+            case "--dump-time":               do_print_time = true; break;
+            case "--cpu":                     if (cpu != null || i + 1 >= args.length || args[i + 1].charAt(0) == '-') bad_usage();
+                                              cpu = args[++i];
+                                              break;
+            case "--abi":                     if (abi != null || i + 1 >= args.length || args[i + 1].charAt(0) == '-') bad_usage();
+                                              abi = args[++i];
+                                              break;
+            case "--target":                  target(); break;
+            case "--version":                 throw Utils.TODO();
+            case "--help":                    usage();
+            default:                          bad_usage();
+            }
         }
 
+        if (input_filename == null) throw bad("no input file' (use --help)");
+        if( !input_filename.isEmpty() && !input_filename.endsWith(".ez") )
+            throw bad("File extension must be .ez");
+        String base = input_filename.substring(0,input_filename.length()-4);
+
         if (do_run || (dump & DUMP_DISASSEMBLE) != 0 || do_print_size) {
             if (cpu == null) cpu = system_cpu;
             if (abi == null) abi = system_abi;
-            if (cpu == null || abi == null) {
-                System.err.println("ERROR: Cannot compile for unknown target");
-                System.exit(1);
-            }
             do_codegen = true;
         }
 
         // Read input file
         try {
-            src = use_stdin ? getInput() : Files.readString(Path.of(input_filename));
-        } catch(IOException e) {
-            System.err.println("ERROR: Cannot read input file");
-            System.exit(1);
-        }
+            src = input_filename.isEmpty() ? getInput() : Files.readString(Path.of(input_filename));
+        } catch( IOException e ) { throw bad("Cannot read input file: "+input_filename);  }
 
         // Compilation pipeline
         CodeGen code = new CodeGen(src);
@@ -260,8 +242,8 @@ public static void main(String[] args) throws Exception {
         dump(code, dump, DUMP_AFTER_LOOP_TREE);
 
         if (do_codegen) {
-            code.instSelect(cpu, abi);
-            dump(code, dump, DUMP_AFTER_INSTR_SELECT);
+            code.instSelect(cpu, abi, PORTS);
+            dump(code, dump, DUMP_AFTER_SELECT);
         }
 
         code.GCM();
@@ -276,6 +258,8 @@ public static void main(String[] args) throws Exception {
 
             code.encode();
             dump(code, dump, DUMP_AFTER_ENCODE);
+
+            code.exportELF(base+".o");
         }
 
         dump(code, dump, DUMP_FINAL);
@@ -285,7 +269,7 @@ public static void main(String[] args) throws Exception {
         }
 
         if (do_print_size) {
-             System.out.println(String.format("Code Size: %d", code._encoding._bits.size()));
+             System.out.printf( "Code Size: %d%n", code._encoding._bits.size());
         }
 
         if (do_print_time) {

From 382d61f5b216073b724bba41ff80e2f831314abb Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Wed, 30 Jul 2025 09:53:26 +0100
Subject: [PATCH 097/119] WIP inlining calls not supported yet

---
 .../ezlang/compiler/nodes/CallEndNode.java                  | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CallEndNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CallEndNode.java
index 947d3e9..8889bdd 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CallEndNode.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CallEndNode.java
@@ -18,6 +18,8 @@ public class CallEndNode extends CFGNode implements MultiNode {
     private boolean _folding;
     public final TypeRPC _rpc;
 
+    public static final boolean ALLOW_INLINING_CALLS = false;   // FIXME this is pending
+
     public CallEndNode(CallNode call) { super(new Node[]{call}); _rpc = TypeRPC.constant(_nid); }
     public CallEndNode(CallEndNode cend) { super(cend); _rpc = cend._rpc; }
 
@@ -63,7 +65,9 @@ public Node idealize() {
 
         // Trivial inlining: call site calls a single function; single function
         // is only called by this call site.
-        if( !_folding && nIns()==2 && in(0) instanceof CallNode call ) {
+        // EZ Lang - we do not support inlining calls yet
+        // EZ Lang - possibly requires updating the type dict to say inlined function is dead
+        if( ALLOW_INLINING_CALLS && !_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

From 57863e5ee266425cbf76f8bc068c851ecdf4e335 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Sat, 9 Aug 2025 22:41:59 +0100
Subject: [PATCH 098/119] update outputs for merge sort

---
 .../ezlang/compiler/Compiler.java             |   2 +-
 seaofnodes/src/test/cases/mergsort/README.md  |  14 +
 seaofnodes/src/test/cases/mergsort/sort_ez.ir | 295 ++++++++++
 .../src/test/cases/mergsort/sort_ez.out       | 440 ++++++++-------
 .../src/test/cases/mergsort/sort_smp.ir       | 329 +++++++++++
 .../src/test/cases/mergsort/sort_smp.out      | 520 ++++++++++--------
 .../ezlang/compiler/Simple.java               |   2 +-
 7 files changed, 1150 insertions(+), 452 deletions(-)
 create mode 100644 seaofnodes/src/test/cases/mergsort/README.md
 create mode 100644 seaofnodes/src/test/cases/mergsort/sort_ez.ir
 create mode 100644 seaofnodes/src/test/cases/mergsort/sort_smp.ir

diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java
index b184240..bf45445 100644
--- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java
+++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java
@@ -280,7 +280,7 @@ private void generateFunction(Symbol.FunctionTypeSymbol functionTypeSymbol) {
 //        INITS.clear();
         _code._stop.peephole();
 //        if( show ) showGraph();
-        showGraph();
+        //showGraph();
     }
 
     /**
diff --git a/seaofnodes/src/test/cases/mergsort/README.md b/seaofnodes/src/test/cases/mergsort/README.md
new file mode 100644
index 0000000..d0f8eda
--- /dev/null
+++ b/seaofnodes/src/test/cases/mergsort/README.md
@@ -0,0 +1,14 @@
+### simple
+
+To compare 
+
+Turn off inlining in CallEndNode
+
+Run simple on our test
+
+--dump-after-local-sched -S --cpu x86_64_v2 --abi Win64 /pat/to/file.smp
+
+Then run the same test in EZ
+
+--dump-after-local-sched -S --cpu x86_64_v2 --abi Win64 /pat/to/file.ez
+
diff --git a/seaofnodes/src/test/cases/mergsort/sort_ez.ir b/seaofnodes/src/test/cases/mergsort/sort_ez.ir
new file mode 100644
index 0000000..24abd97
--- /dev/null
+++ b/seaofnodes/src/test/cases/mergsort/sort_ez.ir
@@ -0,0 +1,295 @@
+START:                            [[   ]]
+  10 Start   ____ ____ ____       [[    12   14   17   23    9  162   28  136   59  115  ]]  [  Ctrl, #TOP, int]
+
+L9:                               [[  START      ]]
+   9 merge_s ____   10            [[    11   13   16   18   22  213    8  ]]  Ctrl
+  11 $mem       9   12            [[     8       ]]  #BOT
+  13 a          9   14            [[     8    5  ]]  *[int]
+  16 n          9   17            [[     8    5  ]]  int
+  22 $rpc       9   23            [[     2       ]]  $[ALL]
+  18 b          9   14            [[     8    5  ]]  *[int]
+   8 call       9   11   13  213   16   18  [[     7  ]]  Ctrl
+ 213 #0         9                 [[     8    5  ]]  0
+
+L162:                             [[  START      ]]
+ 162 main    ____   10            [[   164  212  167  208  171  181  180  179  178  177  176  175  174  173  172  169  193  192  191  190  189  188  187  186  185  184  166  205  204  203  202  201  200  199  198  197  196  163  161  ]]  Ctrl
+ 164 $mem     162   12            [[   163  171  171  ]]  #BOT
+ 212 $rpc     162   23            [[   155       ]]  $[ALL]
+ 181 st8      162  182  183 ____ ____  [[   180  ]]  #3:int
+ 205 st8      162  206  207 ____ ____  [[   204  ]]  #3:int
+ 180 st8      162  181  183 ____ ____  [[   179  ]]  #3:int
+ 204 st8      162  205  207 ____ ____  [[   203  ]]  #3:int
+ 179 st8      162  180  183 ____ ____  [[   178  ]]  #3:int
+ 193 st8      162  194  195 ____ ____  [[   192  ]]  #3:int
+ 203 st8      162  204  207 ____ ____  [[   202  ]]  #3:int
+ 178 st8      162  179  183 ____ ____  [[   177  ]]  #3:int
+ 202 st8      162  203  207 ____ ____  [[   201  ]]  #3:int
+ 192 st8      162  193  195 ____ ____  [[   191  ]]  #3:int
+ 177 st8      162  178  183 ____ ____  [[   176  ]]  #3:int
+ 201 st8      162  202  207 ____ ____  [[   200  ]]  #3:int
+ 191 st8      162  192  195 ____ ____  [[   190  ]]  #3:int
+ 200 st8      162  201  207 ____ ____  [[   199  ]]  #3:int
+ 176 st8      162  177  183 ____ ____  [[   175  ]]  #3:int
+ 190 st8      162  191  195 ____ ____  [[   189  ]]  #3:int
+ 199 st8      162  200  207 ____ ____  [[   198  ]]  #3:int
+ 189 st8      162  190  195 ____ ____  [[   188  ]]  #3:int
+ 175 st8      162  176  183 ____ ____  [[   174  ]]  #3:int
+ 198 st8      162  199  207 ____ ____  [[   197  ]]  #3:int
+ 188 st8      162  189  195 ____ ____  [[   187  ]]  #3:int
+ 174 st8      162  175  183 ____ ____  [[   173  ]]  #3:int
+ 197 st8      162  198  207 ____ ____  [[   196  ]]  #3:int
+ 187 st8      162  188  195 ____ ____  [[   186  ]]  #3:int
+ 173 st8      162  174  183 ____ ____  [[   172  ]]  #3:int
+ 196 st8      162  197  207 ____ ____  [[   163  ]]  #3:int
+ 172 st8      162  173  183 ____ ____  [[   169  ]]  #3:int
+ 186 st8      162  187  195 ____ ____  [[   185  ]]  #3:int
+ 163 ALLMEM   162  164  165  196  [[   161       ]]  #BOT
+ 185 st8      162  186  195 ____ ____  [[   184  ]]  #3:int
+ 169 new_ary  162  167  170  172  [[   168  194  195  ]]  [  Ctrl, *![int], #2:u32, #3:int]
+ 168 $2       169                 [[   166       ]]  #2:u32
+ 194 $3       169                 [[   193       ]]  #3:int
+ 195 [int]    169                 [[   193  192  191  190  189  188  187  186  185  184  161  ]]  *[int]
+ 161 call     162  163  183  195  208  [[   160  ]]  Ctrl
+ 184 st8      162  185  195 ____ ____  [[   166  ]]  #3:int
+ 167 #88      162                 [[   166  169  171  ]]  88
+ 166 new_ary  162  167  168  184  [[   165  206  207  ]]  [  Ctrl, *![int], #2:u32, #3:int]
+ 165 $2       166                 [[   163       ]]  #2:u32
+ 206 $3       166                 [[   205       ]]  #3:int
+ 207 [int]    166                 [[   205  204  203  202  201  200  199  198  197  196  158  ]]  *[int]
+ 171 new_ary  162  167  164  164  [[   170  182  183  ]]  [  Ctrl, *![int], #2:u32, #3:int]
+ 170 $2       171                 [[   169       ]]  #2:u32
+ 182 $3       171                 [[   181       ]]  #3:int
+ 183 [int]    171                 [[   181  180  179  178  177  176  175  174  173  172  161  158  ]]  *[int]
+ 208 #10      162                 [[   161  158  ]]  10
+
+L28:                              [[  START      ]]
+  28 split_m ____   10            [[    32   33   44   45   49   54   31   29   27  ]]  Ctrl
+  32 end       28   17            [[    31   47   39   36  ]]  int
+  33 begin     28   17            [[    31   42   47   36  ]]  int
+  44 $mem      28   12            [[    42   52  ]]  #BOT
+  45 a         28   14            [[    42   39   36  ]]  *[int]
+  54 $rpc      28   23            [[    24       ]]  $[ALL]
+  49 b         28   14            [[    42   39   36  ]]  *[int]
+  31 sub       28   32   33       [[    29       ]]  int
+  29 cmp       28   31            [[    27       ]]  bool
+  27 j<=       28   29            [[    26   43  ]]  [  Ctrl, Ctrl]
+
+L136:                             [[  START      ]]
+ 136 eq      ____   10            [[   142  143  147  150  154  214  135  ]]  Ctrl
+ 142 $mem     136   12            [[   140  146  152  ]]  #BOT
+ 143 a        136   14            [[   140       ]]  *[int]
+ 154 $rpc     136   23            [[   131       ]]  $[ALL]
+ 150 n        136   17            [[   148       ]]  int
+ 147 b        136   14            [[   146       ]]  *[int]
+ 214 #0       136                 [[   153  144  ]]  0
+
+L59:                              [[  START      ]]
+  59 merge   ____   10            [[    76   79   82   89   91   96  110  216  217   58  ]]  Ctrl
+  76 begin     59   17            [[    75   92  ]]  int
+  79 middle    59   17            [[    73   83  ]]  int
+  82 end       59   17            [[    80  107  ]]  int
+ 110 $rpc      59   23            [[    55       ]]  $[ALL]
+  96 a         59   14            [[    95   97   86   99  ]]  *[int]
+  91 b         59   14            [[    90       ]]  *[int]
+  89 $mem      59   12            [[    88  109  ]]  #BOT
+ 217 #1        59                 [[   105  105  ]]  1
+ 216 #0        59                 [[   105  105   55  ]]  0
+
+L115:                             [[  START      ]]
+ 115 copy_ar ____   10            [[   120  122  124  127  129  130  114  ]]  Ctrl
+ 120 begin    115   17            [[   119       ]]  int
+ 130 $rpc     115   23            [[   111       ]]  $[ALL]
+ 129 a        115   14            [[   128       ]]  *[int]
+ 127 b        115   14            [[   126       ]]  *[int]
+ 124 $mem     115   12            [[   123  125  ]]  #BOT
+ 122 end      115   17            [[   117       ]]  int
+
+LOOP135:                          [[  L136      L137       ]]
+ 135 Loop    ____  136  137       [[   144  148  134  ]]  Ctrl
+ 144 Phi_i    135  214  145       [[   140  145  146  148  ]]  int
+ 148 cmp      135  144  150       [[   134       ]]  bool
+ 134 j<       135  148            [[   133  139  ]]  [  Ctrl, Ctrl]
+
+LOOP58:                           [[  L59       L60        ]]
+  58 Loop    ____   59   60       [[    75   83   88   92  107   57  ]]  Ctrl
+  75 Phi_i     58   76   77       [[    73   78   77   95   86  ]]  int
+  83 Phi_j     58   79   84       [[    80   84   85   97   99  ]]  int
+  88 Phi_$3    58   89   90       [[    86   90   95   97   99  109  ]]  #BOT
+  92 Phi_k     58   76   93       [[    90   93  107  ]]  int
+ 107 cmp       58   92   82       [[    57       ]]  bool
+  57 j<        58  107            [[    56   72  ]]  [  Ctrl, Ctrl]
+
+LOOP114:                          [[  L115      L116       ]]
+ 114 Loop    ____  115  116       [[   119  125  117  113  ]]  Ctrl
+ 119 Phi_k    114  120  121       [[   117  121  126  128  ]]  int
+ 125 Phi_$3   114  124  126       [[   123  126  128  ]]  #BOT
+ 117 cmp      114  119  122       [[   113       ]]  bool
+ 113 j<       114  117            [[   112  116  ]]  [  Ctrl, Ctrl]
+
+L7:                               [[  L9         ]]
+   7 CallEnd    8                 [[     6   19  ]]  [  Ctrl, #BOT, int]
+  19 $mem       7                 [[     5       ]]  #BOT
+
+L160:                             [[  L162       ]]
+ 160 CallEnd  161                 [[   159  209  ]]  [  Ctrl, #BOT, int]
+ 209 $mem     160                 [[   158       ]]  #BOT
+
+L26:                              [[  L28        ]]
+  26 True      27                 [[    25       ]]  Ctrl
+
+L43:                              [[  L28        ]]
+  43 False     27                 [[    47   48   46   42  ]]  Ctrl
+  42 call      43   44   45   33   46   49  [[    41  ]]  Ctrl
+  47 add       43   32   33       [[    46       ]]  int
+  48 #2        43                 [[    46       ]]  2
+  46 div       43   47   48       [[    42   39   36  ]]  int
+
+L6:                               [[  L7         ]]
+   6 $ctrl      7                 [[     5       ]]  Ctrl
+   5 call       6   19   13  213   16   18  [[     4  ]]  Ctrl
+
+L116:                             [[  LOOP114    ]]
+ 116 True     113                 [[   128  126  121  114  ]]  Ctrl
+ 121 inc      116  119            [[   119       ]]  int
+ 128 ld8      116  125  129  119  [[   126       ]]  int
+ 126 st8      116  125  127  119  128  [[   125  ]]  #3:Bot
+
+L159:                             [[  L160       ]]
+ 159 $ctrl    160                 [[   158       ]]  Ctrl
+ 158 call     159  209  183  207  208  [[   157  ]]  Ctrl
+
+L56:                              [[  LOOP58     ]]
+  56 False     57                 [[   109   55  ]]  Ctrl
+ 109 ALLMEM    56   89 ____   88  [[    55       ]]  #BOT
+  55 Return    56  109  216  110  [[     1       ]]  [  Ctrl, #BOT, 0]
+
+L112:                             [[  LOOP114    ]]
+ 112 False    113                 [[   123  215  111  ]]  Ctrl
+ 123 ALLMEM   112  124 ____  125  [[   111       ]]  #BOT
+ 215 #0       112                 [[   111       ]]  0
+ 111 Return   112  123  215  130  [[     1       ]]  [  Ctrl, #BOT, 0]
+
+L72:                              [[  LOOP58     ]]
+  72 True      57                 [[    73   71  ]]  Ctrl
+  73 cmp       72   75   79       [[    71       ]]  bool
+  71 j<        72   73            [[    70  100  ]]  [  Ctrl, Ctrl]
+
+L139:                             [[  LOOP135    ]]
+ 139 True     134                 [[   146  140  138  ]]  Ctrl
+ 146 ld8      139  142  147  144  [[   140       ]]  int
+ 140 cmp8     139  142  143  144  146  [[   138  ]]  bool
+ 138 j==      139  140            [[   137  151  ]]  [  Ctrl, Ctrl]
+
+L133:                             [[  LOOP135    ]]
+ 133 False    134                 [[   106  132  ]]  Ctrl
+ 106 #1       133                 [[   153       ]]  1
+
+L41:                              [[  L43        ]]
+  41 CallEnd   42                 [[    40   50  ]]  [  Ctrl, #BOT, 0]
+  50 $mem      41                 [[    39       ]]  #BOT
+
+L4:                               [[  L6         ]]
+   4 CallEnd    5                 [[     3   20   21  ]]  [  Ctrl, #BOT, int]
+  20 $mem       4                 [[     2       ]]  #BOT
+  21 #2         4                 [[     2       ]]  int
+
+L157:                             [[  L159       ]]
+ 157 CallEnd  158                 [[   156  210  211  ]]  [  Ctrl, #BOT, bool]
+ 210 $mem     157                 [[   155       ]]  #BOT
+ 211 #2       157                 [[   155       ]]  bool
+
+L70:                              [[  L72        ]]
+  70 True      71                 [[    80   69  ]]  Ctrl
+  80 cmp       70   82   83       [[    69       ]]  bool
+  69 j<=       70   80            [[    68  101  ]]  [  Ctrl, Ctrl]
+
+L100:                             [[  L72        ]]
+ 100 False     71                 [[    65       ]]  Ctrl
+
+L137:                             [[  L139       ]]
+ 137 True     138                 [[   145  135  ]]  Ctrl
+ 145 inc      137  144            [[   144       ]]  int
+
+L40:                              [[  L41        ]]
+  40 $ctrl     41                 [[    39       ]]  Ctrl
+  39 call      40   50   45   46   32   49  [[    38  ]]  Ctrl
+
+L151:                             [[  L139       ]]
+ 151 False    138                 [[   132       ]]  Ctrl
+
+L3:                               [[  L4         ]]
+   3 $ctrl      4                 [[     2       ]]  Ctrl
+   2 Return     3   20   21   22  [[     1       ]]  [  Ctrl, #BOT, int]
+
+L132:                             [[  L133      L151       ]]
+ 132 Region  ____  133  151       [[   153  152  131  ]]  Ctrl
+ 153 Phi_res  132  106  214       [[   131       ]]  bool
+ 152 ALLMEM   132  142 ____ ____  [[   131       ]]  #BOT
+ 131 Return   132  152  153  154  [[     1       ]]  [  Ctrl, #BOT, bool]
+
+L156:                             [[  L157       ]]
+ 156 $ctrl    157                 [[   155       ]]  Ctrl
+ 155 Return   156  210  211  212  [[     1       ]]  [  Ctrl, #BOT, bool]
+
+L68:                              [[  L70        ]]
+  68 False     69                 [[    99   86   67  ]]  Ctrl
+  99 ld8       68   88   96   83  [[    86       ]]  int
+  86 cmp8      68   88   96   75   99  [[    67  ]]  bool
+  67 j<=       68   86            [[    66  102  ]]  [  Ctrl, Ctrl]
+
+L38:                              [[  L40        ]]
+  38 CallEnd   39                 [[    37   51  ]]  [  Ctrl, #BOT, 0]
+  51 $mem      38                 [[    36       ]]  #BOT
+
+L101:                             [[  L70        ]]
+ 101 True      69                 [[    65       ]]  Ctrl
+
+L37:                              [[  L38        ]]
+  37 $ctrl     38                 [[    36       ]]  Ctrl
+  36 call      37   51   49   33   46   32   45  [[    35  ]]  Ctrl
+
+L66:                              [[  L68        ]]
+  66 True      67                 [[    65       ]]  Ctrl
+
+L102:                             [[  L68        ]]
+ 102 False     67                 [[    65       ]]  Ctrl
+
+L65:                              [[  L66       L100      L101      L102       ]]
+  65 Region  ____   66  100  101  102  [[   105  103   64  ]]  Ctrl
+ 105 Phi_con   65  217  216  217  216  [[   103  ]]  bool
+ 103 test      65  105            [[    64       ]]
+  64 j!=       65  103            [[    61   98  ]]  [  Ctrl, Ctrl]
+
+L35:                              [[  L37        ]]
+  35 CallEnd   36                 [[    34   53  ]]  [  Ctrl, #BOT, int]
+  53 $mem      35                 [[    52       ]]  #BOT
+
+L34:                              [[  L35        ]]
+  34 $ctrl     35                 [[    25       ]]  Ctrl
+
+L98:                              [[  L65        ]]
+  98 False     64                 [[    97   85   60  ]]  Ctrl
+  85 inc       98   83            [[    84       ]]  int
+  97 ld8       98   88   96   83  [[    94       ]]  int
+
+L25:                              [[  L26       L34        ]]
+  25 Region  ____   26   34       [[    52   15   24  ]]  Ctrl
+  52 Phi_$me   25   44   53       [[    24       ]]  #BOT
+  15 #0        25                 [[    24       ]]  0
+  24 Return    25   52   15   54  [[     1       ]]  [  Ctrl, #BOT, 0]
+
+L61:                              [[  L65        ]]
+  61 True      64                 [[    95   78   60  ]]  Ctrl
+  78 inc       61   75            [[    77       ]]  int
+  95 ld8       61   88   96   75  [[    94       ]]  int
+
+L60:                              [[  L61       L98        ]]
+  60 Region  ____   61   98       [[    77   84   94   90   93   58  ]]  Ctrl
+  77 Phi_i     60   78   75       [[    75       ]]  int
+  84 Phi_j     60   83   85       [[    83       ]]  int
+  94 Phi_$3    60   95   97       [[    90       ]]  int
+  93 inc       60   92            [[    92       ]]  int
+  90 st8       60   88   91   92   94  [[    88  ]]  #3:Bot
+
+L1:                               [[  L112      L132      L156       ]]
+   1 Stop       2   24   55  111  131  155  [[   ]]  Bot
\ No newline at end of file
diff --git a/seaofnodes/src/test/cases/mergsort/sort_ez.out b/seaofnodes/src/test/cases/mergsort/sort_ez.out
index b4cc2a5..92eae4c 100644
--- a/seaofnodes/src/test/cases/mergsort/sort_ez.out
+++ b/seaofnodes/src/test/cases/mergsort/sort_ez.out
@@ -1,250 +1,266 @@
 ---split_merge { *![int] int int *![int] -> int #0}---------------------------
-00F0 4883EC38    subi  rsp -= #56
-                 a:r9,b:rcx,begin:rdx,end:r8
-00F4 4889542428  mov   [rsp+40] = rdx                 // def/loop #1
-00F9 4C89442458  mov   [rsp+88] = r8                  // def/loop #0
-00FE 48894C2450  mov   [rsp+80] = rcx                 // def/loop #0
-0103 4C894C2448  mov   [rsp+72] = r9                  // def/loop #0
-0108 4C8B442458  mov   r8 = [rsp+88]                  // use/loop/use #0
-010D 4C8B542428  mov   r10 = [rsp+40]                 // use/loop/use #1
-0112 4D2BC2      sub   r8 -= r10
-0115 4983F801    cmp   r8, #1
-0119 7E71        j<=   L65                            // L69
-L69:
-011B 488B442458  mov   rax = [rsp+88]                 // use/loop/use #0
-0120 488B542428  mov   rdx = [rsp+40]                 // use/loop/use #1
-0125 4803C2      add   rax += rdx
-0128 B902000000  ldi   rcx = #2
-012D 489948F7F9  div   rax = rax / rcx                // kill rdx
-0132 4889442440  mov   [rsp+64] = rax                 // def/loop #0
-0137 488B4C2448  mov   rcx = [rsp+72]                 // use/loop/use #0
-013C 4C8B4C2450  mov   r9 = [rsp+80]                  // use/loop/use #0
-0141 4C8B442440  mov   r8 = [rsp+64]                  // use/loop/use #0
-0146 488B542428  mov   rdx = [rsp+40]                 // use/loop/use #1
-014B E8A0FFFFFF  call  split_merge  rcx  rdx  r8  r9
-0150 488B4C2448  mov   rcx = [rsp+72]                 // use/loop/use #0
-0155 4C8B4C2450  mov   r9 = [rsp+80]                  // use/loop/use #0
-015A 4C8B442458  mov   r8 = [rsp+88]                  // use/loop/use #0
-015F 488B542440  mov   rdx = [rsp+64]                 // use/loop/use #0
-0164 E887FFFFFF  call  split_merge  rcx  rdx  r8  r9
-0169 FF7424488F  mov   [rsp+32] = [rsp+72]            // use/loop/use #0
-016E 442420
-0171 488B4C2450  mov   rcx = [rsp+80]                 // use/loop/use #0
-0176 4C8B4C2458  mov   r9 = [rsp+88]                  // use/loop/use #0
-017B 4C8B442440  mov   r8 = [rsp+64]                  // use/loop/use #0
-0180 488B542428  mov   rdx = [rsp+40]                 // use/loop/use #1
-0185 E8A6FEFFFF  call  merge  rcx  rdx  r8  r9  [stk#42]
-018A EB02        jmp   L64
-L412:
-L65:
-018C 33C0        xor   rax,rax
-L64:
-                 arg:rax
-018E 4883C438C3  addi  rsp += #56
-0193             ret
+00C0 4883EC38    subi  rsp -= #56
+                 end:r8,begin:rdx,a:r9,b:rcx
+00C4 4889542428  mov   [rsp+40] = rdx                 // def/loop #1
+00C9 48894C2458  mov   [rsp+88] = rcx                 // def/loop #0
+00CE 4C894C2450  mov   [rsp+80] = r9                  // def/loop #0
+00D3 4C89442448  mov   [rsp+72] = r8                  // def/loop #0
+00D8 4C8B542428  mov   r10 = [rsp+40]                 // use/loop/use #1
+00DD 4D2BC2      sub   r8 -= r10
+00E0 4983F801    cmp   r8, #1
+00E4 7E6F        j<=   L25                            // L43
+L43:
+00E6 488B442448  mov   rax = [rsp+72]                 // use/loop/use #0
+00EB 488B542428  mov   rdx = [rsp+40]                 // use/loop/use #1
+00F0 4803C2      add   rax += rdx
+00F3 B902000000  ldi   rcx = #2
+00F8 489948F7F9  div   rax = rax / rcx                // kill rdx
+00FD 4889442440  mov   [rsp+64] = rax                 // def/loop #0
+0102 488B4C2450  mov   rcx = [rsp+80]                 // use/loop/use #0
+0107 4C8B4C2458  mov   r9 = [rsp+88]                  // use/loop/use #0
+010C 4C8B442440  mov   r8 = [rsp+64]                  // use/loop/use #0
+0111 488B542428  mov   rdx = [rsp+40]                 // use/loop/use #1
+0116 E8A5FFFFFF  call  split_merge  rcx  rdx  r8  r9
+011B 4C8B442448  mov   r8 = [rsp+72]                  // use/loop/use #0
+0120 488B4C2450  mov   rcx = [rsp+80]                 // use/loop/use #0
+0125 4C8B4C2458  mov   r9 = [rsp+88]                  // use/loop/use #0
+012A 488B542440  mov   rdx = [rsp+64]                 // use/loop/use #0
+012F E88CFFFFFF  call  split_merge  rcx  rdx  r8  r9
+0134 4C8B4C2448  mov   r9 = [rsp+72]                  // use/loop/use #0
+0139 FF7424508F  mov   [rsp+32] = [rsp+80]            // use/loop/use #0
+013E 442420
+0141 488B4C2458  mov   rcx = [rsp+88]                 // use/loop/use #0
+0146 4C8B442440  mov   r8 = [rsp+64]                  // use/loop/use #0
+014B 488B542428  mov   rdx = [rsp+40]                 // use/loop/use #1
+0150 E8CBFEFFFF  call  merge  rcx  rdx  r8  r9  [stk#42]
+L26:
+L25:
+0155 33C0        xor   rax,rax
+0157 4883C438C3  addi  rsp += #56
+015C             ret
 ---{ *![int] int int *![int] -> int #0}---------------------------
 
 ---copy_array { *![int] int int *![int] -> int #1}---------------------------
                  begin:rdx,end:r8,b:r9,a:rcx
-LOOP157:
+LOOP114:
                  k:rdx
-02F0 493BD0      cmp   rdx, r8
-02F3 7D10        j>=   L155                           // L159
-L159:
-02F5 488B44D108  ld8   rax,[rcx+rdx*8+8]
-02FA 498944D108  st8   [r9+rdx*8+8],rax
-02FF 4883C201    inc   rdx += #1
-0303 EBEB        jmp   LOOP157
-L411:
-L155:
-0305 33C0        xor   rax,rax
-0307 C3          ret
+0000 493BD0      cmp   rdx, r8
+0003 7D0F        j>=   L112                           // L116
+L116:
+0005 488B44D108  ld8   rax,[rcx+rdx*8+8]
+000A 498944D108  st8   [r9+rdx*8+8],rax
+000F 48FFC2      inc   rdx += #1
+0012 EBEC        jmp   LOOP114
+L423:
+L112:
+0014 33C0        xor   rax,rax
+0016 C3          ret
 ---{ *![int] int int *![int] -> int #1}---------------------------
 
 ---merge { *![int] int int int *![int] -> int #0}---------------------------
                  begin:rdx,middle:r8,end:r9,b:rcx,a:[rsp+40]
-0030 66480F6EC3  mov   xmm0 = rbx                     // def/loop #2
-0035 488B5C2428  mov   rbx = [rsp+40]                 // def/empty1 #0
-003A 4C8BDA      mov   r11 = rdx                      // use/self/phi #1
-003D 4D8BD0      mov   r10 = r8                       // use/self/use #1
-LOOP101:
+0020 66480F6EC3  mov   xmm0 = rbx                     // def/loop #2
+0025 488B5C2428  mov   rbx = [rsp+40]                 // def/empty1 #0
+002A 4C8BDA      mov   r11 = rdx                      // use/self/phi #1
+002D 4D8BD0      mov   r10 = r8                       // use/self/use #1
+LOOP58:
                  i:r11,j:r10,k:rdx
-0040 493BD1      cmp   rdx, r9
-0043 7D52        j>=   L99                            // L115
-L115:
-0045 4D3BD8      cmp   r11, r8
-0048 7C04        j<    L113                           // L143
-L143:
-004A 33C0        xor   rax,rax
-004C EB24        jmp   L108
-L417:
-L113:
-004E 4D3BCA      cmp   r9, r10
-0051 7E1A        j<=   L144                           // L111
-L111:
-0053 488BC3      mov   rax = rbx                      // use/empty1 #0
-0056 4A8B44D008  ld8   rax,[rax+r10*8+8]
-005B 4A3944DB08  cmp8  rax, [rbx+r11*8+8]
-0060 7E04        j<=   L109                           // L145
-L145:
-0062 33C0        xor   rax,rax
-0064 EB0C        jmp   L108
-L416:
-L109:
-0066 B801000000  ldi   rax = #1
-006B EB05        jmp   L108
-L415:
-L144:
-006D B801000000  ldi   rax = #1
-L108:
+0030 493BD1      cmp   rdx, r9
+0033 7D4F        j>=   L56                            // L72
+L72:
+0035 4D3BD8      cmp   r11, r8
+0038 7C04        j<    L70                            // L100
+L100:
+003A 33C0        xor   rax,rax
+003C EB24        jmp   L65
+L422:
+L70:
+003E 4D3BCA      cmp   r9, r10
+0041 7E1A        j<=   L101                           // L68
+L68:
+0043 488BC3      mov   rax = rbx                      // use/empty1 #0
+0046 4A8B44D008  ld8   rax,[rax+r10*8+8]
+004B 4A3944DB08  cmp8  rax, [rbx+r11*8+8]
+0050 7E04        j<=   L66                            // L102
+L102:
+0052 33C0        xor   rax,rax
+0054 EB0C        jmp   L65
+L421:
+L66:
+0056 B801000000  ldi   rax = #1
+005B EB05        jmp   L65
+L420:
+L101:
+005D B801000000  ldi   rax = #1
+L65:
                  cond:rax
-0072 4883F800    test  rax
-0076 750B        j!=   L104                           // L141
-L141:
-0078 4A8B44D308  ld8   rax,[rbx+r10*8+8]
-007D 4983C201    inc   r10 += #1
-0081 EB09        jmp   L103
-L414:
-L104:
-0083 4A8B44DB08  ld8   rax,[rbx+r11*8+8]
-0088 4983C301    inc   r11 += #1
-L103:
+0062 4883F800    test  rax
+0066 750A        j!=   L61                            // L98
+L98:
+0068 4A8B44D308  ld8   rax,[rbx+r10*8+8]
+006D 49FFC2      inc   r10 += #1
+0070 EB08        jmp   L60
+L419:
+L61:
+0072 4A8B44DB08  ld8   rax,[rbx+r11*8+8]
+0077 49FFC3      inc   r11 += #1
+L60:
                  i:r11,j:r10,$3:rax
-008C 488944D108  st8   [rcx+rdx*8+8],rax
-0091 4883C201    inc   rdx += #1
-0095 EBA9        jmp   LOOP101
-L413:
-L99:
-0097 66480F7EC3  mov   rbx = xmm0                     // use/loop/use #2
-009C 33C0        xor   rax,rax
-009E C3          ret
+007A 488944D108  st8   [rcx+rdx*8+8],rax
+007F 48FFC2      inc   rdx += #1
+0082 EBAC        jmp   LOOP58
+L418:
+L56:
+0084 66480F7EC3  mov   rbx = xmm0                     // use/loop/use #2
+0089 33C0        xor   rax,rax
+008B C3          ret
 ---{ *![int] int int int *![int] -> int #0}---------------------------
 
 
 ---eq { *![int] *![int] int -> int #1}---------------------------
                  a:rcx,b:rdx,n:r8
-0000 33C0        xor   rax,rax
-LOOP178:
+0090 33C0        xor   rax,rax
+LOOP135:
                  i:rax
-0002 493BC0      cmp   rax, r8
-0005 7D16        j>=   L176                           // L186
-L186:
-0007 4C8B4CC208  ld8   r9,[rdx+rax*8+8]
-000C 4C394CC108  cmp8  r9, [rcx+rax*8+8]
-0011 7506        j!=   L198                           // L184
-L184:
-0013 4883C001    inc   rax += #1
-0017 EBE9        jmp   LOOP178
-L419:
-L198:
-0019 33C0        xor   rax,rax
-001B EB05        jmp   L175
-L418:
-L176:
-001D B801000000  ldi   rax = #1
-L175:
+0092 493BC0      cmp   rax, r8
+0095 7D15        j>=   L133                           // L139
+L139:
+0097 4C8B4CC208  ld8   r9,[rdx+rax*8+8]
+009C 4C394CC108  cmp8  r9, [rcx+rax*8+8]
+00A1 7505        j!=   L151                           // L137
+L137:
+00A3 48FFC0      inc   rax += #1
+00A6 EBEA        jmp   LOOP135
+L417:
+L151:
+00A8 33C0        xor   rax,rax
+00AA EB05        jmp   L132
+L416:
+L133:
+00AC B801000000  ldi   rax = #1
+L132:
                  result:rax
-0022 C3          ret
+00B1 C3          ret
 ---{ *![int] *![int] int -> int #1}---------------------------
 
 ---main { -> int #0}---------------------------
-01A0 4883EC28    subi  rsp -= #40
-01A4 48895C2420  mov   [rsp+32] = rbx                 // def/loop #2
-01A9 BA58000000  ldi   rdx = #88
-01AE B901000000  alloc ldi   rcx = #1
-01B3 E800000000  call  #calloc
-01B8 4889442418  mov   [rsp+24] = rax                 // def/loop #0
-01BD 488B542418  mov   rdx = [rsp+24]                 // use/loop/use #0
-01C2 48C742080A  st8   [rdx+8],#10
-01C7 000000
-01CA 488B4C2418  mov   rcx = [rsp+24]                 // use/loop/use #0
-01CF 48C7411009  st8   [rcx+16],#9
-01D4 000000
-01D7 488B442418  mov   rax = [rsp+24]                 // use/loop/use #0
-01DC 48C7401808  st8   [rax+24],#8
-01E1 000000
-01E4 4C8B5C2418  mov   r11 = [rsp+24]                 // use/loop/use #0
-01E9 49C7432007  st8   [r11+32],#7
-01EE 000000
-01F1 4C8B542418  mov   r10 = [rsp+24]                 // use/loop/use #0
-01F6 49C7422806  st8   [r10+40],#6
-01FB 000000
-01FE 4C8B4C2418  mov   r9 = [rsp+24]                  // use/loop/use #0
-0203 49C7413005  st8   [r9+48],#5
-0208 000000
-020B 4C8B442418  mov   r8 = [rsp+24]                  // use/loop/use #0
-0210 49C7403804  st8   [r8+56],#4
-0215 000000
-0218 488B542418  mov   rdx = [rsp+24]                 // use/loop/use #0
-021D 48C7424003  st8   [rdx+64],#3
-0222 000000
-0225 488B4C2418  mov   rcx = [rsp+24]                 // use/loop/use #0
-022A 48C7414802  st8   [rcx+72],#2
-022F 000000
-0232 488B442418  mov   rax = [rsp+24]                 // use/loop/use #0
-0237 48C7405001  st8   [rax+80],#1
-023C 000000
-023F BA58000000  ldi   rdx = #88
-0244 B901000000  alloc ldi   rcx = #1
-0249 E800000000  call  #calloc
-024E 4889442410  mov   [rsp+16] = rax                 // def/empty1 #1
-0253 BA58000000  ldi   rdx = #88
-0258 B901000000  alloc ldi   rcx = #1
-025D E800000000  call  #calloc
-0262 488BD8      mov   rbx = rax                      // def/empty1 #0
-0265 48C7430801  st8   [rbx+8],#1
-026A 000000
-026D 48C7431002  st8   [rbx+16],#2
-0272 000000
-0275 48C7431803  st8   [rbx+24],#3
+0160 4883EC28    subi  rsp -= #40
+0164 48895C2420  mov   [rsp+32] = rbx                 // def/loop #2
+0169 BA58000000  ldi   rdx = #88
+016E B901000000  alloc ldi   rcx = #1
+0173 E800000000  call  #calloc
+0178 4889442418  mov   [rsp+24] = rax                 // def/loop #0
+017D 488B542418  mov   rdx = [rsp+24]                 // use/loop/use #0
+0182 48C742080A  st8   [rdx+8],#10
+0187 000000
+018A 4C8B4C2418  mov   r9 = [rsp+24]                  // use/loop/use #0
+018F 49C7411009  st8   [r9+16],#9
+0194 000000
+0197 488B4C2418  mov   rcx = [rsp+24]                 // use/loop/use #0
+019C 48C7411808  st8   [rcx+24],#8
+01A1 000000
+01A4 4C8B442418  mov   r8 = [rsp+24]                  // use/loop/use #0
+01A9 49C7402007  st8   [r8+32],#7
+01AE 000000
+01B1 488B442418  mov   rax = [rsp+24]                 // use/loop/use #0
+01B6 48C7402806  st8   [rax+40],#6
+01BB 000000
+01BE 4C8B5C2418  mov   r11 = [rsp+24]                 // use/loop/use #0
+01C3 49C7433005  st8   [r11+48],#5
+01C8 000000
+01CB 4C8B542418  mov   r10 = [rsp+24]                 // use/loop/use #0
+01D0 49C7423804  st8   [r10+56],#4
+01D5 000000
+01D8 4C8B4C2418  mov   r9 = [rsp+24]                  // use/loop/use #0
+01DD 49C7414003  st8   [r9+64],#3
+01E2 000000
+01E5 488B4C2418  mov   rcx = [rsp+24]                 // use/loop/use #0
+01EA 48C7414802  st8   [rcx+72],#2
+01EF 000000
+01F2 488B442418  mov   rax = [rsp+24]                 // use/loop/use #0
+01F7 48C7405001  st8   [rax+80],#1
+01FC 000000
+01FF BA58000000  ldi   rdx = #88
+0204 B901000000  alloc ldi   rcx = #1
+0209 E800000000  call  #calloc
+020E 4889442410  mov   [rsp+16] = rax                 // def/empty1 #1
+0213 48C7400800  st8   [rax+8],#0
+0218 000000
+021B 48C7401000  st8   [rax+16],#0
+0220 000000
+0223 48C7401800  st8   [rax+24],#0
+0228 000000
+022B 48C7402000  st8   [rax+32],#0
+0230 000000
+0233 48C7402800  st8   [rax+40],#0
+0238 000000
+023B 48C7403000  st8   [rax+48],#0
+0240 000000
+0243 48C7403800  st8   [rax+56],#0
+0248 000000
+024B 48C7404000  st8   [rax+64],#0
+0250 000000
+0253 48C7404800  st8   [rax+72],#0
+0258 000000
+025B 48C7405000  st8   [rax+80],#0
+0260 000000
+0263 BA58000000  ldi   rdx = #88
+0268 B901000000  alloc ldi   rcx = #1
+026D E800000000  call  #calloc
+0272 488BD8      mov   rbx = rax                      // def/empty1 #0
+0275 48C7430801  st8   [rbx+8],#1
 027A 000000
-027D 48C7432004  st8   [rbx+32],#4
+027D 48C7431002  st8   [rbx+16],#2
 0282 000000
-0285 48C7432805  st8   [rbx+40],#5
+0285 48C7431803  st8   [rbx+24],#3
 028A 000000
-028D 48C7433006  st8   [rbx+48],#6
+028D 48C7432004  st8   [rbx+32],#4
 0292 000000
-0295 48C7433807  st8   [rbx+56],#7
+0295 48C7432805  st8   [rbx+40],#5
 029A 000000
-029D 48C7434008  st8   [rbx+64],#8
+029D 48C7433006  st8   [rbx+48],#6
 02A2 000000
-02A5 48C7434809  st8   [rbx+72],#9
+02A5 48C7433807  st8   [rbx+56],#7
 02AA 000000
-02AD 48C743500A  st8   [rbx+80],#10
+02AD 48C7434008  st8   [rbx+64],#8
 02B2 000000
-02B5 488B4C2418  mov   rcx = [rsp+24]                 // use/loop/use #0
-02BA 488B542410  mov   rdx = [rsp+16]                 // use/empty1 #0
-02BF 41B80A0000  ldi   r8 = #10
-02C4 00
-02C5 E8D6FDFFFF  call  merge_sort  rcx  rdx  r8
-02CA 488B4C2418  mov   rcx = [rsp+24]                 // use/loop/use #0
-02CF 488BD3      mov   rdx = rbx                      // use/empty1 #0
-02D2 41B80A0000  ldi   r8 = #10
-02D7 00
-02D8 E823FDFFFF  call  eq  rcx  rdx  r8
-02DD 488B5C2420  mov   rbx = [rsp+32]                 // use/loop/use #2
-02E2 4883C428C3  addi  rsp += #40
-02E7             ret
+02B5 48C7434809  st8   [rbx+72],#9
+02BA 000000
+02BD 48C743500A  st8   [rbx+80],#10
+02C2 000000
+02C5 488B4C2418  mov   rcx = [rsp+24]                 // use/loop/use #0
+02CA 488B542410  mov   rdx = [rsp+16]                 // use/empty1 #0
+02CF 41B80A0000  ldi   r8 = #10
+02D4 00
+02D5 E826000000  call  merge_sort  rcx  rdx  r8
+02DA 488B4C2418  mov   rcx = [rsp+24]                 // use/loop/use #0
+02DF 488BD3      mov   rdx = rbx                      // use/empty1 #0
+02E2 41B80A0000  ldi   r8 = #10
+02E7 00
+02E8 E8A3FDFFFF  call  eq  rcx  rdx  r8
+02ED 488B5C2420  mov   rbx = [rsp+32]                 // use/loop/use #2
+02F2 4883C428C3  addi  rsp += #40
+02F7             ret
 ---{ -> int #0}---------------------------
 
 ---merge_sort { *![int] *![int] int -> int #0}---------------------------
-00A0 4883EC28    subi  rsp -= #40
+0300 4883EC28    subi  rsp -= #40
                  a:rcx,n:r8,b:rdx
-00A4 4C89442440  mov   [rsp+64] = r8                  // def/loop #1
-00A9 48894C2438  mov   [rsp+56] = rcx                 // def/loop #1
-00AE 4889542430  mov   [rsp+48] = rdx                 // def/loop #0
-00B3 4C8B4C2430  mov   r9 = [rsp+48]                  // use/loop/use #0
-00B8 488B4C2438  mov   rcx = [rsp+56]                 // use/loop/use #1
-00BD 4C8B442440  mov   r8 = [rsp+64]                  // use/loop/use #1
-00C2 33D2        xor   rdx,rdx
-00C4 E827020000  call  copy_array  rcx  rdx  r8  r9
-00C9 4C8B4C2430  mov   r9 = [rsp+48]                  // use/loop/use #0
-00CE 488B4C2438  mov   rcx = [rsp+56]                 // use/loop/use #1
-00D3 4C8B442440  mov   r8 = [rsp+64]                  // use/loop/use #1
-00D8 33D2        xor   rdx,rdx
-00DA E811000000  call  split_merge  rcx  rdx  r8  r9
-00DF 4883C428C3  addi  rsp += #40
-00E4             ret
+0304 4C89442440  mov   [rsp+64] = r8                  // def/loop #1
+0309 48894C2438  mov   [rsp+56] = rcx                 // def/loop #1
+030E 4889542430  mov   [rsp+48] = rdx                 // def/loop #0
+0313 4C8B4C2430  mov   r9 = [rsp+48]                  // use/loop/use #0
+0318 488B4C2438  mov   rcx = [rsp+56]                 // use/loop/use #1
+031D 4C8B442440  mov   r8 = [rsp+64]                  // use/loop/use #1
+0322 33D2        xor   rdx,rdx
+0324 E8D7FCFFFF  call  copy_array  rcx  rdx  r8  r9
+0329 4C8B4C2430  mov   r9 = [rsp+48]                  // use/loop/use #0
+032E 488B4C2438  mov   rcx = [rsp+56]                 // use/loop/use #1
+0333 4C8B442440  mov   r8 = [rsp+64]                  // use/loop/use #1
+0338 33D2        xor   rdx,rdx
+033A E881FDFFFF  call  split_merge  rcx  rdx  r8  r9
+033F 4883C428C3  addi  rsp += #40
+0344             ret
 ---{ *![int] *![int] int -> int #0}---------------------------
 
 
diff --git a/seaofnodes/src/test/cases/mergsort/sort_smp.ir b/seaofnodes/src/test/cases/mergsort/sort_smp.ir
new file mode 100644
index 0000000..a22f77a
--- /dev/null
+++ b/seaofnodes/src/test/cases/mergsort/sort_smp.ir
@@ -0,0 +1,329 @@
+START:                            [[   ]]
+   7 Start   ____ ____ ____       [[    15   18   22   27   31   45    6   37  189  158   62   85  146  ]]  [  Ctrl, #TOP, int]
+
+L6:                               [[  START      ]]
+   6 sys.ary ____    7            [[    14   17   21   24   26   29   30  241    5  ]]  Ctrl
+  14 len        6   15            [[     9       ]]  int
+  17 $mem       6   18            [[    16   19  ]]  #BOT
+  30 $rpc       6   31            [[     2       ]]  $[ALL]
+  29 soff       6   15            [[    28       ]]  int
+  26 src        6   27            [[    25       ]]  *[u8]
+  24 doff       6   15            [[    23       ]]  int
+  21 dst        6   22            [[    20    2  ]]  *[u8]
+ 241 #0         6                 [[    11       ]]  0
+
+L189:                             [[  START      ]]
+ 189 merge   ____    7            [[   206  209  212  219  222  226  240  242   56  188  ]]  Ctrl
+ 206 begin    189   15            [[   205  223  ]]  int
+ 209 middle   189   15            [[   203  213  ]]  int
+ 212 end      189   15            [[   210  237  ]]  int
+ 240 $rpc     189   31            [[   185       ]]  $[ALL]
+ 226 a        189   45            [[   225  229  216  230  ]]  *[int]
+ 222 b        189   45            [[   221  227  ]]  *[int]
+ 219 $mem     189   18            [[   218  239  ]]  #BOT
+  56 #1       189                 [[   236  236  ]]  1
+ 242 #0       189                 [[   185  236  236  ]]  0
+
+L37:                              [[  START      ]]
+  37 eq      ____    7            [[    43   44   49   52   57   12   36  ]]  Ctrl
+  43 $mem      37   18            [[    41   48   54  ]]  #BOT
+  44 a         37   45            [[    41       ]]  *[int]
+  57 $rpc      37   31            [[    32       ]]  $[ALL]
+  52 n         37   15            [[    50       ]]  int
+  49 b         37   45            [[    48       ]]  *[int]
+  12 #0        37                 [[    55   46  ]]  0
+
+L62:                              [[  START      ]]
+  62 copy_ar ____    7            [[    67   69   71   74   76   77   61  ]]  Ctrl
+  67 begin     62   15            [[    66       ]]  int
+  77 $rpc      62   31            [[    58       ]]  $[ALL]
+  76 a         62   45            [[    75       ]]  *[int]
+  74 b         62   45            [[    73       ]]  *[int]
+  71 $mem      62   18            [[    70   72  ]]  #BOT
+  69 end       62   15            [[    64       ]]  int
+
+L85:                              [[  START      ]]
+  85 main    ____    7            [[    87  138   91  134   97  108  107  106  105  104  103  102  101  100   95   99   94  120  119  118  117  116  115  114  113  112   92  111   90  132  131  130  129  128  127  126  125  124   88  123   86   84  ]]  Ctrl
+  87 $mem      85   18            [[    86   97   97  ]]  #BOT
+ 138 $rpc      85   31            [[    78       ]]  $[ALL]
+ 108 st8       85  109   98 ____ ____  [[   107  ]]  #19:int
+ 107 st8       85  108   98 ____ ____  [[   106  ]]  #19:int
+ 132 st8       85  133  122 ____ ____  [[   131  ]]  #19:int
+ 106 st8       85  107   98 ____ ____  [[   105  ]]  #19:int
+ 131 st8       85  132  122 ____ ____  [[   130  ]]  #19:int
+ 105 st8       85  106   98 ____ ____  [[   104  ]]  #19:int
+ 120 st8       85  121  110 ____ ____  [[   119  ]]  #19:int
+ 130 st8       85  131  122 ____ ____  [[   129  ]]  #19:int
+ 104 st8       85  105   98 ____ ____  [[   103  ]]  #19:int
+ 129 st8       85  130  122 ____ ____  [[   128  ]]  #19:int
+ 119 st8       85  120  110 ____ ____  [[   118  ]]  #19:int
+ 103 st8       85  104   98 ____ ____  [[   102  ]]  #19:int
+ 128 st8       85  129  122 ____ ____  [[   127  ]]  #19:int
+ 118 st8       85  119  110 ____ ____  [[   117  ]]  #19:int
+ 127 st8       85  128  122 ____ ____  [[   126  ]]  #19:int
+ 102 st8       85  103   98 ____ ____  [[   101  ]]  #19:int
+ 117 st8       85  118  110 ____ ____  [[   116  ]]  #19:int
+ 126 st8       85  127  122 ____ ____  [[   125  ]]  #19:int
+ 101 st8       85  102   98 ____ ____  [[   100  ]]  #19:int
+ 116 st8       85  117  110 ____ ____  [[   115  ]]  #19:int
+ 125 st8       85  126  122 ____ ____  [[   124  ]]  #19:int
+ 115 st8       85  116  110 ____ ____  [[   114  ]]  #19:int
+ 100 st8       85  101   98 ____ ____  [[    99  ]]  #19:int
+ 124 st8       85  125  122 ____ ____  [[   123  ]]  #19:int
+ 114 st8       85  115  110 ____ ____  [[   113  ]]  #19:int
+  99 st8       85  100   98 ____ ____  [[    94  ]]  #19:int
+  95 st4       85   96   98 ____ ____  [[    94  ]]  #18:u32
+ 123 st8       85  124  122 ____ ____  [[    86  ]]  #19:int
+  88 st4       85   89  122 ____ ____  [[    86  ]]  #18:u32
+ 113 st8       85  114  110 ____ ____  [[   112  ]]  #19:int
+  94 new_ary   85   91   95   99  [[    93  110  121  ]]  [  Ctrl, *[int]!, #18:u32, #19:int]
+  93 $18       94                 [[    92       ]]  #18:u32
+ 110 [int]     94                 [[    92  120  119  118  117  116  115  114  113  112  111   84  ]]  *[int]
+ 121 $19       94                 [[   120       ]]  #19:int
+  86 ALLMEM    85   87 ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____   88  123  [[    84  ]]  #BOT
+ 112 st8       85  113  110 ____ ____  [[   111  ]]  #19:int
+  84 call      85   86   98  110  134  [[    83  ]]  Ctrl
+  92 st4       85   93  110 ____ ____  [[    90  ]]  #18:u32
+ 111 st8       85  112  110 ____ ____  [[    90  ]]  #19:int
+  91 #88       85                 [[    90   94   97  ]]  88
+  90 new_ary   85   91   92  111  [[    89  122  133  ]]  [  Ctrl, *[int]!, #18:u32, #19:int]
+  89 $18       90                 [[    88       ]]  #18:u32
+ 122 [int]     90                 [[    88  132  131  130  129  128  127  126  125  124  123   81  ]]  *[int]
+ 133 $19       90                 [[   132       ]]  #19:int
+  97 new_ary   85   91   87   87  [[    96   98  109  ]]  [  Ctrl, *[int]!, #18:u32, #19:int]
+  96 $18       97                 [[    95       ]]  #18:u32
+  98 [int]     97                 [[    95  108  107  106  105  104  103  102  101  100   99   84   81  ]]  *[int]
+ 109 $19       97                 [[   108       ]]  #19:int
+ 134 #10       85                 [[    84   81  ]]  10
+
+L158:                             [[  START      ]]
+ 158 split_m ____    7            [[   162  163  174  175  179  184  161  159  157  ]]  Ctrl
+ 162 end      158   15            [[   161  177  169  166  ]]  int
+ 163 begin    158   15            [[   161  172  177  166  ]]  int
+ 174 $mem     158   18            [[   172  182  ]]  #BOT
+ 175 a        158   45            [[   172  169  166  ]]  *[int]
+ 184 $rpc     158   31            [[   154       ]]  $[ALL]
+ 179 b        158   45            [[   172  169  166  ]]  *[int]
+ 161 sub      158  162  163       [[   159       ]]  int
+ 159 cmp      158  161            [[   157       ]]  bool
+ 157 j<=      158  159            [[   156  173  ]]  [  Ctrl, Ctrl]
+
+L146:                             [[  START      ]]
+ 146 merge_s ____    7            [[   147  148  149  150  153  244  145  ]]  Ctrl
+ 147 $mem     146   18            [[   145       ]]  #BOT
+ 148 a        146   45            [[   145  142  ]]  *[int]
+ 149 n        146   15            [[   145  142  ]]  int
+ 153 $rpc     146   31            [[   139       ]]  $[ALL]
+ 150 b        146   45            [[   145  142  ]]  *[int]
+ 145 call     146  147  148  244  149  150  [[   144  ]]  Ctrl
+ 244 #0       146                 [[   139  142  145  ]]  0
+
+LOOP5:                            [[  L6        L8         ]]
+   5 Loop    ____    6    8       [[    11   19    9    4  ]]  Ctrl
+  11 Phi_i      5  241   13       [[     9   13   23   28  ]]  int
+  19 Phi_$14    5   17   20       [[    16   20   25  ]]  #BOT
+   9 cmp        5   11   14       [[     4       ]]  bool
+   4 j<         5    9            [[     3    8  ]]  [  Ctrl, Ctrl]
+
+LOOP188:                          [[  L189      L190       ]]
+ 188 Loop    ____  189  190       [[   205  213  218  223  237  187  ]]  Ctrl
+ 205 Phi_i    188  206  207       [[   203  208  207  225  216  ]]  int
+ 213 Phi_j    188  209  214       [[   210  214  215  229  230  ]]  int
+ 218 Phi_$19  188  219  220       [[   216  221  225  227  229  230  239  ]]  #BOT
+ 223 Phi_k    188  206  224       [[   221  224  227  237  ]]  int
+ 237 cmp      188  223  212       [[   187       ]]  bool
+ 187 j<       188  237            [[   186  202  ]]  [  Ctrl, Ctrl]
+
+LOOP36:                           [[  L37       L38        ]]
+  36 Loop    ____   37   38       [[    46   50   35  ]]  Ctrl
+  46 Phi_i     36   12   47       [[    41   47   48   50  ]]  int
+  50 cmp       36   46   52       [[    35       ]]  bool
+  35 j<        36   50            [[    34   40  ]]  [  Ctrl, Ctrl]
+
+LOOP61:                           [[  L62       L63        ]]
+  61 Loop    ____   62   63       [[    66   72   64   60  ]]  Ctrl
+  66 Phi_k     61   67   68       [[    64   68   73   75  ]]  int
+  72 Phi_$19   61   71   73       [[    70   73   75  ]]  #BOT
+  64 cmp       61   66   69       [[    60       ]]  bool
+  60 j<        61   64            [[    59   63  ]]  [  Ctrl, Ctrl]
+
+L173:                             [[  L158       ]]
+ 173 False    157                 [[   177  178  176  172  ]]  Ctrl
+ 172 call     173  174  175  163  176  179  [[   171  ]]  Ctrl
+ 177 add      173  162  163       [[   176       ]]  int
+ 178 #2       173                 [[   176       ]]  2
+ 176 div      173  177  178       [[   172  169  166  ]]  int
+
+L83:                              [[  L85        ]]
+  83 CallEnd   84                 [[    82  135  ]]  [  Ctrl, #BOT, 0]
+ 135 $mem      83                 [[    81       ]]  #BOT
+
+L156:                             [[  L158       ]]
+ 156 True     157                 [[   155       ]]  Ctrl
+
+L144:                             [[  L146       ]]
+ 144 CallEnd  145                 [[   143  151  ]]  [  Ctrl, #BOT, 0]
+ 151 $mem     144                 [[   142       ]]  #BOT
+
+L3:                               [[  LOOP5      ]]
+   3 False      4                 [[    16    2  ]]  Ctrl
+  16 ALLMEM     3   17 ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____   19  [[     2  ]]  #BOT
+   2 Return     3   16   21   30  [[     1       ]]  [  Ctrl, #BOT, *[u8]!]
+
+L186:                             [[  LOOP188    ]]
+ 186 False    187                 [[   239  185  ]]  Ctrl
+ 239 ALLMEM   186  219 ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____  218  [[   185  ]]  #BOT
+ 185 Return   186  239  242  240  [[     1       ]]  [  Ctrl, #BOT, 0]
+
+L8:                               [[  LOOP5      ]]
+   8 True       4                 [[    28   25   23   20   13    5  ]]  Ctrl
+  28 add        8   11   29       [[    25       ]]  int
+  13 inc        8   11            [[    11       ]]  int
+  25 ld1        8   19   26   28  [[    20       ]]  u8
+  23 add        8   11   24       [[    20       ]]  int
+  20 st1        8   19   21   23   25  [[    19  ]]  #14:Bot
+
+L143:                             [[  L144       ]]
+ 143 $ctrl    144                 [[   142       ]]  Ctrl
+ 142 call     143  151  148  244  149  150  [[   141  ]]  Ctrl
+
+L34:                              [[  LOOP36     ]]
+  34 False     35                 [[   246   33  ]]  Ctrl
+ 246 #1        34                 [[    55       ]]  1
+
+L40:                              [[  LOOP36     ]]
+  40 True      35                 [[    48   41   39  ]]  Ctrl
+  48 ld8       40   43   49   46  [[    41       ]]  int
+  41 cmp8      40   43   44   46   48  [[    39  ]]  bool
+  39 j==       40   41            [[    38   53  ]]  [  Ctrl, Ctrl]
+
+L82:                              [[  L83        ]]
+  82 $ctrl     83                 [[    81       ]]  Ctrl
+  81 call      82  135   98  122  134  [[    80  ]]  Ctrl
+
+L202:                             [[  LOOP188    ]]
+ 202 True     187                 [[   203  201  ]]  Ctrl
+ 203 cmp      202  205  209       [[   201       ]]  bool
+ 201 j<       202  203            [[   200  231  ]]  [  Ctrl, Ctrl]
+
+L59:                              [[  LOOP61     ]]
+  59 False     60                 [[    70  245   58  ]]  Ctrl
+  70 ALLMEM    59   71 ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____   72  [[    58  ]]  #BOT
+ 245 #0        59                 [[    58       ]]  0
+  58 Return    59   70  245   77  [[     1       ]]  [  Ctrl, #BOT, 0]
+
+L63:                              [[  LOOP61     ]]
+  63 True      60                 [[    75   73   68   61  ]]  Ctrl
+  68 inc       63   66            [[    66       ]]  int
+  75 ld8       63   72   76   66  [[    73       ]]  int
+  73 st8       63   72   74   66   75  [[    72  ]]  #19:Bot
+
+L171:                             [[  L173       ]]
+ 171 CallEnd  172                 [[   170  180  ]]  [  Ctrl, #BOT, 0]
+ 180 $mem     171                 [[   169       ]]  #BOT
+
+L141:                             [[  L143       ]]
+ 141 CallEnd  142                 [[   140  152  ]]  [  Ctrl, #BOT, 0]
+ 152 $mem     141                 [[   139       ]]  #BOT
+
+L80:                              [[  L82        ]]
+  80 CallEnd   81                 [[    79  136  137  ]]  [  Ctrl, #BOT, bool]
+ 136 $mem      80                 [[    78       ]]  #BOT
+ 137 #2        80                 [[    78       ]]  bool
+
+L170:                             [[  L171       ]]
+ 170 $ctrl    171                 [[   169       ]]  Ctrl
+ 169 call     170  180  175  176  162  179  [[   168  ]]  Ctrl
+
+L231:                             [[  L202       ]]
+ 231 False    201                 [[   195       ]]  Ctrl
+
+L200:                             [[  L202       ]]
+ 200 True     201                 [[   210  199  ]]  Ctrl
+ 210 cmp      200  212  213       [[   199       ]]  bool
+ 199 j<=      200  210            [[   198  232  ]]  [  Ctrl, Ctrl]
+
+L38:                              [[  L40        ]]
+  38 True      39                 [[    47   36  ]]  Ctrl
+  47 inc       38   46            [[    46       ]]  int
+
+L53:                              [[  L40        ]]
+  53 False     39                 [[    33       ]]  Ctrl
+
+L140:                             [[  L141       ]]
+ 140 $ctrl    141                 [[   139       ]]  Ctrl
+ 139 Return   140  152  244  153  [[     1       ]]  [  Ctrl, #BOT, 0]
+
+L33:                              [[  L34       L53        ]]
+  33 Region  ____   34   53       [[    55   54   32  ]]  Ctrl
+  55 Phi_res   33  246   12       [[    32       ]]  bool
+  54 ALLMEM    33   43 ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____  [[    32  ]]  #BOT
+  32 Return    33   54   55   57  [[     1       ]]  [  Ctrl, #BOT, bool]
+
+L79:                              [[  L80        ]]
+  79 $ctrl     80                 [[    78       ]]  Ctrl
+  78 Return    79  136  137  138  [[     1       ]]  [  Ctrl, #BOT, bool]
+
+L198:                             [[  L200       ]]
+ 198 False    199                 [[   230  216  197  ]]  Ctrl
+ 230 ld8      198  218  226  213  [[   216       ]]  int
+ 216 cmp8     198  218  226  205  230  [[   197  ]]  bool
+ 197 j<=      198  216            [[   196  233  ]]  [  Ctrl, Ctrl]
+
+L168:                             [[  L170       ]]
+ 168 CallEnd  169                 [[   167  181  ]]  [  Ctrl, #BOT, 0]
+ 181 $mem     168                 [[   166       ]]  #BOT
+
+L232:                             [[  L200       ]]
+ 232 True     199                 [[   195       ]]  Ctrl
+
+L167:                             [[  L168       ]]
+ 167 $ctrl    168                 [[   166       ]]  Ctrl
+ 166 call     167  181  179  163  176  162  175  [[   165  ]]  Ctrl
+
+L233:                             [[  L198       ]]
+ 233 False    197                 [[   195       ]]  Ctrl
+
+L196:                             [[  L198       ]]
+ 196 True     197                 [[   195       ]]  Ctrl
+
+L165:                             [[  L167       ]]
+ 165 CallEnd  166                 [[   164  183  ]]  [  Ctrl, #BOT, 0]
+ 183 $mem     165                 [[   182       ]]  #BOT
+
+L195:                             [[  L196      L231      L232      L233       ]]
+ 195 Region  ____  196  231  232  233  [[   236  234  194  ]]  Ctrl
+ 236 Phi_con  195   56  242   56  242  [[   234  ]]  bool
+ 234 test     195  236            [[   194       ]]
+ 194 j!=      195  234            [[   191  228  ]]  [  Ctrl, Ctrl]
+
+L164:                             [[  L165       ]]
+ 164 $ctrl    165                 [[   155       ]]  Ctrl
+
+L191:                             [[  L195       ]]
+ 191 True     194                 [[   225  221  208  190  ]]  Ctrl
+ 208 inc      191  205            [[   207       ]]  int
+ 225 ld8      191  218  226  205  [[   221       ]]  int
+ 221 st8      191  218  222  223  225  [[   220  ]]  #19:Bot
+
+L155:                             [[  L156      L164       ]]
+ 155 Region  ____  156  164       [[   182  243  154  ]]  Ctrl
+ 182 Phi_$me  155  174  183       [[   154       ]]  #BOT
+ 243 #0       155                 [[   154       ]]  0
+ 154 Return   155  182  243  184  [[     1       ]]  [  Ctrl, #BOT, 0]
+
+L228:                             [[  L195       ]]
+ 228 False    194                 [[   229  227  215  190  ]]  Ctrl
+ 215 inc      228  213            [[   214       ]]  int
+ 229 ld8      228  218  226  213  [[   227       ]]  int
+ 227 st8      228  218  222  223  229  [[   220  ]]  #19:Bot
+
+L190:                             [[  L191      L228       ]]
+ 190 Region  ____  191  228       [[   207  214  220  224  188  ]]  Ctrl
+ 207 Phi_i    190  208  205       [[   205       ]]  int
+ 214 Phi_j    190  213  215       [[   213       ]]  int
+ 220 Phi_$19  190  221  227       [[   218       ]]  #19:Bot
+ 224 inc      190  223            [[   223       ]]  int
+
+L1:                               [[  L79       L140      L155      L186       ]]
+   1 Stop       2   32   58   78  139  154  185  [[   ]]  Bot
\ No newline at end of file
diff --git a/seaofnodes/src/test/cases/mergsort/sort_smp.out b/seaofnodes/src/test/cases/mergsort/sort_smp.out
index c06e3ab..bd574a0 100644
--- a/seaofnodes/src/test/cases/mergsort/sort_smp.out
+++ b/seaofnodes/src/test/cases/mergsort/sort_smp.out
@@ -1,253 +1,297 @@
----split_merge { *![int] int int *![int] -> 0 #0}---------------------------
-0000 4883EC38    subi  rsp -= #56
-                 a:r9,b:rcx,begin:rdx,end:r8
-0004 4889542428  mov   [rsp+40] = rdx                 // def/loop #1
-0009 4C89442458  mov   [rsp+88] = r8                  // def/loop #0
-000E 48894C2450  mov   [rsp+80] = rcx                 // def/loop #0
-0013 4C894C2448  mov   [rsp+72] = r9                  // def/loop #0
-0018 4C8B442458  mov   r8 = [rsp+88]                  // use/loop/use #0
-001D 4C8B542428  mov   r10 = [rsp+40]                 // use/loop/use #1
-0022 4D2BC2      sub   r8 -= r10
-0025 4983F801    cmp   r8, #1
-0029 7E6F        j<=   L90                            // L98
-L98:
-002B 488B442458  mov   rax = [rsp+88]                 // use/loop/use #0
-0030 488B542428  mov   rdx = [rsp+40]                 // use/loop/use #1
-0035 4803C2      add   rax += rdx
-0038 B902000000  ldi   rcx = #2
-003D 489948F7F9  div   rax = rax / rcx                // kill rdx
-0042 4889442440  mov   [rsp+64] = rax                 // def/loop #0
-0047 488B4C2448  mov   rcx = [rsp+72]                 // use/loop/use #0
-004C 4C8B4C2450  mov   r9 = [rsp+80]                  // use/loop/use #0
-0051 4C8B442440  mov   r8 = [rsp+64]                  // use/loop/use #0
-0056 488B542428  mov   rdx = [rsp+40]                 // use/loop/use #1
-005B E8A0FFFFFF  call  split_merge  rcx  rdx  r8  r9
-0060 488B4C2448  mov   rcx = [rsp+72]                 // use/loop/use #0
-0065 4C8B4C2450  mov   r9 = [rsp+80]                  // use/loop/use #0
-006A 4C8B442458  mov   r8 = [rsp+88]                  // use/loop/use #0
-006F 488B542440  mov   rdx = [rsp+64]                 // use/loop/use #0
-0074 E887FFFFFF  call  split_merge  rcx  rdx  r8  r9
-0079 FF7424488F  mov   [rsp+32] = [rsp+72]            // use/loop/use #0
-007E 442420
-0081 488B4C2450  mov   rcx = [rsp+80]                 // use/loop/use #0
-0086 4C8B4C2458  mov   r9 = [rsp+88]                  // use/loop/use #0
-008B 4C8B442440  mov   r8 = [rsp+64]                  // use/loop/use #0
-0090 488B542428  mov   rdx = [rsp+40]                 // use/loop/use #1
-0095 E836000000  call  merge  rcx  rdx  r8  r9  [stk#42]
-L91:
-L90:
-009A 33C0        xor   rax,rax
-009C 4883C438C3  addi  rsp += #56
-00A1             ret
----{ *![int] int int *![int] -> 0 #0}---------------------------
+---split_merge { *[int]! int int *[int]! -> 0 #0}---------------------------
+0220 4883EC38    subi  rsp -= #56
+                 end:r8,begin:rdx,a:r9,b:rcx
+0224 4889542428  mov   [rsp+40] = rdx                 // def/loop #1
+0229 48894C2458  mov   [rsp+88] = rcx                 // def/loop #0
+022E 4C894C2450  mov   [rsp+80] = r9                  // def/loop #0
+0233 4C89442448  mov   [rsp+72] = r8                  // def/loop #0
+0238 4C8B542428  mov   r10 = [rsp+40]                 // use/loop/use #1
+023D 4D2BC2      sub   r8 -= r10
+0240 4983F801    cmp   r8, #1
+0244 7E6F        j<=   L155                           // L173
+L173:
+0246 488B442448  mov   rax = [rsp+72]                 // use/loop/use #0
+024B 488B542428  mov   rdx = [rsp+40]                 // use/loop/use #1
+0250 4803C2      add   rax += rdx
+0253 B902000000  ldi   rcx = #2
+0258 489948F7F9  div   rax = rax / rcx                // kill rdx
+025D 4889442440  mov   [rsp+64] = rax                 // def/loop #0
+0262 488B4C2450  mov   rcx = [rsp+80]                 // use/loop/use #0
+0267 4C8B4C2458  mov   r9 = [rsp+88]                  // use/loop/use #0
+026C 4C8B442440  mov   r8 = [rsp+64]                  // use/loop/use #0
+0271 488B542428  mov   rdx = [rsp+40]                 // use/loop/use #1
+0276 E8A5FFFFFF  call  split_merge  rcx  rdx  r8  r9
+027B 4C8B442448  mov   r8 = [rsp+72]                  // use/loop/use #0
+0280 488B4C2450  mov   rcx = [rsp+80]                 // use/loop/use #0
+0285 4C8B4C2458  mov   r9 = [rsp+88]                  // use/loop/use #0
+028A 488B542440  mov   rdx = [rsp+64]                 // use/loop/use #0
+028F E88CFFFFFF  call  split_merge  rcx  rdx  r8  r9
+0294 4C8B4C2448  mov   r9 = [rsp+72]                  // use/loop/use #0
+0299 FF7424508F  mov   [rsp+32] = [rsp+80]            // use/loop/use #0
+029E 442420
+02A1 488B4C2458  mov   rcx = [rsp+88]                 // use/loop/use #0
+02A6 4C8B442440  mov   r8 = [rsp+64]                  // use/loop/use #0
+02AB 488B542428  mov   rdx = [rsp+40]                 // use/loop/use #1
+02B0 E80B000000  call  merge  rcx  rdx  r8  r9  [stk#42]
+L156:
+L155:
+02B5 33C0        xor   rax,rax
+02B7 4883C438C3  addi  rsp += #56
+02BC             ret
+---{ *[int]! int int *[int]! -> 0 #0}---------------------------
 
----copy_array { *![int] int int *![int] -> 0 #1}---------------------------
+---copy_array { *[int]! int int *[int]! -> 0 #1}---------------------------
                  begin:rdx,end:r8,b:r9,a:rcx
-LOOP69:
+LOOP61:
                  k:rdx
-00B0 493BD0      cmp   rdx, r8
-00B3 7D10        j>=   L67                            // L71
-L71:
-00B5 488B44D108  ld8   rax,[rcx+rdx*8+8]
-00BA 498944D108  st8   [r9+rdx*8+8],rax
-00BF 4883C201    inc   rdx += #1
-00C3 EBEB        jmp   LOOP69
-L425:
-L67:
-00C5 33C0        xor   rax,rax
-00C7 C3          ret
----{ *![int] int int *![int] -> 0 #1}---------------------------
+0200 493BD0      cmp   rdx, r8
+0203 7D0F        j>=   L59                            // L63
+L63:
+0205 488B44D108  ld8   rax,[rcx+rdx*8+8]
+020A 498944D108  st8   [r9+rdx*8+8],rax
+020F 48FFC2      inc   rdx += #1
+0212 EBEC        jmp   LOOP61
+L485:
+L59:
+0214 33C0        xor   rax,rax
+0216 C3          ret
+---{ *[int]! int int *[int]! -> 0 #1}---------------------------
 
----merge { *![int] int int int *![int] -> 0 #0}---------------------------
+---merge { *[int]! int int int *[int]! -> 0 #0}---------------------------
                  begin:rdx,middle:r8,end:r9,b:rcx,a:[rsp+40]
-00D0 66480F6EC3  mov   xmm0 = rbx                     // def/loop #2
-00D5 488B5C2428  mov   rbx = [rsp+40]                 // def/empty1 #0
-00DA 4C8BDA      mov   r11 = rdx                      // use/self/phi #1
-00DD 4D8BD0      mov   r10 = r8                       // use/self/use #1
-LOOP120:
+02C0 66480F6EC3  mov   xmm0 = rbx                     // def/loop #3
+02C5 488B5C2428  mov   rbx = [rsp+40]                 // def/empty1 #0
+02CA 4C8BDA      mov   r11 = rdx                      // use/self/phi #1
+02CD 4D8BD0      mov   r10 = r8                       // use/self/use #1
+LOOP188:
                  i:r11,j:r10,k:rdx
-00E0 493BD1      cmp   rdx, r9
-00E3 7D57        j>=   L118                           // L134
-L134:
-00E5 4D3BD8      cmp   r11, r8
-00E8 7C04        j<    L132                           // L163
-L163:
-00EA 33C0        xor   rax,rax
-00EC EB24        jmp   L127
-L424:
-L132:
-00EE 4D3BCA      cmp   r9, r10
-00F1 7E1A        j<=   L164                           // L130
-L130:
-00F3 488BC3      mov   rax = rbx                      // use/empty1 #0
-00F6 4A8B44D008  ld8   rax,[rax+r10*8+8]
-00FB 4A3944DB08  cmp8  rax, [rbx+r11*8+8]
-0100 7E04        j<=   L128                           // L165
-L165:
-0102 33C0        xor   rax,rax
-0104 EB0C        jmp   L127
-L423:
-L128:
-0106 B801000000  ldi   rax = #1
-010B EB05        jmp   L127
-L422:
-L164:
-010D B801000000  ldi   rax = #1
-L127:
+02D0 493BD1      cmp   rdx, r9
+02D3 7D54        j>=   L186                           // L202
+L202:
+02D5 4D3BD8      cmp   r11, r8
+02D8 7C04        j<    L200                           // L231
+L231:
+02DA 33C0        xor   rax,rax
+02DC EB24        jmp   L195
+L484:
+L200:
+02DE 4D3BCA      cmp   r9, r10
+02E1 7E1A        j<=   L232                           // L198
+L198:
+02E3 488BC3      mov   rax = rbx                      // use/empty1 #0
+02E6 4A8B44D008  ld8   rax,[rax+r10*8+8]
+02EB 4A3944DB08  cmp8  rax, [rbx+r11*8+8]
+02F0 7E04        j<=   L196                           // L233
+L233:
+02F2 33C0        xor   rax,rax
+02F4 EB0C        jmp   L195
+L483:
+L196:
+02F6 B801000000  ldi   rax = #1
+02FB EB05        jmp   L195
+L482:
+L232:
+02FD B801000000  ldi   rax = #1
+L195:
                  cond:rax
-0112 4883F800    test  rax
-0116 7510        j!=   L123                           // L160
-L160:
-0118 4A8B44D308  ld8   rax,[rbx+r10*8+8]
-011D 488944D108  st8   [rcx+rdx*8+8],rax
-0122 4983C201    inc   r10 += #1
-0126 EB0E        jmp   L122
-L421:
-L123:
-0128 4A8B44DB08  ld8   rax,[rbx+r11*8+8]
-012D 488944D108  st8   [rcx+rdx*8+8],rax
-0132 4983C301    inc   r11 += #1
-L122:
+0302 4883F800    test  rax
+0306 750F        j!=   L191                           // L228
+L228:
+0308 4A8B44D308  ld8   rax,[rbx+r10*8+8]
+030D 488944D108  st8   [rcx+rdx*8+8],rax
+0312 49FFC2      inc   r10 += #1
+0315 EB0D        jmp   L190
+L481:
+L191:
+0317 4A8B44DB08  ld8   rax,[rbx+r11*8+8]
+031C 488944D108  st8   [rcx+rdx*8+8],rax
+0321 49FFC3      inc   r11 += #1
+L190:
                  i:r11,j:r10
-0136 4883C201    inc   rdx += #1
-013A EBA4        jmp   LOOP120
-L420:
-L118:
-013C 66480F7EC3  mov   rbx = xmm0                     // use/loop/use #2
-0141 33C0        xor   rax,rax
-0143 C3          ret
----{ *![int] int int int *![int] -> 0 #0}---------------------------
+0324 48FFC2      inc   rdx += #1
+0327 EBA7        jmp   LOOP188
+L480:
+L186:
+0329 66480F7EC3  mov   rbx = xmm0                     // use/loop/use #3
+032E 33C0        xor   rax,rax
+0330 C3          ret
+---{ *[int]! int int int *[int]! -> 0 #0}---------------------------
 
----eq { *![int] *![int] int -> bool #1}---------------------------
+---eq { *[int]! *[int]! int -> bool #1}---------------------------
                  a:rcx,b:rdx,n:r8
-0150 33C0        xor   rax,rax
-LOOP189:
+0340 33C0        xor   rax,rax
+LOOP36:
                  i:rax
-0152 493BC0      cmp   rax, r8
-0155 7D16        j>=   L187                           // L193
-L193:
-0157 4C8B4CC208  ld8   r9,[rdx+rax*8+8]
-015C 4C394CC108  cmp8  r9, [rcx+rax*8+8]
-0161 7506        j!=   L205                           // L191
-L191:
-0163 4883C001    inc   rax += #1
-0167 EBE9        jmp   LOOP189
-L419:
-L205:
-0169 33C0        xor   rax,rax
-016B EB05        jmp   L186
-L418:
-L187:
-016D B801000000  ldi   rax = #1
-L186:
+0342 493BC0      cmp   rax, r8
+0345 7D15        j>=   L34                            // L40
+L40:
+0347 4C8B4CC208  ld8   r9,[rdx+rax*8+8]
+034C 4C394CC108  cmp8  r9, [rcx+rax*8+8]
+0351 7505        j!=   L53                            // L38
+L38:
+0353 48FFC0      inc   rax += #1
+0356 EBEA        jmp   LOOP36
+L479:
+L53:
+0358 33C0        xor   rax,rax
+035A EB05        jmp   L33
+L478:
+L34:
+035C B801000000  ldi   rax = #1
+L33:
                  result:rax
-0172 C3          ret
----{ *![int] *![int] int -> bool #1}---------------------------
+0361 C3          ret
+---{ *[int]! *[int]! int -> bool #1}---------------------------
 
----main { -> bool #0}---------------------------
-0180 4883EC28    subi  rsp -= #40
-0184 48895C2420  mov   [rsp+32] = rbx                 // def/loop #2
-0189 BA58000000  ldi   rdx = #88
-018E B901000000  alloc ldi   rcx = #1
-0193 E800000000  call  #calloc
-0198 4889442418  mov   [rsp+24] = rax                 // def/loop #0
-019D 4C8B4C2418  mov   r9 = [rsp+24]                  // use/loop/use #0
-01A2 49C741080A  st8   [r9+8],#10
-01A7 000000
-01AA 4C8B442418  mov   r8 = [rsp+24]                  // use/loop/use #0
-01AF 49C7401009  st8   [r8+16],#9
-01B4 000000
-01B7 488B542418  mov   rdx = [rsp+24]                 // use/loop/use #0
-01BC 48C7421808  st8   [rdx+24],#8
-01C1 000000
-01C4 488B4C2418  mov   rcx = [rsp+24]                 // use/loop/use #0
-01C9 48C7412007  st8   [rcx+32],#7
-01CE 000000
-01D1 488B442418  mov   rax = [rsp+24]                 // use/loop/use #0
-01D6 48C7402806  st8   [rax+40],#6
-01DB 000000
-01DE 4C8B5C2418  mov   r11 = [rsp+24]                 // use/loop/use #0
-01E3 49C7433005  st8   [r11+48],#5
-01E8 000000
-01EB 4C8B542418  mov   r10 = [rsp+24]                 // use/loop/use #0
-01F0 49C7423804  st8   [r10+56],#4
-01F5 000000
-01F8 4C8B4C2418  mov   r9 = [rsp+24]                  // use/loop/use #0
-01FD 49C7414003  st8   [r9+64],#3
-0202 000000
-0205 4C8B442418  mov   r8 = [rsp+24]                  // use/loop/use #0
-020A 49C7404802  st8   [r8+72],#2
-020F 000000
-0212 488B542418  mov   rdx = [rsp+24]                 // use/loop/use #0
-0217 C7020A0000  st4   [rdx],#10
-021C 00
-021D 488B4C2418  mov   rcx = [rsp+24]                 // use/loop/use #0
-0222 48C7415001  st8   [rcx+80],#1
-0227 000000
-022A BA58000000  ldi   rdx = #88
-022F B901000000  alloc ldi   rcx = #1
-0234 E800000000  call  #calloc
-0239 4889442410  mov   [rsp+16] = rax                 // def/empty1 #1
-023E C7000A0000  st4   [rax],#10
-0243 00
-0244 BA58000000  ldi   rdx = #88
-0249 B901000000  alloc ldi   rcx = #1
-024E E800000000  call  #calloc
-0253 488BD8      mov   rbx = rax                      // def/empty1 #0
-0256 48C7430801  st8   [rbx+8],#1
-025B 000000
-025E 48C7431002  st8   [rbx+16],#2
-0263 000000
-0266 48C7431803  st8   [rbx+24],#3
-026B 000000
-026E 48C7432004  st8   [rbx+32],#4
-0273 000000
-0276 48C7432805  st8   [rbx+40],#5
-027B 000000
-027E 48C7433006  st8   [rbx+48],#6
-0283 000000
-0286 48C7433807  st8   [rbx+56],#7
-028B 000000
-028E 48C7434008  st8   [rbx+64],#8
-0293 000000
-0296 48C7434809  st8   [rbx+72],#9
-029B 000000
-029E C7030A0000  st4   [rbx],#10
-02A3 00
-02A4 48C743500A  st8   [rbx+80],#10
-02A9 000000
-02AC 488B4C2418  mov   rcx = [rsp+24]                 // use/loop/use #0
-02B1 488B542410  mov   rdx = [rsp+16]                 // use/empty1 #0
-02B6 41B80A0000  ldi   r8 = #10
-02BB 00
-02BC E81F000000  call  merge_sort  rcx  rdx  r8
-02C1 488B4C2418  mov   rcx = [rsp+24]                 // use/loop/use #0
-02C6 488BD3      mov   rdx = rbx                      // use/empty1 #0
-02C9 41B80A0000  ldi   r8 = #10
-02CE 00
-02CF E87CFEFFFF  call  eq  rcx  rdx  r8
-02D4 488B5C2420  mov   rbx = [rsp+32]                 // use/loop/use #2
-02D9 4883C428C3  addi  rsp += #40
-02DE             ret
----{ -> bool #0}---------------------------
+---main { -> bool #1}---------------------------
+0050 4883EC28    subi  rsp -= #40
+0054 48895C2420  mov   [rsp+32] = rbx                 // def/loop #3
+0059 BA58000000  ldi   rdx = #88
+005E B901000000  alloc ldi   rcx = #1
+0063 E800000000  call  #calloc
+0068 4889442418  mov   [rsp+24] = rax                 // def/loop #0
+006D 4C8B4C2418  mov   r9 = [rsp+24]                  // use/loop/use #0
+0072 49C741080A  st8   [r9+8],#10
+0077 000000
+007A 4C8B442418  mov   r8 = [rsp+24]                  // use/loop/use #0
+007F 49C7401009  st8   [r8+16],#9
+0084 000000
+0087 488B542418  mov   rdx = [rsp+24]                 // use/loop/use #0
+008C 48C7421808  st8   [rdx+24],#8
+0091 000000
+0094 488B4C2418  mov   rcx = [rsp+24]                 // use/loop/use #0
+0099 48C7412007  st8   [rcx+32],#7
+009E 000000
+00A1 488B442418  mov   rax = [rsp+24]                 // use/loop/use #0
+00A6 48C7402806  st8   [rax+40],#6
+00AB 000000
+00AE 4C8B5C2418  mov   r11 = [rsp+24]                 // use/loop/use #0
+00B3 49C7433005  st8   [r11+48],#5
+00B8 000000
+00BB 4C8B542418  mov   r10 = [rsp+24]                 // use/loop/use #0
+00C0 49C7423804  st8   [r10+56],#4
+00C5 000000
+00C8 4C8B4C2418  mov   r9 = [rsp+24]                  // use/loop/use #0
+00CD 49C7414003  st8   [r9+64],#3
+00D2 000000
+00D5 4C8B442418  mov   r8 = [rsp+24]                  // use/loop/use #0
+00DA 49C7404802  st8   [r8+72],#2
+00DF 000000
+00E2 488B542418  mov   rdx = [rsp+24]                 // use/loop/use #0
+00E7 C7020A0000  st4   [rdx],#10
+00EC 00
+00ED 488B4C2418  mov   rcx = [rsp+24]                 // use/loop/use #0
+00F2 48C7415001  st8   [rcx+80],#1
+00F7 000000
+00FA BA58000000  ldi   rdx = #88
+00FF B901000000  alloc ldi   rcx = #1
+0104 E800000000  call  #calloc
+0109 4889442410  mov   [rsp+16] = rax                 // def/empty1 #1
+010E 48C7400800  st8   [rax+8],#0
+0113 000000
+0116 48C7401000  st8   [rax+16],#0
+011B 000000
+011E 48C7401800  st8   [rax+24],#0
+0123 000000
+0126 48C7402000  st8   [rax+32],#0
+012B 000000
+012E 48C7402800  st8   [rax+40],#0
+0133 000000
+0136 48C7403000  st8   [rax+48],#0
+013B 000000
+013E 48C7403800  st8   [rax+56],#0
+0143 000000
+0146 48C7404000  st8   [rax+64],#0
+014B 000000
+014E 48C7404800  st8   [rax+72],#0
+0153 000000
+0156 C7000A0000  st4   [rax],#10
+015B 00
+015C 48C7405000  st8   [rax+80],#0
+0161 000000
+0164 BA58000000  ldi   rdx = #88
+0169 B901000000  alloc ldi   rcx = #1
+016E E800000000  call  #calloc
+0173 488BD8      mov   rbx = rax                      // def/empty1 #0
+0176 48C7430801  st8   [rbx+8],#1
+017B 000000
+017E 48C7431002  st8   [rbx+16],#2
+0183 000000
+0186 48C7431803  st8   [rbx+24],#3
+018B 000000
+018E 48C7432004  st8   [rbx+32],#4
+0193 000000
+0196 48C7432805  st8   [rbx+40],#5
+019B 000000
+019E 48C7433006  st8   [rbx+48],#6
+01A3 000000
+01A6 48C7433807  st8   [rbx+56],#7
+01AB 000000
+01AE 48C7434008  st8   [rbx+64],#8
+01B3 000000
+01B6 48C7434809  st8   [rbx+72],#9
+01BB 000000
+01BE C7030A0000  st4   [rbx],#10
+01C3 00
+01C4 48C743500A  st8   [rbx+80],#10
+01C9 000000
+01CC 488B4C2418  mov   rcx = [rsp+24]                 // use/loop/use #0
+01D1 488B542410  mov   rdx = [rsp+16]                 // use/empty1 #0
+01D6 41B80A0000  ldi   r8 = #10
+01DB 00
+01DC E81FFEFFFF  call  merge_sort  rcx  rdx  r8
+01E1 488B4C2418  mov   rcx = [rsp+24]                 // use/loop/use #0
+01E6 488BD3      mov   rdx = rbx                      // use/empty1 #0
+01E9 41B80A0000  ldi   r8 = #10
+01EE 00
+01EF E84C010000  call  eq  rcx  rdx  r8
+01F4 488B5C2420  mov   rbx = [rsp+32]                 // use/loop/use #3
+01F9 4883C428C3  addi  rsp += #40
+01FE             ret
+---{ -> bool #1}---------------------------
 
----merge_sort { *![int] *![int] int -> 0 #0}---------------------------
-02E0 4883EC28    subi  rsp -= #40
+---merge_sort { *[int]! *[int]! int -> 0 #0}---------------------------
+0000 4883EC28    subi  rsp -= #40
                  a:rcx,n:r8,b:rdx
-02E4 4C89442440  mov   [rsp+64] = r8                  // def/loop #1
-02E9 48894C2438  mov   [rsp+56] = rcx                 // def/loop #1
-02EE 4889542430  mov   [rsp+48] = rdx                 // def/loop #0
-02F3 4C8B4C2430  mov   r9 = [rsp+48]                  // use/loop/use #0
-02F8 33D2        xor   rdx,rdx
-02FA 488B4C2438  mov   rcx = [rsp+56]                 // use/loop/use #1
-02FF 4C8B442440  mov   r8 = [rsp+64]                  // use/loop/use #1
-0304 E8A7FDFFFF  call  copy_array  rcx  rdx  r8  r9
-0309 4C8B4C2430  mov   r9 = [rsp+48]                  // use/loop/use #0
-030E 33D2        xor   rdx,rdx
-0310 488B4C2438  mov   rcx = [rsp+56]                 // use/loop/use #1
-0315 4C8B442440  mov   r8 = [rsp+64]                  // use/loop/use #1
-031A E8E1FCFFFF  call  split_merge  rcx  rdx  r8  r9
-031F 33C0        xor   rax,rax
-0321 4883C428C3  addi  rsp += #40
-0326             ret
----{ *![int] *![int] int -> 0 #0}---------------------------
+0004 4C89442440  mov   [rsp+64] = r8                  // def/loop #1
+0009 48894C2438  mov   [rsp+56] = rcx                 // def/loop #1
+000E 4889542430  mov   [rsp+48] = rdx                 // def/loop #0
+0013 4C8B4C2430  mov   r9 = [rsp+48]                  // use/loop/use #0
+0018 33D2        xor   rdx,rdx
+001A 488B4C2438  mov   rcx = [rsp+56]                 // use/loop/use #1
+001F 4C8B442440  mov   r8 = [rsp+64]                  // use/loop/use #1
+0024 E8D7010000  call  copy_array  rcx  rdx  r8  r9
+0029 4C8B4C2430  mov   r9 = [rsp+48]                  // use/loop/use #0
+002E 33D2        xor   rdx,rdx
+0030 488B4C2438  mov   rcx = [rsp+56]                 // use/loop/use #1
+0035 4C8B442440  mov   r8 = [rsp+64]                  // use/loop/use #1
+003A E8E1010000  call  split_merge  rcx  rdx  r8  r9
+003F 33C0        xor   rax,rax
+0041 4883C428C3  addi  rsp += #40
+0046             ret
+---{ *[int]! *[int]! int -> 0 #0}---------------------------
+
+---sys.ary.copy { *[u8]! int *[u8] int int -> *[u8]! #0}---------------------------
+                 len:[rsp+40],dst:rcx,doff:rdx,src:r8,soff:r9
+0370 4C8BD1      mov   r10 = rcx                      // def/empty1 #0
+0373 33C9        xor   rcx,rcx
+LOOP5:
+                 i:rcx
+0375 4C8B5C2428  mov   r11 = [rsp+40]                 // use/empty1 #0
+037A 493BCB      cmp   rcx, r11
+037D 7D1C        j>=   L3                             // L8
+L8:
+037F 488BC1      mov   rax = rcx                      // use/self/use #1
+0382 4903C1      add   rax += r9
+0385 490FB64400  ld1   rax,[r8+rax+4]
+038A 04
+038B 4C8BD9      mov   r11 = rcx                      // use/self/use #1
+038E 4C03DA      add   r11 += rdx
+0391 4388441A04  st1   [r10+r11+4],rax
+0396 48FFC1      inc   rcx += #1
+0399 EBDA        jmp   LOOP5
+L477:
+L3:
+039B 498BC2      mov   rax = r10                      // use/empty1 #0
+039E C3          ret
+---{ *[u8]! int *[u8] int int -> *[u8]! #0}---------------------------
diff --git a/seaofnodes/src/test/java/com/compilerprogramming/ezlang/compiler/Simple.java b/seaofnodes/src/test/java/com/compilerprogramming/ezlang/compiler/Simple.java
index bc6819b..297b53c 100644
--- a/seaofnodes/src/test/java/com/compilerprogramming/ezlang/compiler/Simple.java
+++ b/seaofnodes/src/test/java/com/compilerprogramming/ezlang/compiler/Simple.java
@@ -155,7 +155,7 @@ static String getInput() {
     public static void main(String[] args) throws Exception {
         String input_filename = null;
         boolean do_eval = false;
-        boolean do_run = true;
+        boolean do_run = false;
         boolean do_codegen = false;
         boolean do_print_size = false;
         boolean do_print_time = false;

From 4f936312c34ce8e387a9a0481ecbb6083bb93bd4 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Sat, 9 Aug 2025 23:01:28 +0100
Subject: [PATCH 099/119] update outputs for fib

---
 seaofnodes/src/test/cases/fib/README.md       |  9 ++
 seaofnodes/src/test/cases/fib/fib.ez_out      | 27 ++++++
 seaofnodes/src/test/cases/fib/fib.ir          | 89 +++++++++----------
 seaofnodes/src/test/cases/fib/fib.simple_ir   | 46 ----------
 .../test/cases/fib/{fib.simple => fib.smp}    |  0
 seaofnodes/src/test/cases/fib/fib.smp_ir      | 45 ++++++++++
 seaofnodes/src/test/cases/fib/fib.smp_out     | 27 ++++++
 7 files changed, 152 insertions(+), 91 deletions(-)
 create mode 100644 seaofnodes/src/test/cases/fib/README.md
 create mode 100644 seaofnodes/src/test/cases/fib/fib.ez_out
 delete mode 100644 seaofnodes/src/test/cases/fib/fib.simple_ir
 rename seaofnodes/src/test/cases/fib/{fib.simple => fib.smp} (100%)
 create mode 100644 seaofnodes/src/test/cases/fib/fib.smp_ir
 create mode 100644 seaofnodes/src/test/cases/fib/fib.smp_out

diff --git a/seaofnodes/src/test/cases/fib/README.md b/seaofnodes/src/test/cases/fib/README.md
new file mode 100644
index 0000000..a679926
--- /dev/null
+++ b/seaofnodes/src/test/cases/fib/README.md
@@ -0,0 +1,9 @@
+### Notes
+
+Flags being used:
+
+```
+--dump-after-local-sched -S --cpu x86_64_v2 --abi SystemV
+```
+
+Simple ch21 output because fails in ch22.
\ No newline at end of file
diff --git a/seaofnodes/src/test/cases/fib/fib.ez_out b/seaofnodes/src/test/cases/fib/fib.ez_out
new file mode 100644
index 0000000..d172c55
--- /dev/null
+++ b/seaofnodes/src/test/cases/fib/fib.ez_out
@@ -0,0 +1,27 @@
+---foo { -> int #0}---------------------------
+0030 4883EC08    subi  rsp -= #8
+0034 BF0A000000  ldi   rdi = #10
+0039 E8C2FFFFFF  call  fib  rdi
+003E 4883C408C3  addi  rsp += #8
+0043             ret
+---{ -> int #0}---------------------------
+
+---fib { int -> int #1}---------------------------
+                 n:rdi
+0000 BA01000000  ldi   rdx = #1
+0005 B901000000  ldi   rcx = #1
+LOOP5:
+                 i:rdi,f2:rdx,f1:rcx
+000A 488BC2      mov   rax = rdx                      // def/self #0
+000D 4883FF01    cmp   rdi, #1
+0011 7E0E        j<=   L3                             // L8
+L8:
+0013 48FFCF      dec   rdi += #-1
+0016 488BD0      mov   rdx = rax                      // use/self/use #0
+0019 4803D1      add   rdx += rcx
+001C 488BC8      mov   rcx = rax                      // use/self/use #0
+001F EBE9        jmp   LOOP5
+L51:
+L3:
+0021 C3          ret
+---{ int -> int #1}---------------------------
\ No newline at end of file
diff --git a/seaofnodes/src/test/cases/fib/fib.ir b/seaofnodes/src/test/cases/fib/fib.ir
index b0821d1..1c334cc 100644
--- a/seaofnodes/src/test/cases/fib/fib.ir
+++ b/seaofnodes/src/test/cases/fib/fib.ir
@@ -1,46 +1,45 @@
 START:                            [[   ]]
-   1 Start   ____ ____ ____       [[     3    4    9   16   18    8    5   46  ]]  [  Ctrl, #TOP, int]
-
-L8:                               [[  START      ]]
-   8 fib     ____    1            [[    10   17   19   20   22  ]]  Ctrl
-  10 $rpc       8    9            [[    14       ]]  $[ALL]
-  17 $mem       8   16            [[    14       ]]  #BOT
-  19 n$2        8   18            [[    25       ]]  int
-  20 #1         8                 [[    37   39   26   41  ]]  1
-
-L46:                              [[  START      ]]
-  46 foo     ____    1            [[    48   55   56   57   59  ]]  Ctrl
-  48 $rpc      46    9            [[    52       ]]  $[ALL]
-  55 $mem      46   16            [[    59       ]]  #BOT
-  57 #10       46                 [[    59       ]]  10
-  56 #{ int    46                 [[    59       ]]  { int -> int #1}
-  59 Call      46   55   57   56  [[    60       ]]  Ctrl
-
-LOOP22:                           [[  L8        L29        ]]
-  22 Loop    ____    8   29       [[    39   25   37   26   28  ]]  Ctrl
-  39 Phi_f2$   22   20   38       [[    37   14   38  ]]  int
-  25 Phi_i$3   22   19   41       [[    26   41  ]]  int
-  37 Phi_f1$   22   20   39       [[    38       ]]  int
-  26 LT        22   20   25       [[    28       ]]  bool
-  28 If        22   26            [[    29   30  ]]  [  Ctrl, Ctrl]
-
-L60:                              [[  L46        ]]
-  60 CallEnd   59                 [[    61   63   64  ]]  [  Ctrl, #BOT, int]
-  63 $mem      60                 [[    52       ]]  #BOT
-  64 #2        60                 [[    52       ]]  int
-
-L30:                              [[  LOOP22     ]]
-  30 False     28                 [[    14       ]]  Ctrl
-  14 Return    30   17   39   10  [[     2       ]]  [  Ctrl, #BOT, int]
-
-L29:                              [[  LOOP22     ]]
-  29 True      28                 [[    38   41   22  ]]  Ctrl
-  41 Sub       29   25   20       [[    25       ]]  int
-  38 Add       29   39   37       [[    39       ]]  int
-
-L61:                              [[  L60        ]]
-  61 $ctrl     60                 [[    52       ]]  Ctrl
-  52 Return    61   63   64   48  [[     2       ]]  [  Ctrl, #BOT, int]
-
-L2:                               [[   ]]
-   2 Stop      14   52            [[             ]]  Bot
+   7 Start   ____ ____ ____       [[    13   16   22   27    6  ]]  [  Ctrl, #TOP, int]
+
+L6:                               [[  START      ]]
+   6 fib     ____    7            [[    12   15   21   18    5  ]]  Ctrl
+  12 n          6   13            [[    11       ]]  int
+  15 $mem       6   16            [[     2       ]]  #BOT
+  21 $rpc       6   22            [[     2       ]]  $[ALL]
+  18 #1         6                 [[    17   20  ]]  1
+
+L27:                              [[  START      ]]
+  27 foo     ____    7            [[    28   32   29   26  ]]  Ctrl
+  28 $mem      27   16            [[    26       ]]  #BOT
+  32 $rpc      27   22            [[    23       ]]  $[ALL]
+  29 #10       27                 [[    26       ]]  10
+  26 call      27   28   29       [[    25       ]]  Ctrl
+
+LOOP5:                            [[  L6        L8         ]]
+   5 Loop    ____    6    8       [[    11   17   20    9    4  ]]  Ctrl
+  11 Phi_i      5   12   14       [[     9   14  ]]  int
+  17 Phi_f2     5   18   19       [[     2   19   20  ]]  int
+  20 Phi_f1     5   18   17       [[    19       ]]  int
+   9 cmp        5   11            [[     4       ]]  bool
+   4 j>         5    9            [[     3    8  ]]  [  Ctrl, Ctrl]
+
+L25:                              [[  L27        ]]
+  25 CallEnd   26                 [[    24   30   31  ]]  [  Ctrl, #BOT, int]
+  30 $mem      25                 [[    23       ]]  #BOT
+  31 #2        25                 [[    23       ]]  int
+
+L3:                               [[  LOOP5      ]]
+   3 False      4                 [[     2       ]]  Ctrl
+   2 Return     3   15   17   21  [[     1       ]]  [  Ctrl, #BOT, int]
+
+L24:                              [[  L25        ]]
+  24 $ctrl     25                 [[    23       ]]  Ctrl
+  23 Return    24   30   31   32  [[     1       ]]  [  Ctrl, #BOT, int]
+
+L8:                               [[  LOOP5      ]]
+   8 True       4                 [[    14   19    5  ]]  Ctrl
+  14 dec        8   11            [[    11       ]]  int
+  19 add        8   17   20       [[    17       ]]  int
+
+L1:                               [[   ]]
+   1 Stop       2   23            [[             ]]  Bot
\ No newline at end of file
diff --git a/seaofnodes/src/test/cases/fib/fib.simple_ir b/seaofnodes/src/test/cases/fib/fib.simple_ir
deleted file mode 100644
index d45c9a8..0000000
--- a/seaofnodes/src/test/cases/fib/fib.simple_ir
+++ /dev/null
@@ -1,46 +0,0 @@
-START:                            [[   ]]
-   1 Start   ____ ____ ____       [[     4    5    9   16   18    8    6   20  ]]  [  Ctrl, #TOP, int]
-
-L8:                               [[  START      ]]
-   8 main    ____    1            [[    10   17   57   58   60  ]]  Ctrl
-  10 $rpc       8    9            [[    14       ]]  $[ALL]
-  17 $mem       8   16            [[    60       ]]  #BOT
-  58 #10        8                 [[    60       ]]  10
-  57 #{ int     8                 [[    60       ]]  { int -> int #1}
-  60 Call       8   17   58   57  [[    61       ]]  Ctrl
-
-L20:                              [[  START      ]]
-  20 fib     ____    1            [[    22   29   31   33   35  ]]  Ctrl
-  22 $rpc      20    9            [[    26       ]]  $[ALL]
-  29 $mem      20   16            [[    26       ]]  #BOT
-  31 n         20   18            [[    38       ]]  int
-  33 #1        20                 [[    50   52   39   54  ]]  1
-
-LOOP35:                           [[  L20       L42        ]]
-  35 Loop    ____   20   42       [[    52   38   50   39   41  ]]  Ctrl
-  52 Phi_f2    35   33   51       [[    50   26   51  ]]  int
-  38 Phi_i     35   31   54       [[    39   54  ]]  int
-  50 Phi_f1    35   33   52       [[    51       ]]  int
-  39 LT        35   33   38       [[    41       ]]  bool
-  41 If        35   39            [[    42   43  ]]  [  Ctrl, Ctrl]
-
-L61:                              [[  L8         ]]
-  61 CallEnd   60                 [[    63   65   66  ]]  [  Ctrl, #BOT, int]
-  65 $mem      61                 [[    14       ]]  #BOT
-  66 #2        61                 [[    14       ]]  int
-
-L63:                              [[  L61        ]]
-  63 $ctrl     61                 [[    14       ]]  Ctrl
-  14 Return    63   65   66   10  [[     2       ]]  [  Ctrl, #BOT, int]
-
-L43:                              [[  LOOP35     ]]
-  43 False     41                 [[    26       ]]  Ctrl
-  26 Return    43   29   52   22  [[     2       ]]  [  Ctrl, #BOT, int]
-
-L42:                              [[  LOOP35     ]]
-  42 True      41                 [[    51   54   35  ]]  Ctrl
-  54 Sub       42   38   33       [[    38       ]]  int
-  51 Add       42   52   50       [[    52       ]]  int
-
-L2:                               [[   ]]
-   2 Stop      14   26            [[             ]]  Bot
diff --git a/seaofnodes/src/test/cases/fib/fib.simple b/seaofnodes/src/test/cases/fib/fib.smp
similarity index 100%
rename from seaofnodes/src/test/cases/fib/fib.simple
rename to seaofnodes/src/test/cases/fib/fib.smp
diff --git a/seaofnodes/src/test/cases/fib/fib.smp_ir b/seaofnodes/src/test/cases/fib/fib.smp_ir
new file mode 100644
index 0000000..fda1c0d
--- /dev/null
+++ b/seaofnodes/src/test/cases/fib/fib.smp_ir
@@ -0,0 +1,45 @@
+START:                            [[   ]]
+   7 Start   ____ ____ ____       [[     9   29   21   15    6  ]]  [  Ctrl, #TOP, int]
+
+L6:                               [[  START      ]]
+   6 main    ____    7            [[     8   33   10    5  ]]  Ctrl
+   8 $mem       6    9            [[     5       ]]  #BOT
+  33 $rpc       6   29            [[     2       ]]  $[ALL]
+  10 #10        6                 [[     5       ]]  10
+   5 call       6    8   10       [[     4       ]]  Ctrl
+
+L15:                              [[  START      ]]
+  15 fib     ____    7            [[    20   23   28   25   14  ]]  Ctrl
+  20 n         15   21            [[    19       ]]  int
+  23 $mem      15    9            [[    11       ]]  #BOT
+  28 $rpc      15   29            [[    11       ]]  $[ALL]
+  25 #1        15                 [[    24   27  ]]  1
+
+LOOP14:                           [[  L15       L16        ]]
+  14 Loop    ____   15   16       [[    19   24   27   17   13  ]]  Ctrl
+  19 Phi_i     14   20   22       [[    17   22  ]]  int
+  24 Phi_f2    14   25   26       [[    11   26   27  ]]  int
+  27 Phi_f1    14   25   24       [[    26       ]]  int
+  17 cmp       14   19            [[    13       ]]  bool
+  13 j>        14   17            [[    12   16  ]]  [  Ctrl, Ctrl]
+
+L4:                               [[  L6         ]]
+   4 CallEnd    5                 [[     3   31   32  ]]  [  Ctrl, #BOT, int]
+  31 $mem       4                 [[     2       ]]  #BOT
+  32 #2         4                 [[     2       ]]  int
+
+L3:                               [[  L4         ]]
+   3 $ctrl      4                 [[     2       ]]  Ctrl
+   2 Return     3   31   32   33  [[     1       ]]  [  Ctrl, #BOT, int]
+
+L16:                              [[  LOOP14     ]]
+  16 True      13                 [[    22   26   14  ]]  Ctrl
+  22 dec       16   19            [[    19       ]]  int
+  26 add       16   24   27       [[    24       ]]  int
+
+L12:                              [[  LOOP14     ]]
+  12 False     13                 [[    11       ]]  Ctrl
+  11 Return    12   23   24   28  [[     1       ]]  [  Ctrl, #BOT, int]
+
+L1:                               [[   ]]
+   1 Stop       2   11            [[             ]]  Bot
\ No newline at end of file
diff --git a/seaofnodes/src/test/cases/fib/fib.smp_out b/seaofnodes/src/test/cases/fib/fib.smp_out
new file mode 100644
index 0000000..469c9cc
--- /dev/null
+++ b/seaofnodes/src/test/cases/fib/fib.smp_out
@@ -0,0 +1,27 @@
+---main { int -> int #0}---------------------------
+0000 4883EC08    subi  rsp -= #8
+0004 BF0A000000  ldi   rdi = #10
+0009 E812000000  call  fib  rdi
+000E 4883C408C3  addi  rsp += #8
+0013             ret
+---{ int -> int #0}---------------------------
+
+---fib { int -> int #1}---------------------------
+                 n:rdi
+0020 BA01000000  ldi   rdx = #1
+0025 B901000000  ldi   rcx = #1
+LOOP14:
+                 i:rdi,f2:rdx,f1:rcx
+002A 488BC2      mov   rax = rdx                      // def/self #0
+002D 4883FF01    cmp   rdi, #1
+0031 7E0F        j<=   L12                            // L16
+L16:
+0033 4883C7FF    dec   rdi += #-1
+0037 488BD0      mov   rdx = rax                      // use/self/use #0
+003A 4803D1      add   rdx += rcx
+003D 488BC8      mov   rcx = rax                      // use/self/use #0
+0040 EBE8        jmp   LOOP14
+L52:
+L12:
+0042 C3          ret
+---{ int -> int #1}---------------------------
\ No newline at end of file

From 650b4ef3d646396ee42329ae4e04ce0212710888 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Sat, 9 Aug 2025 23:07:21 +0100
Subject: [PATCH 100/119] update outputs for sieve

---
 seaofnodes/src/test/cases/sieve/README.md     |   9 +
 seaofnodes/src/test/cases/sieve/sieve.ez_ir   | 203 ++++++++++++++++++
 seaofnodes/src/test/cases/sieve/sieve.ez_out  | 175 +++++++++++++++
 seaofnodes/src/test/cases/sieve/sieve.smp_ir  | 159 ++++++++++++++
 seaofnodes/src/test/cases/sieve/sieve.smp_out | 138 ++++++++++++
 5 files changed, 684 insertions(+)
 create mode 100644 seaofnodes/src/test/cases/sieve/README.md
 create mode 100644 seaofnodes/src/test/cases/sieve/sieve.ez_ir
 create mode 100644 seaofnodes/src/test/cases/sieve/sieve.ez_out
 create mode 100644 seaofnodes/src/test/cases/sieve/sieve.smp_ir
 create mode 100644 seaofnodes/src/test/cases/sieve/sieve.smp_out

diff --git a/seaofnodes/src/test/cases/sieve/README.md b/seaofnodes/src/test/cases/sieve/README.md
new file mode 100644
index 0000000..0fa809d
--- /dev/null
+++ b/seaofnodes/src/test/cases/sieve/README.md
@@ -0,0 +1,9 @@
+### Notes
+
+Flags used:
+
+```
+--dump-after-local-sched -S --cpu x86_64_v2 --abi SystemV
+```
+
+Outputs from Simple ch22 and EZ 
\ No newline at end of file
diff --git a/seaofnodes/src/test/cases/sieve/sieve.ez_ir b/seaofnodes/src/test/cases/sieve/sieve.ez_ir
new file mode 100644
index 0000000..ea69e3b
--- /dev/null
+++ b/seaofnodes/src/test/cases/sieve/sieve.ez_ir
@@ -0,0 +1,203 @@
+START:                            [[   ]]
+  13 Start   ____ ____ ____       [[    32   38  114  100  133  106   12  ]]  [  Ctrl, #TOP, int]
+
+L12:                              [[  START      ]]
+  12 sieve   ____   13            [[    31   37   99   36   33   30   29   35   28   63  156  157   11  ]]  Ctrl
+  31 N         12   32            [[    30   36   58   60   82  ]]  int
+  37 $mem      12   38            [[    35   35   89  ]]  #BOT
+  99 $rpc      12  100            [[     2       ]]  $[ALL]
+  63 #4        12                 [[    62       ]]  4
+ 157 #1        12                 [[    55       ]]  1
+  36 lea       12 ____   31       [[    35       ]]  int
+  33 #2        12                 [[    30   52  ]]  2
+  35 new_ary   12   36   37   37  [[    34   39   44  ]]  [  Ctrl, *![int], #2:u32, #3:int]
+  34 $2        35                 [[    28       ]]  #2:u32
+  39 $3        35                 [[    28       ]]  #3:int
+  44 [int]     35                 [[    42   24   71  ]]  *[int]
+  30 div       12   31   33       [[    29       ]]  int
+  29 lea       12 ____   30       [[    28       ]]  int
+ 156 #0        12                 [[    46   87  ]]  0
+  28 new_ary   12   29   34   39  [[    27   43   93  ]]  [  Ctrl, *![int], #2:u32, #3:int]
+  27 $3        28                 [[    26       ]]  #3:int
+  43 [int]     28                 [[    42   75   98  ]]  *[int]
+  93 $2        28                 [[    91       ]]  #2:u32
+
+L133:                             [[  START      ]]
+ 133 main    ____   13            [[   134  155  135  132  ]]  Ctrl
+ 134 $mem     133   38            [[   132       ]]  #BOT
+ 155 $rpc     133  100            [[   126       ]]  $[ALL]
+ 135 #20      133                 [[   132       ]]  20
+ 132 call     133  134  135       [[   131       ]]  Ctrl
+
+L106:                             [[  START      ]]
+ 106 eq      ____   13            [[   112  113  118  121  125   47  105  ]]  Ctrl
+ 112 $mem     106   38            [[   110  117  123  ]]  #BOT
+ 113 a        106  114            [[   110       ]]  *[int]
+ 125 $rpc     106  100            [[   101       ]]  $[ALL]
+ 121 n        106   32            [[   119       ]]  int
+ 118 b        106  114            [[   117       ]]  *[int]
+  47 #0       106                 [[   124  115  ]]  0
+
+LOOP11:                           [[  L12       L14        ]]
+  11 Loop    ____   12   14       [[    26   46   52   62   60   10  ]]  Ctrl
+  26 Phi_$3    11   27   40       [[    24   41   73  ]]  #3:Bot
+  46 Phi_npr   11  156   48       [[    45   48   76  ]]  int
+  52 Phi_p     11   33   53       [[    51   79  ]]  int
+  62 Phi_p     11   63   64       [[    60       ]]  int
+  60 cmp       11   62   31       [[    10       ]]  bool
+  10 j<        11   60            [[     9   22  ]]  [  Ctrl, Ctrl]
+
+LOOP105:                          [[  L106      L107       ]]
+ 105 Loop    ____  106  107       [[   115  119  104  ]]  Ctrl
+ 115 Phi_i    105   47  116       [[   110  116  117  119  ]]  int
+ 119 cmp      105  115  121       [[   104       ]]  bool
+ 104 j<       105  119            [[   103  109  ]]  [  Ctrl, Ctrl]
+
+L131:                             [[  L133       ]]
+ 131 CallEnd  132                 [[   130  137  151  ]]  [  Ctrl, #BOT, *![int]]
+ 137 $mem     131                 [[   136  139  139  ]]  #BOT
+ 151 #2       131                 [[   129       ]]  *[int]
+
+L9:                               [[  LOOP11     ]]
+   9 False     10                 [[     8       ]]  Ctrl
+
+L130:                             [[  L131       ]]
+ 130 $ctrl    131                 [[   140  152  139  148  147  146  145  144  143  142  141  136  129  ]]  Ctrl
+ 148 st8      130  149  150 ____ ____  [[   147  ]]  #3:int
+ 147 st8      130  148  150 ____ ____  [[   146  ]]  #3:int
+ 146 st8      130  147  150 ____ ____  [[   145  ]]  #3:int
+ 145 st8      130  146  150 ____ ____  [[   144  ]]  #3:int
+ 144 st8      130  145  150 ____ ____  [[   143  ]]  #3:int
+ 143 st8      130  144  150 ____ ____  [[   142  ]]  #3:int
+ 140 #72      130                 [[   139       ]]  72
+ 142 st8      130  143  150 ____ ____  [[   141  ]]  #3:int
+ 141 st8      130  142  150 ____ ____  [[   136  ]]  #3:int
+ 139 new_ary  130  140  137  137  [[   138  149  150  ]]  [  Ctrl, *![int], #2:u32, #3:int]
+ 138 $2       139                 [[   136       ]]  #2:u32
+ 149 $3       139                 [[   148       ]]  #3:int
+ 150 [int]    139                 [[   148  147  146  145  144  143  142  141  129  ]]  *[int]
+ 136 ALLMEM   130  137  138  141  [[   129       ]]  #BOT
+ 152 #8       130                 [[   129       ]]  8
+ 129 call     130  136  151  150  152  [[   128  ]]  Ctrl
+
+L109:                             [[  LOOP105    ]]
+ 109 True     104                 [[   117  110  108  ]]  Ctrl
+ 117 ld8      109  112  118  115  [[   110       ]]  int
+ 110 cmp8     109  112  113  115  117  [[   108  ]]  bool
+ 108 j==      109  110            [[   107  122  ]]  [  Ctrl, Ctrl]
+
+L22:                              [[  LOOP11     ]]
+  22 True      10                 [[    21       ]]  Ctrl
+
+L103:                             [[  LOOP105    ]]
+ 103 False    104                 [[    56  102  ]]  Ctrl
+  56 #1       103                 [[   124       ]]  1
+
+LOOP8:                            [[  L9        L65        ]]
+   8 Loop    ____    9   65       [[    73   76   79   82    7  ]]  Ctrl
+  73 Phi_$3     8   26   74       [[    71   75   74   91  ]]  #BOT
+  76 Phi_npr    8   46   77       [[    75   78   77   85   92  ]]  int
+  79 Phi_p      8   52   80       [[    75   80   71   82  ]]  int
+  82 cmp        8   79   31       [[     7       ]]  bool
+   7 j<         8   82            [[     6   70  ]]  [  Ctrl, Ctrl]
+
+LOOP21:                           [[  L22       L23        ]]
+  21 Loop    ____   22   23       [[    51   53   24   20  ]]  Ctrl
+  51 Phi_p     21   52   53       [[    50   53   54   55   24  ]]  int
+  24 test8     21   26   44   51 ____  [[    20  ]]
+  20 j!=       21   24            [[    17   23  ]]  [  Ctrl, Ctrl]
+  53 inc       21   51            [[    52   51   64   64  ]]  int
+
+L107:                             [[  L109       ]]
+ 107 True     108                 [[   116  105  ]]  Ctrl
+ 116 inc      107  115            [[   115       ]]  int
+
+L122:                             [[  L109       ]]
+ 122 False    108                 [[   102       ]]  Ctrl
+
+L128:                             [[  L130       ]]
+ 128 CallEnd  129                 [[   127  153  154  ]]  [  Ctrl, #BOT, bool]
+ 153 $mem     128                 [[   126       ]]  #BOT
+ 154 #2       128                 [[   126       ]]  bool
+
+L6:                               [[  LOOP8      ]]
+   6 False      7                 [[    92   91    5  ]]  Ctrl
+  92 lea        6 ____   76       [[    91       ]]  int
+  91 new_ary    6   92   93   73  [[    90   95   97  ]]  [  Ctrl, *![int], #2:u32, #3:int]
+  90 $2        91                 [[    89       ]]  #2:u32
+  95 $3        91                 [[    94       ]]  #3:int
+  97 [int]     91                 [[    96    2  ]]  *[int]
+
+L102:                             [[  L103      L122       ]]
+ 102 Region  ____  103  122       [[   124  123  101  ]]  Ctrl
+ 124 Phi_res  102   56   47       [[   101       ]]  bool
+ 123 ALLMEM   102  112 ____ ____  [[   101       ]]  #BOT
+ 101 Return   102  123  124  125  [[     1       ]]  [  Ctrl, #BOT, bool]
+
+L17:                              [[  LOOP21     ]]
+  17 False     20                 [[    50   16  ]]  Ctrl
+  50 shli      17   51            [[    49       ]]  int
+
+L70:                              [[  LOOP8      ]]
+  70 True       7                 [[    71   69  ]]  Ctrl
+  71 test8     70   73   44   79 ____  [[    69  ]]
+  69 j!=       70   71            [[    66   81  ]]  [  Ctrl, Ctrl]
+
+L127:                             [[  L128       ]]
+ 127 $ctrl    128                 [[   126       ]]  Ctrl
+ 126 Return   127  153  154  155  [[     1       ]]  [  Ctrl, #BOT, bool]
+
+LOOP5:                            [[  L6        L84        ]]
+   5 Loop    ____    6   84       [[    87   94   85    4  ]]  Ctrl
+  87 Phi_j      5  156   88       [[    85   88   96   98  ]]  int
+  94 Phi_$3     5   95   96       [[    89   96   98  ]]  #3:Bot
+  85 cmp        5   87   76       [[     4       ]]  bool
+   4 j<         5   85            [[     3   84  ]]  [  Ctrl, Ctrl]
+
+LOOP16:                           [[  L17       L57        ]]
+  16 Loop    ____   17   57       [[    41   42   45   49   55   40   58   15  ]]  Ctrl
+  41 Phi_$3    16   26   40       [[    40       ]]  #3:Bot
+  42 Phi_$3    16   43   44       [[    40       ]]  *[int]
+  45 Phi_$3    16   46   49       [[    40       ]]  int
+  49 Phi_i     16   50   54       [[    45   54   58  ]]  int
+  55 Phi_$3    16   51  157       [[    40       ]]  int
+  58 cmp       16   49   31       [[    15       ]]  bool
+  15 j<        16   58            [[    14   57  ]]  [  Ctrl, Ctrl]
+  40 st8       16   41   42   45   55  [[    26   41  ]]  #3:Bot
+
+L66:                              [[  L70        ]]
+  66 False     69                 [[    75   78   65  ]]  Ctrl
+  78 inc       66   76            [[    77       ]]  int
+  75 st8       66   73   43   76   79  [[    74  ]]  #3:Bot
+
+L81:                              [[  L70        ]]
+  81 True      69                 [[    65       ]]  Ctrl
+
+L3:                               [[  LOOP5      ]]
+   3 False      4                 [[    89    2  ]]  Ctrl
+  89 ALLMEM     3   37   90   94  [[     2       ]]  #BOT
+   2 Return     3   89   97   99  [[     1       ]]  [  Ctrl, #BOT, *![int]]
+
+L84:                              [[  LOOP5      ]]
+  84 True       4                 [[    98   96   88    5  ]]  Ctrl
+  88 inc       84   87            [[    87       ]]  int
+  98 ld8       84   94   43   87  [[    96       ]]  int
+  96 st8       84   94   97   87   98  [[    94  ]]  #3:Bot
+
+L57:                              [[  LOOP16     ]]
+  57 True      15                 [[    54   16  ]]  Ctrl
+  54 add       57   49   51       [[    49       ]]  int
+
+L14:                              [[  LOOP16     ]]
+  14 False     15                 [[    48   64   11  ]]  Ctrl
+  64 mul       14   53   53       [[    62       ]]  int
+  48 inc       14   46            [[    46       ]]  int
+
+L65:                              [[  L66       L81        ]]
+  65 Region  ____   66   81       [[    74   77   80    8  ]]  Ctrl
+  74 Phi_$3    65   75   73       [[    73       ]]  #BOT
+  77 Phi_npr   65   78   76       [[    76       ]]  int
+  80 inc       65   79            [[    79       ]]  int
+
+L1:                               [[   ]]
+   1 Stop       2  101  126       [[             ]]  Bot
\ No newline at end of file
diff --git a/seaofnodes/src/test/cases/sieve/sieve.ez_out b/seaofnodes/src/test/cases/sieve/sieve.ez_out
new file mode 100644
index 0000000..863147c
--- /dev/null
+++ b/seaofnodes/src/test/cases/sieve/sieve.ez_out
@@ -0,0 +1,175 @@
+---sieve { int -> *![int] #1}---------------------------
+0000 4883EC28    subi  rsp -= #40
+                 N:rdi
+0004 48895C2418  mov   [rsp+24] = rbx                 // def/loop #3
+0009 48896C2410  mov   [rsp+16] = rbp                 // def/loop #3
+000E 4C89642408  mov   [rsp+8] = r12                  // def/loop #3
+0013 4C892C24    mov   [rsp+0] = r13                  // def/loop #3
+0017 4C8BE7      mov   r12 = rdi                      // def/empty1 #0
+001A 4A8D34E508  lea   rsi = r12<<3 + #8
+001F 000000
+0022 B902000000  ldi   rcx = #2
+0027 488BC7      mov   rax = rdi                      // use/empty1 #0
+002A 489948F7F9  div   rax = rax / rcx                // kill rdx
+002F 488D1CC508  lea   rbx = rax<<3 + #8
+0034 000000
+0037 BF01000000  alloc ldi   rcx = #1
+003C E800000000  call  #calloc
+0041 4C8BE8      mov   r13 = rax                      // def/empty1 #2
+0044 488BF3      mov   rsi = rbx                      // use/empty1 #1
+0047 BF01000000  alloc ldi   rcx = #1
+004C E800000000  call  #calloc
+0051 488BD8      mov   rbx = rax                      // def/empty1 #2
+0054 4D33C9      xor   r9,r9
+0057 B802000000  ldi   rax = #2
+005C BA04000000  ldi   rdx = #4
+LOOP11:
+                 nprimes:r9,p:rax,p:rdx
+0061 488BC8      mov   rcx = rax                      // def/self #1
+0064 493BD4      cmp   rdx, r12
+0067 7D59        j>=   LOOP8                          // L22
+L22:
+LOOP21:
+                 p:rcx
+0069 488BC1      mov   rax = rcx                      // use/self/use #1
+006C 48FFC0      inc   rax += #1
+006F 498BD5      mov   rdx = r13                      // use/empty1 #2
+0072 48817CCA08  test8 , [rdx+rcx*8+8]
+0077 0000000000000000
+007F 7405        j==   L17                            // L23
+L23:
+0081 488BC8      mov   rcx = rax                      // use/self/use #1
+0084 EBE3        jmp   LOOP21
+L244:
+L17:
+0086 488BF1      mov   rsi = rcx                      // use/self/two #1
+0089 48C1E601    shli  rsi <<= #1
+008D 4D8BC1      mov   r8 = r9                        // use/self/phi #1
+0090 488BF9      mov   rdi = rcx                      // use/self/use #1
+0093 488BD3      mov   rdx = rbx                      // use/empty1 #2
+LOOP16:
+                 $3:rdx,$3:r8,i:rsi,$3:rdi
+0096 488BEE      mov   rbp = rsi                      // def/self #1
+0099 4A897CC208  st8   [rdx+r8*8+8],rdi
+009E 493BEC      cmp   rbp, r12
+00A1 7D13        j>=   L14                            // L57
+L57:
+00A3 488BF5      mov   rsi = rbp                      // use/self/use #1
+00A6 4803F1      add   rsi += rcx
+00A9 4C8BC5      mov   r8 = rbp                       // use/self/use #1
+00AC BF01000000  ldi   rdi = #1
+00B1 498BD5      mov   rdx = r13                      // use/self/use #1
+00B4 EBE0        jmp   LOOP16
+L243:
+L14:
+00B6 49FFC1      inc   r9 += #1
+00B9 488BD0      mov   rdx = rax                      // use/self/use #1
+00BC 480FAFD0    mul   rdx = rdx * rax
+00C0 EB9F        jmp   LOOP11
+L242:
+L9:
+LOOP8:
+                 nprimes:r9,p:rcx
+00C2 498BE9      mov   rbp = r9                       // def/self #1
+00C5 493BCC      cmp   rcx, r12
+00C8 7D24        j>=   L6                             // L70
+L70:
+00CA 49817CCD08  test8 , [r13+rcx*8+8]
+00CF 0000000000000000
+00D7 7405        j==   L66                            // L81
+L81:
+00D9 4C8BCD      mov   r9 = rbp                       // use/self/use #1
+00DC EB0B        jmp   L65
+L241:
+L66:
+00DE 48894CEB08  st8   [rbx+rbp*8+8],rcx
+00E3 4C8BCD      mov   r9 = rbp                       // use/self/use #1
+00E6 49FFC1      inc   r9 += #1
+L65:
+                 nprimes:r9
+00E9 48FFC1      inc   rcx += #1
+00EC EBD4        jmp   LOOP8
+L240:
+L6:
+00EE 488D34ED08  lea   rsi = rbp<<3 + #8
+00F3 000000
+00F6 BF01000000  alloc ldi   rcx = #1
+00FB E800000000  call  #calloc
+0100 33C9        xor   rcx,rcx
+LOOP5:
+                 j:rcx
+0102 483BCD      cmp   rcx, rbp
+0105 7D0F        j>=   L3                             // L84
+L84:
+0107 488B54CB08  ld8   rdx,[rbx+rcx*8+8]
+010C 488954C808  st8   [rax+rcx*8+8],rdx
+0111 48FFC1      inc   rcx += #1
+0114 EBEC        jmp   LOOP5
+L239:
+L3:
+0116 4C8B2C24    mov   r13 = [rsp+0]                  // use/loop/use #3
+011A 4C8B642408  mov   r12 = [rsp+8]                  // use/loop/use #3
+011F 488B6C2410  mov   rbp = [rsp+16]                 // use/loop/use #3
+0124 488B5C2418  mov   rbx = [rsp+24]                 // use/loop/use #3
+0129 4883C428C3  addi  rsp += #40
+012E             ret
+---{ int -> *![int] #1}---------------------------
+
+---eq { *![int] *![int] int -> int #0}---------------------------
+                 a:rdi,b:rsi,n:rdx
+0130 33C0        xor   rax,rax
+LOOP105:
+                 i:rax
+0132 483BC2      cmp   rax, rdx
+0135 7D15        j>=   L103                           // L109
+L109:
+0137 488B4CC608  ld8   rcx,[rsi+rax*8+8]
+013C 48394CC708  cmp8  rcx, [rdi+rax*8+8]
+0141 7505        j!=   L122                           // L107
+L107:
+0143 48FFC0      inc   rax += #1
+0146 EBEA        jmp   LOOP105
+L238:
+L122:
+0148 33C0        xor   rax,rax
+014A EB05        jmp   L102
+L237:
+L103:
+014C B801000000  ldi   rax = #1
+L102:
+                 result:rax
+0151 C3          ret
+---{ *![int] *![int] int -> int #0}---------------------------
+
+---main { -> int #0}---------------------------
+0160 4883EC08    subi  rsp -= #8
+0164 BF14000000  ldi   rdi = #20
+0169 E892FEFFFF  call  sieve  rdi
+016E 48890424    mov   [rsp+0] = rax                  // def/empty1 #1
+0172 BE48000000  ldi   rsi = #72
+0177 BF01000000  alloc ldi   rcx = #1
+017C E800000000  call  #calloc
+0181 488BC8      mov   rcx = rax                      // def/empty1 #0
+0184 48C7410802  st8   [rcx+8],#2
+0189 000000
+018C 48C7411003  st8   [rcx+16],#3
+0191 000000
+0194 48C7411805  st8   [rcx+24],#5
+0199 000000
+019C 48C7412007  st8   [rcx+32],#7
+01A1 000000
+01A4 48C741280B  st8   [rcx+40],#11
+01A9 000000
+01AC 48C741300D  st8   [rcx+48],#13
+01B1 000000
+01B4 48C7413811  st8   [rcx+56],#17
+01B9 000000
+01BC 48C7414013  st8   [rcx+64],#19
+01C1 000000
+01C4 488B3C24    mov   rdi = [rsp+0]                  // use/empty1 #0
+01C8 488BF0      mov   rsi = rax                      // use/empty1 #0
+01CB BA08000000  ldi   rdx = #8
+01D0 E85BFFFFFF  call  eq  rdi  rsi  rdx
+01D5 4883C408C3  addi  rsp += #8
+01DA             ret
+---{ -> int #0}---------------------------
\ No newline at end of file
diff --git a/seaofnodes/src/test/cases/sieve/sieve.smp_ir b/seaofnodes/src/test/cases/sieve/sieve.smp_ir
new file mode 100644
index 0000000..6abc7d6
--- /dev/null
+++ b/seaofnodes/src/test/cases/sieve/sieve.smp_ir
@@ -0,0 +1,159 @@
+START:                            [[   ]]
+   7 Start   ____ ____ ____       [[    15   18   22   27   31   42    6  ]]  [  Ctrl, #TOP, int]
+
+L6:                               [[  START      ]]
+   6 sys.ary ____    7            [[    14   17   21   24   26   29   30  129    5  ]]  Ctrl
+  14 len        6   15            [[     9       ]]  int
+  17 $mem       6   18            [[    16   19  ]]  #BOT
+  30 $rpc       6   31            [[     2       ]]  $[ALL]
+  29 soff       6   15            [[    28       ]]  int
+  26 src        6   27            [[    25       ]]  *[u8]
+  24 doff       6   15            [[    23       ]]  int
+  21 dst        6   22            [[    20    2  ]]  *[u8]
+ 129 #0         6                 [[    11       ]]  0
+
+L42:                              [[  START      ]]
+  42 sieve   ____    7            [[    59   60  128   75   58   57   74   79  115  114  113   69   12   41  ]]  Ctrl
+  59 N         42   15            [[    58   74   76   92  105  115  ]]  int
+  60 $mem      42   18            [[    57   57  104  113  113  ]]  #BOT
+ 128 $rpc      42   31            [[    32       ]]  $[ALL]
+  75 #429496   42                 [[    74       ]]  4294967295
+  79 #4        42                 [[    78       ]]  4
+  69 #2        42                 [[    68       ]]  2
+  74 and       42   59   75       [[    72       ]]  u32
+ 114 lea       42 ____  115       [[   113       ]]  int
+  12 #0        42                 [[   100   97  ]]  0
+  58 addi      42   59            [[    57       ]]  int
+ 113 new_ary   42  114   60   60  [[   112  116  119  ]]  [  Ctrl, *[u32]!, #20:u32, #21:u32]
+ 112 $20      113                 [[   111       ]]  #20:u32
+ 116 [u32]    113                 [[   111  120  122  127  ]]  *[u32]
+ 119 $21      113                 [[   118       ]]  #21:u32
+  57 new_ary   42   58   60   60  [[    56   64  106  ]]  [  Ctrl, *[bool]!, #18:u32, #19:bool]
+  56 $19       57                 [[    55       ]]  #19:bool
+  64 [bool]    57                 [[    62   53   87  105  ]]  *[bool]
+ 106 $18       57                 [[   105       ]]  #18:u32
+ 115 sari      42   59            [[   114  111  ]]  [-4611686018427387904-4611686018427387903]
+
+LOOP5:                            [[  L6        L8         ]]
+   5 Loop    ____    6    8       [[    11   19    9    4  ]]  Ctrl
+  11 Phi_i      5  129   13       [[     9   13   23   28  ]]  int
+  19 Phi_$14    5   17   20       [[    16   20   25  ]]  #BOT
+   9 cmp        5   11   14       [[     4       ]]  bool
+   4 j<         5    9            [[     3    8  ]]  [  Ctrl, Ctrl]
+
+LOOP41:                           [[  L42       L43        ]]
+  41 Loop    ____   42   43       [[    55   68   78  100  118   76   40  ]]  Ctrl
+  55 Phi_$19   41   56   61       [[    53   61   87  104  ]]  #BOT
+  68 Phi_p     41   69   70       [[    67   89  ]]  int
+  78 Phi_p     41   79   80       [[    76       ]]  int
+ 118 Phi_$21   41  119  120       [[   117  120  ]]  #21:Bot
+ 100 Phi_npr   41   12  101       [[    99  101  120  ]]  int
+  76 cmp       41   78   59       [[    40       ]]  bool
+  40 j<        41   76            [[    39   51  ]]  [  Ctrl, Ctrl]
+
+L3:                               [[  LOOP5      ]]
+   3 False      4                 [[    16    2  ]]  Ctrl
+  16 ALLMEM     3   17 ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____   19  [[     2  ]]  #BOT
+   2 Return     3   16   21   30  [[     1       ]]  [  Ctrl, #BOT, *[u8]!]
+
+L8:                               [[  LOOP5      ]]
+   8 True       4                 [[    28   25   23   20   13    5  ]]  Ctrl
+  28 add        8   11   29       [[    25       ]]  int
+  13 inc        8   11            [[    11       ]]  int
+  25 ld1        8   19   26   28  [[    20       ]]  u8
+  23 add        8   11   24       [[    20       ]]  int
+  20 st1        8   19   21   23   25  [[    19  ]]  #14:Bot
+
+L39:                              [[  LOOP41     ]]
+  39 False     40                 [[    38       ]]  Ctrl
+
+L51:                              [[  LOOP41     ]]
+  51 True      40                 [[    50       ]]  Ctrl
+
+LOOP50:                           [[  L51       L52        ]]
+  50 Loop    ____   51   52       [[    67   70   53   49  ]]  Ctrl
+  67 Phi_p     50   68   70       [[    66   70   71   53  120  ]]  int
+  70 inc       50   67            [[    68   67   80   80  ]]  int
+  53 test1     50   55   64   67 ____  [[    49  ]]
+  49 j!=       50   53            [[    46   52  ]]  [  Ctrl, Ctrl]
+
+LOOP38:                           [[  L39       L81        ]]
+  38 Loop    ____   39   81       [[    89   99  117   92   37  ]]  Ctrl
+  89 Phi_p     38   68   90       [[    87   90   92  122  ]]  int
+  99 Phi_npr   38  100  102       [[    95  103  102  110  122  107  ]]  int
+ 117 Phi_$21   38  118  121       [[   109  122  121  ]]  #BOT
+  92 cmp       38   89   59       [[    37       ]]  bool
+  37 j<        38   92            [[    36   86  ]]  [  Ctrl, Ctrl]
+
+L86:                              [[  LOOP38     ]]
+  86 True      37                 [[    87   85  ]]  Ctrl
+  87 test1     86   55   64   89 ____  [[    85  ]]
+  85 j!=       86   87            [[    82   91  ]]  [  Ctrl, Ctrl]
+
+L46:                              [[  LOOP50     ]]
+  46 False     49                 [[    66   45  ]]  Ctrl
+  66 shli      46   67            [[    65       ]]  int
+
+L36:                              [[  LOOP38     ]]
+  36 False     37                 [[   110  111  109   35  ]]  Ctrl
+ 110 lea       36 ____   99       [[   109       ]]  int
+ 111 st4       36  112  116 ____  115  [[   109  ]]  #20:u32
+ 109 new_ary   36  110  111  117  [[   108  123  125  ]]  [  Ctrl, *[u32]!, #20:u32, #21:u32]
+ 108 $20      109                 [[   107       ]]  #20:u32
+ 123 [u32]    109                 [[   107  126   32  ]]  *[u32]
+ 125 $21      109                 [[   124       ]]  #21:u32
+
+LOOP35:                           [[  L36       L94        ]]
+  35 Loop    ____   36   94       [[    97  124   95   34  ]]  Ctrl
+  97 Phi_j     35   12   98       [[    95   98  126  127  ]]  int
+ 124 Phi_$21   35  125  126       [[   104  126  127  ]]  #21:Bot
+  95 cmp       35   97   99       [[    34       ]]  bool
+  34 j<        35   95            [[    33   94  ]]  [  Ctrl, Ctrl]
+
+LOOP45:                           [[  L46       L63        ]]
+  45 Loop    ____   46   63       [[    61   65   72   44  ]]  Ctrl
+  61 Phi_$19   45   55   62       [[    55   62  ]]  #BOT
+  65 Phi_i     45   66   71       [[    62   71   72  ]]  int
+  72 cmp       45   65   74       [[    44       ]]  bool
+  44 j<        45   72            [[    43   63  ]]  [  Ctrl, Ctrl]
+
+L82:                              [[  L86        ]]
+  82 False     85                 [[   122  103   81  ]]  Ctrl
+ 122 st4       82  117  116   99   89  [[   121  ]]  #21:Bot
+ 103 inc       82   99            [[   102       ]]  int
+
+L91:                              [[  L86        ]]
+  91 True      85                 [[    81       ]]  Ctrl
+
+L63:                              [[  LOOP45     ]]
+  63 True      44                 [[    62   71   45  ]]  Ctrl
+  71 add       63   65   67       [[    65       ]]  int
+  62 st1       63   61   64   65 ____  [[    61  ]]  #19:Bot
+
+L43:                              [[  LOOP45     ]]
+  43 False     44                 [[    80  101  120   41  ]]  Ctrl
+  80 mul       43   70   70       [[    78       ]]  int
+ 120 st4       43  118  116  100   67  [[   118  ]]  #21:Bot
+ 101 inc       43  100            [[   100       ]]  int
+
+L94:                              [[  LOOP35     ]]
+  94 True      34                 [[   127  126   98   35  ]]  Ctrl
+  98 inc       94   97            [[    97       ]]  int
+ 127 ld4       94  124  116   97  [[   126       ]]  u32
+ 126 st4       94  124  123   97  127  [[   124  ]]  #21:Bot
+
+L81:                              [[  L82       L91        ]]
+  81 Region  ____   82   91       [[   102  121   90   38  ]]  Ctrl
+ 102 Phi_npr   81  103   99       [[    99       ]]  int
+ 121 Phi_$21   81  122  117       [[   117       ]]  #BOT
+  90 inc       81   89            [[    89       ]]  int
+
+L33:                              [[  LOOP35     ]]
+  33 False     34                 [[   105  107  104   32  ]]  Ctrl
+ 105 st4       33  106   64 ____   59  [[   104  ]]  #18:u32
+ 107 st4       33  108  123 ____   99  [[   104  ]]  #20:u32
+ 104 ALLMEM    33   60 ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____  105   55  107  124  [[    32  ]]  #BOT
+  32 Return    33  104  123  128  [[     1       ]]  [  Ctrl, #BOT, *[u32]!]
+
+L1:                               [[   ]]
+   1 Stop       2   32            [[             ]]  Bot
\ No newline at end of file
diff --git a/seaofnodes/src/test/cases/sieve/sieve.smp_out b/seaofnodes/src/test/cases/sieve/sieve.smp_out
new file mode 100644
index 0000000..23336a5
--- /dev/null
+++ b/seaofnodes/src/test/cases/sieve/sieve.smp_out
@@ -0,0 +1,138 @@
+---sys.ary.copy { *[u8]! int *[u8] int int -> *[u8]! #0}---------------------------
+                 len:r8,dst:rdi,doff:rsi,src:rdx,soff:rcx
+0000 4C8BDF      mov   r11 = rdi                      // def/empty1 #0
+0003 4D33D2      xor   r10,r10
+LOOP5:
+                 i:r10
+0006 4D3BD0      cmp   r10, r8
+0009 7D1C        j>=   L3                             // L8
+L8:
+000B 4D8BCA      mov   r9 = r10                       // use/self/use #1
+000E 4C03C9      add   r9 += rcx
+0011 4A0FB6440A  ld1   rax,[rdx+r9+4]
+0016 04
+0017 498BFA      mov   rdi = r10                      // use/self/use #1
+001A 4803FE      add   rdi += rsi
+001D 4188443B04  st1   [r11+rdi+4],rax
+0022 49FFC2      inc   r10 += #1
+0025 EBDF        jmp   LOOP5
+L199:
+L3:
+0027 498BC3      mov   rax = r11                      // use/empty1 #0
+002A C3          ret
+---{ *[u8]! int *[u8] int int -> *[u8]! #0}---------------------------
+
+---sieve { int -> *[u32]! #1}---------------------------
+0030 4883EC28    subi  rsp -= #40
+                 N:rdi
+0034 48895C2420  mov   [rsp+32] = rbx                 // def/loop #3
+0039 48896C2418  mov   [rsp+24] = rbp                 // def/loop #3
+003E 4C89642410  mov   [rsp+16] = r12                 // def/loop #3
+0043 4C896C2408  mov   [rsp+8] = r13                  // def/loop #3
+0048 4C893424    mov   [rsp+0] = r14                  // def/loop #3
+004C 4C8BE7      mov   r12 = rdi                      // def/empty1 #2
+004F 4C8BF7      mov   r14 = rdi                      // use/empty1 #2
+0052 BBFFFFFFFF  ldi   rbx = #4294967295
+0057 498BF4      mov   rsi = r12                      // use/self/two #1
+005A 4883C604    addi  rsi += #4
+005E BF01000000  alloc ldi   rcx = #1
+0063 E800000000  call  #calloc
+0068 488BE8      mov   rbp = rax                      // def/empty1 #1
+006B 4D8BEC      mov   r13 = r12                      // use/self/two #1
+006E 4C23EB      and   r13 &= rbx
+0071 49C1FC01    sari  r12 >>= #1
+0075 4A8D34A504  lea   rsi = r12<<2 + #4
+007A 000000
+007D BF01000000  alloc ldi   rcx = #1
+0082 E800000000  call  #calloc
+0087 488BD8      mov   rbx = rax                      // def/empty1 #1
+008A B902000000  ldi   rcx = #2
+008F 41B8040000  ldi   r8 = #4
+0094 00
+0095 33FF        xor   rdi,rdi
+LOOP41:
+                 p:rcx,p:r8,nprimes:rdi
+0097 488BC7      mov   rax = rdi                      // def/self #1
+009A 488BD1      mov   rdx = rcx                      // def/self #1
+009D 4D3BC6      cmp   r8, r14
+00A0 7D41        j>=   L39                            // L51
+L51:
+LOOP50:
+                 p:rdx
+00A2 488BCA      mov   rcx = rdx                      // use/self/use #1
+00A5 48FFC1      inc   rcx += #1
+00A8 488BF5      mov   rsi = rbp                      // use/empty1 #1
+00AB 807C160400  test1 , [rsi+rdx+4]
+00B0 7405        j==   L46                            // L52
+L52:
+00B2 488BD1      mov   rdx = rcx                      // use/self/use #1
+00B5 EBEB        jmp   LOOP50
+L198:
+L46:
+00B7 488BF2      mov   rsi = rdx                      // use/self/use #1
+00BA 48C1E601    shli  rsi <<= #1
+LOOP45:
+                 i:rsi
+00BE 493BF5      cmp   rsi, r13
+00C1 7D0A        j>=   L43                            // L63
+L63:
+00C3 C644350401  st1   [rbp+rsi+4],#1
+00C8 4803F2      add   rsi += rdx
+00CB EBF1        jmp   LOOP45
+L197:
+L43:
+00CD 4C8BC1      mov   r8 = rcx                       // use/self/use #1
+00D0 4C0FAFC1    mul   r8 = r8 * rcx
+00D4 488BF8      mov   rdi = rax                      // use/self/use #1
+00D7 48FFC7      inc   rdi += #1
+00DA 488BF3      mov   rsi = rbx                      // use/empty1 #1
+00DD 89548604    st4   [rsi+rax*4+4],rdx
+00E1 EBB4        jmp   LOOP41
+L196:
+L39:
+00E3 488BCA      mov   rcx = rdx                      // use/self/use #1
+00E6 4C8BE8      mov   r13 = rax                      // use/self/phi #1
+LOOP38:
+                 p:rcx,nprimes:r13
+00E9 493BCE      cmp   rcx, r14
+00EC 7D14        j>=   L36                            // L86
+L86:
+00EE 807C0D0400  test1 , [rbp+rcx+4]
+00F3 7508        j!=   L81                            // L82
+L82:
+00F5 42894CAB04  st4   [rbx+r13*4+4],rcx
+00FA 49FFC5      inc   r13 += #1
+L91:
+L81:
+                 nprimes:r13
+00FD 48FFC1      inc   rcx += #1
+0100 EBE7        jmp   LOOP38
+L195:
+L36:
+0102 4A8D34AD04  lea   rsi = r13<<2 + #4
+0107 000000
+010A 448923      st4   [rbx],r12
+010D BF01000000  alloc ldi   rcx = #1
+0112 E800000000  call  #calloc
+0117 33C9        xor   rcx,rcx
+LOOP35:
+                 j:rcx
+0119 493BCD      cmp   rcx, r13
+011C 7D0D        j>=   L33                            // L94
+L94:
+011E 8B548B04    ld4   rdx,[rbx+rcx*4+4]
+0122 89548804    st4   [rax+rcx*4+4],rdx
+0126 48FFC1      inc   rcx += #1
+0129 EBEE        jmp   LOOP35
+L194:
+L33:
+012B 44897500    st4   [rbp],r14
+012F 448928      st4   [rax],r13
+0132 4C8B3424    mov   r14 = [rsp+0]                  // use/loop/use #3
+0136 4C8B6C2408  mov   r13 = [rsp+8]                  // use/loop/use #3
+013B 4C8B642410  mov   r12 = [rsp+16]                 // use/loop/use #3
+0140 488B6C2418  mov   rbp = [rsp+24]                 // use/loop/use #3
+0145 488B5C2420  mov   rbx = [rsp+32]                 // use/loop/use #3
+014A 4883C428C3  addi  rsp += #40
+014F             ret
+---{ int -> *[u32]! #1}---------------------------
\ No newline at end of file

From b9793154ceb9d80044d40bfaa00802e0b5d9c493 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <dibyendu@redukti.com>
Date: Sun, 10 Aug 2025 14:56:52 +0100
Subject: [PATCH 101/119] #57 Implement phase 1 of SSA Exit - make conventional
 SSA

---
 .../ezlang/compiler/BasicBlock.java           |   5 +
 .../ezlang/compiler/ExitSSA2.java             | 127 ++++++++++++++++++
 .../ezlang/compiler/Instruction.java          |  63 +++++++++
 3 files changed, 195 insertions(+)
 create mode 100644 optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA2.java

diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/BasicBlock.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/BasicBlock.java
index 51f7d51..e45d63b 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/BasicBlock.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/BasicBlock.java
@@ -158,6 +158,11 @@ public int whichPred(BasicBlock pred) {
         }
         throw new IllegalStateException();
     }
+    public BasicBlock predecessor(int i) {
+        if (i >= predecessors.size())
+            return null;
+        return predecessors.get(i);
+    }
     public int whichSucc(BasicBlock succ) {
         int i = 0;
         for (BasicBlock s: successors) {
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA2.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA2.java
new file mode 100644
index 0000000..40e4025
--- /dev/null
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA2.java
@@ -0,0 +1,127 @@
+package com.compilerprogramming.ezlang.compiler;
+
+import java.util.*;
+
+/**
+ * Implement method to exit SSA by converting to conventional SSA,
+ * without coalescing. This is the basic form.
+ */
+public class ExitSSA2 {
+
+    CompiledFunction function;
+    Map<BasicBlock,PCopy> parallelCopies = new HashMap<>();
+    List<BasicBlock> allBlocks;
+
+    public ExitSSA2(CompiledFunction function, EnumSet<Options> options) {
+        this.function = function;
+        allBlocks = function.getBlocks();
+        insertPCopiesForEachBlock();
+        makeConventionalSSA();
+        removePhis();
+        sequentialzeParallelCopies();
+    }
+
+    private void insertPCopiesForEachBlock() {
+        // We do not actually insert pcopy instruction until needed
+        // but we create an auxiliary data structure to help us track these
+        for (BasicBlock block: allBlocks) {
+            parallelCopies.put(block,new PCopy(block));
+        }
+    }
+    private void insertAtEnd(BasicBlock bb, Instruction i) {
+        assert bb.instructions.size() > 0;
+        // Last instruction is a branch - so new instruction will
+        // go before that
+        int pos = bb.instructions.size()-1;
+        bb.add(pos, i);
+    }
+
+    private Instruction.ParallelCopyInstruction getParallelCopyAtEnd(BasicBlock block) {
+        PCopy pcopy = parallelCopies.get(block);
+        if (pcopy.pCopyEnd == null) {
+            pcopy.pCopyEnd = new Instruction.ParallelCopyInstruction();
+            insertAtEnd(block,pcopy.pCopyEnd);
+        }
+        return pcopy.pCopyEnd;
+    }
+
+    private void insertAfterPhis(BasicBlock bb, Instruction newInst) {
+        assert bb.instructions.size() > 0;
+        int insertionPos = -1;
+        for (int pos = 0; pos < bb.instructions.size(); pos++) {
+            Instruction i = bb.instructions.get(pos);
+            if (i instanceof Instruction.Phi) {
+                insertionPos = pos+1;   // After phi
+            }
+            else
+                break;
+        }
+        if (insertionPos < 0) {
+            throw new IllegalStateException();
+        }
+        bb.add(insertionPos, newInst);
+    }
+
+    private Instruction.ParallelCopyInstruction getParallelCopyAtBegin(BasicBlock block) {
+        PCopy pcopy = parallelCopies.get(block);
+        if (pcopy.pCopyBegin == null) {
+            pcopy.pCopyBegin = new Instruction.ParallelCopyInstruction();
+            insertAfterPhis(block,pcopy.pCopyBegin);
+        }
+        return pcopy.pCopyBegin;
+    }
+
+    /**
+     * Isolate phi nodes to make SSA conventionalS
+     * This is Phase 1 as described in Engineering a Compiler 3rd Edition, p490.
+     */
+    private void makeConventionalSSA() {
+        var blocks = function.getBlocks();
+        for (BasicBlock block: blocks) {
+            var phis = block.phis();
+            if (phis.isEmpty())
+                continue;
+            for (var phi: phis) {
+                for (int j = 0; j < phi.numInputs(); j++) {
+                    BasicBlock pred = block.predecessor(j);
+                    var pCopyBEnd = getParallelCopyAtEnd(pred);
+                    var oldInput = phi.input(j);
+                    var newInput = function.registerPool.newTempReg(oldInput.type);
+                    pCopyBEnd.addCopy(oldInput,new Operand.RegisterOperand(newInput));
+                    phi.replaceInput(j,newInput);
+                }
+                var oldPhiVar = phi.value();
+                var newPhiVar = function.registerPool.newTempReg(oldPhiVar.type);
+                phi.replaceValue(newPhiVar);
+                var pCopyBBegin = getParallelCopyAtBegin(block);
+                pCopyBBegin.addCopy(new Operand.RegisterOperand(newPhiVar),new Operand.RegisterOperand(oldPhiVar));
+            }
+        }
+    }
+
+    private void removePhis() {
+
+    }
+
+    private void sequentialzeParallelCopies() {
+
+    }
+
+    static final class PCopy {
+        BasicBlock block;
+        /**
+         * Parallel copy instruction after any Phi instructions
+         * in the block, null if not present
+         */
+        Instruction.ParallelCopyInstruction pCopyBegin = null;
+        /**
+         * Parallel copy instruction at the end of a block,
+         * null if not present
+         */
+        Instruction.ParallelCopyInstruction pCopyEnd = null;
+
+        public PCopy(BasicBlock block) {
+            this.block = block;
+        }
+    }
+}
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java
index 3ea0f44..b4b506a 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java
@@ -25,6 +25,7 @@ public abstract class Instruction {
     static final int I_ARRAY_LOAD = 13;
     static final int I_FIELD_GET = 14;
     static final int I_FIELD_SET = 15;
+    static final int I_PARALLEL_COPY = 16;
 
     public final int opcode;
     protected Operand.RegisterOperand def;
@@ -477,5 +478,67 @@ public StringBuilder toStr(StringBuilder sb) {
         }
     }
 
+    /**
+     * The parallel copy instruction is only used temporarily to exit
+     * SSA form. It represents the parallel copy semantics of phi instructions.
+     */
+    public static class ParallelCopyInstruction extends Instruction {
+
+        List<Operand> sourceOperands = new ArrayList<>();
+        List<Operand.RegisterOperand> destOperands = new ArrayList<>();
+
+        protected ParallelCopyInstruction() {
+            super(I_PARALLEL_COPY);
+        }
+
+        @Override
+        public StringBuilder toStr(StringBuilder sb) {
+            sb.append("(");
+            for (int i = 0; i < destOperands.size(); i++) {
+                if (i > 0)
+                    sb.append(",");
+                sb.append(destOperands.get(i));
+            }
+            sb.append(") = (");
+            for (int i = 0; i < sourceOperands.size(); i++) {
+                if (i > 0)
+                    sb.append(",");
+                sb.append(sourceOperands.get(i));
+            }
+            sb.append(")");
+            return sb;
+        }
+
+        public void addCopy(Operand sourceOperand, Operand.RegisterOperand destOperand) {
+            sourceOperands.add(sourceOperand);
+            destOperands.add(destOperand);
+        }
+
+        @Override
+        public Register def() {
+            throw new UnsupportedOperationException();
+        }
+        @Override
+        public void replaceDef(Register newDef) {
+            throw new UnsupportedOperationException();
+        }
+        @Override
+        public boolean definesVar() {
+            return false;
+        }
+        @Override
+        public List<Register> uses() {
+            return Collections.emptyList();
+        }
+        @Override
+        public void replaceUses(Register[] newUses) {
+            throw new UnsupportedOperationException();
+        }
+        @Override
+        public boolean replaceUse(Register source, Register target) {
+            throw new UnsupportedOperationException();
+        }
+    }
+
     public abstract StringBuilder toStr(StringBuilder sb);
 }

From fe514cf896410059b19939b7373fa11b5b0121c7 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Sun, 10 Aug 2025 16:46:17 +0100
Subject: [PATCH 102/119] #57 remove phis

---
 .../ezlang/compiler/ExitSSA2.java             | 22 ++++++++++++++++---
 1 file changed, 19 insertions(+), 3 deletions(-)

diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA2.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA2.java
index 40e4025..ef8f9a2 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA2.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA2.java
@@ -18,7 +18,7 @@ public ExitSSA2(CompiledFunction function, EnumSet<Options> options) {
         insertPCopiesForEachBlock();
         makeConventionalSSA();
         removePhis();
-        sequentialzeParallelCopies();
+        sequenceParallelCopies();
     }
 
     private void insertPCopiesForEachBlock() {
@@ -100,10 +100,26 @@ private void makeConventionalSSA() {
     }
 
     private void removePhis() {
-
+        var blocks = function.getBlocks();
+        for (BasicBlock block: blocks) {
+            var phis = block.phis();
+            if (phis.isEmpty())
+                continue;
+            // Insert copy in predecessor
+            for (var phi: phis) {
+                for (int j = 0; j < phi.numInputs(); j++) {
+                    BasicBlock pred = block.predecessor(j);
+                    var pCopyBEnd = getParallelCopyAtEnd(pred);
+                    var phiInput = phi.input(j);
+                    var phiVar = phi.value();
+                    pCopyBEnd.addCopy(phiInput,new Operand.RegisterOperand(phiVar));
+                }
+            }
+            block.instructions.removeIf(instruction -> instruction instanceof Instruction.Phi);
+        }
     }
 
-    private void sequentialzeParallelCopies() {
+    private void sequenceParallelCopies() {
 
     }
 

From c77a931de1fea400bdc6e1e8ee4e5566df16c855 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Sun, 10 Aug 2025 23:22:53 +0100
Subject: [PATCH 103/119] #57 Operands (constants and register operands) can be
 compared for equality

---
 .../ezlang/compiler/Operand.java              | 28 +++++++++++++++++++
 1 file changed, 28 insertions(+)

diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Operand.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Operand.java
index f6af988..7759cbc 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Operand.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Operand.java
@@ -3,6 +3,8 @@
 import com.compilerprogramming.ezlang.types.Symbol;
 import com.compilerprogramming.ezlang.types.EZType;
 
+import java.util.Objects;
+
 public class Operand {
 
     EZType type;
@@ -17,6 +19,19 @@ public ConstantOperand(long value, EZType type) {
         public String toString() {
             return String.valueOf(value);
         }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            ConstantOperand that = (ConstantOperand) o;
+            return value == that.value;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hashCode(value);
+        }
     }
 
     public static class NullConstantOperand extends Operand {
@@ -46,6 +61,19 @@ public RegisterOperand copy(Register register) {
         public String toString() {
             return reg.name();
         }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            RegisterOperand operand = (RegisterOperand) o;
+            return Objects.equals(reg, operand.reg);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hashCode(reg);
+        }
     }
 
     public static class LocalRegisterOperand extends RegisterOperand {

From ce644ee08265b77de023bf38831110e4f47afc35 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Sun, 10 Aug 2025 23:23:23 +0100
Subject: [PATCH 104/119] #57 Utility to replace 1 instruction with many

---
 .../ezlang/compiler/BasicBlock.java                  | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/BasicBlock.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/BasicBlock.java
index e45d63b..d44eaf4 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/BasicBlock.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/BasicBlock.java
@@ -172,7 +172,17 @@ public int whichSucc(BasicBlock succ) {
         }
         throw new IllegalStateException();
     }
-
+    public void replaceInstruction(Instruction instruction, List<Instruction> replacements) {
+        int i;
+        for (i = 0; i < instructions.size(); i++)
+            if (instructions.get(i) == instruction)
+                break;
+        assert i < instructions.size();
+        for (int j = replacements.size()-1; j >= 0; j--) {
+            instructions.add(i+1, replacements.get(j));
+        }
+        instructions.remove(i);
+    }
     public static StringBuilder toStr(StringBuilder sb, BasicBlock bb, BitSet visited, boolean dumpLiveness)
     {
         if (visited.get(bb.bid))

From b54ee29d9ff2cab9e4913b21d43adecdc3bae655 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Sun, 10 Aug 2025 23:25:04 +0100
Subject: [PATCH 105/119] #57 WIP sequence parallel copies

---
 .../ezlang/compiler/ExitSSA2.java             | 115 ++++++++++++++++++
 1 file changed, 115 insertions(+)

diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA2.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA2.java
index ef8f9a2..0249e37 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA2.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA2.java
@@ -120,7 +120,122 @@ private void removePhis() {
     }
 
     private void sequenceParallelCopies() {
+        for (var block: function.getBlocks()) {
+            var pcopy = parallelCopies.get(block);
+            if (pcopy.pCopyBegin != null)
+                sequenceParallelCopy(block,pcopy.pCopyBegin);
+            if (pcopy.pCopyEnd != null)
+                sequenceParallelCopy(block,pcopy.pCopyEnd);
+        }
+    }
+
+    private void replaceInstruction(BasicBlock block, Instruction.ParallelCopyInstruction pcopy, ArrayList<Instruction> instructions) {
+        block.replaceInstruction(pcopy, instructions);
+    }
+
+    static final class Copy {
+        Operand src;
+        Operand dest;
+        boolean removed = false;
+
+        public Copy(Operand src, Operand dest) {
+            this.src = src;
+            this.dest = dest;
+        }
+    }
+
+    List<Copy> getCopies(Instruction.ParallelCopyInstruction pcopy) {
+        List<Copy> copies = new ArrayList<>();
+        for (int i = 0; i < pcopy.sourceOperands.size(); i++) {
+            var src = pcopy.sourceOperands.get(i);
+            var dest = pcopy.destOperands.get(i);
+            if (src instanceof Operand.RegisterOperand srcR && dest instanceof Operand.RegisterOperand destR) {
+                if (srcR.reg.id == destR.reg.id)
+                    continue;
+            }
+            copies.add(new Copy(src,dest));
+        }
+        return copies;
+    }
+
+    private void sequenceParallelCopy(BasicBlock block, Instruction.ParallelCopyInstruction pcopy) {
+        var copyInstructions = new ArrayList<Instruction>();
+        var copies = getCopies(pcopy);
+
+        while (copies.size() > 0) {
+            boolean progress = false;
+
+            for (var copy: copies) {
+                boolean cycle = false;
+                for (int i = 0; i < copies.size(); i++) {
+                    if (copy.removed == true)
+                        continue;
+                    if (copy.src.equals(copies.get(i).dest)) {
+                        cycle = true;
+                        break;
+                    }
+                }
+                if (!cycle) {
+                    copyInstructions.add(new Instruction.Move(copy.src,copy.dest));
+                    copy.removed = true;
+                    progress = true;
+                }
+            }
+
+            copies.removeIf(c->c.removed);
+            if (progress)
+                continue;
+
+            var copy = copies.removeFirst();
+            var temp = new Operand.RegisterOperand(function.registerPool.newTempReg(copy.src.type));
+            copyInstructions.add(new Instruction.Move(copy.src,temp));
+            copies.add(new Copy(copy.dest,temp));
+        }
+        replaceInstruction(block,pcopy,copyInstructions);
+    }
 
+    private void sequenceParallelCopyX(BasicBlock block, Instruction.ParallelCopyInstruction pcopy) {
+        var copyInstructions = new ArrayList<Instruction>();
+        var ready = new ArrayList<Operand>();
+        var toDo = new ArrayList<Operand>();
+        var directPred = new HashMap<Operand,Operand>();
+        var loc = new HashMap<Operand,Operand>();
+        for (int i = 0; i <pcopy.sourceOperands.size(); i++) {
+            var a = pcopy.sourceOperands.get(i);
+            var b = pcopy.destOperands.get(i);
+            if (a.equals(b))
+                continue;
+            loc.put(a,a);
+            directPred.put(b,a);
+            toDo.add(b);
+        }
+        for (int i = 0; i <pcopy.sourceOperands.size(); i++) {
+            var a = pcopy.sourceOperands.get(i);
+            var b = pcopy.destOperands.get(i);
+            if (a == b)
+                continue;
+            if (loc.get(b) == null)
+                ready.add(b);
+        }
+        while (!toDo.isEmpty()) {
+            while (!ready.isEmpty()) {
+                var b = ready.removeLast();
+                var a = directPred.get(b);
+                var c = loc.get(a);
+                copyInstructions.add(new Instruction.Move(c,b));
+                loc.put(a,b);
+                if (a == c && directPred.get(a) != null)
+                    ready.add(a);
+            }
+            var b = toDo.removeLast();
+            if (b == loc.get(directPred.get(b))) {
+                var n = new Operand.RegisterOperand(function.registerPool.newTempReg(b.type));
+                copyInstructions.add(new Instruction.Move(b,n));
+                loc.put(b,n);
+                ready.add(b);
+            }
+        }
+        replaceInstruction(block,pcopy,copyInstructions);
     }
 
     static final class PCopy {

From 6ae04dec3c11b6aebb7d3f0e4cec78d0630aa9a9 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Mon, 11 Aug 2025 10:17:33 +0100
Subject: [PATCH 106/119] #57 WIP sequence parallel copies

---
 .../ezlang/compiler/ExitSSA2.java             | 144 +++++++-----------
 1 file changed, 53 insertions(+), 91 deletions(-)

diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA2.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA2.java
index 0249e37..0010abb 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA2.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA2.java
@@ -133,111 +133,73 @@ private void replaceInstruction(BasicBlock block, Instruction.ParallelCopyInstru
         block.replaceInstruction(pcopy, instructions);
     }
 
-    static final class Copy {
-        Operand src;
-        Operand dest;
-        boolean removed = false;
+    private boolean isEqual(Operand op1, Operand op2) {
+        if (op1 instanceof Operand.RegisterOperand reg1 && op2 instanceof Operand.RegisterOperand reg2)
+            return reg1.reg.id == reg2.reg.id;
+        return false;
+    }
 
-        public Copy(Operand src, Operand dest) {
-            this.src = src;
-            this.dest = dest;
-        }
+    // The parallel move algo below is from
+    // https://xavierleroy.org/publi/parallel-move.pdf
+    // Tilting at windmills with Coq:
+    // formal verification of a compilation algorithm
+    // for parallel moves
+    // Laurence Rideau, Bernard Paul Serpette, Xavier Leroy
+    enum MoveStatus {
+        TO_MOVE, BEING_MOVED, MOVED
     }
 
-    List<Copy> getCopies(Instruction.ParallelCopyInstruction pcopy) {
-        List<Copy> copies = new ArrayList<>();
-        for (int i = 0; i < pcopy.sourceOperands.size(); i++) {
-            var src = pcopy.sourceOperands.get(i);
-            var dest = pcopy.destOperands.get(i);
-            if (src instanceof Operand.RegisterOperand srcR && dest instanceof Operand.RegisterOperand destR) {
-                if (srcR.reg.id == destR.reg.id)
-                    continue;
-            }
-            copies.add(new Copy(src,dest));
+    static final class MoveCtx {
+        Operand[] src;
+        Operand[] dst;
+        MoveStatus[] status;
+        ArrayList<Instruction> copyInstructions;
+
+        MoveCtx(Instruction.ParallelCopyInstruction pcopy) {
+            src = pcopy.sourceOperands.toArray(new Operand[0]);
+            dst = pcopy.destOperands.toArray(new Operand[0]);
+            copyInstructions = new ArrayList<Instruction>();
+            status = new MoveStatus[src.length];
+            Arrays.fill(status, MoveStatus.TO_MOVE);
         }
-        return copies;
     }
 
-    private void sequenceParallelCopy(BasicBlock block, Instruction.ParallelCopyInstruction pcopy) {
-        var copyInstructions = new ArrayList<Instruction>();
-        var copies = getCopies(pcopy);
-
-        while (copies.size() > 0) {
-            boolean progress = false;
-
-            for (var copy: copies) {
-                boolean cycle = false;
-                for (int i = 0; i < copies.size(); i++) {
-                    if (copy.removed == true)
-                        continue;
-                    if (copy.src.equals(copies.get(i).dest)) {
-                        cycle = true;
-                        break;
+    private void moveOne(MoveCtx ctx, int i) {
+        Operand[] src = ctx.src;
+        Operand[] dst = ctx.dst;
+        if (!isEqual(src[i], dst[i])) {
+            ctx.status[i] = MoveStatus.BEING_MOVED;
+            for (int j = 0; j < src.length; j++) {
+                if (isEqual(src[j],dst[i])) {
+                    // cycle found
+                    switch (ctx.status[j]) {
+                        case TO_MOVE:
+                            moveOne(ctx, j);
+                            break;
+                        case BEING_MOVED:
+                            var temp = new Operand.RegisterOperand(function.registerPool.newTempReg(src[j].type));
+                            ctx.copyInstructions.add(new Instruction.Move(src[j], temp));
+                            src[j] = temp;
+                            break;
+                        case MOVED:
+                            break;
                     }
                 }
-                if (!cycle) {
-                    copyInstructions.add(new Instruction.Move(copy.src,copy.dest));
-                    copy.removed = true;
-                    progress = true;
-                }
             }
-
-            copies.removeIf(c->c.removed);
-            if (progress)
-                continue;
-
-            var copy = copies.removeFirst();
-            var temp = new Operand.RegisterOperand(function.registerPool.newTempReg(copy.src.type));
-            copyInstructions.add(new Instruction.Move(copy.src,temp));
-            copies.add(new Copy(copy.dest,temp));
+            ctx.copyInstructions.add(new Instruction.Move(src[i], dst[i]));
+            ctx.status[i] = MoveStatus.MOVED;
         }
-        replaceInstruction(block,pcopy,copyInstructions);
     }
 
-    private void sequenceParallelCopyX(BasicBlock block, Instruction.ParallelCopyInstruction pcopy) {
-        var copyInstructions = new ArrayList<Instruction>();
-        var ready = new ArrayList<Operand>();
-        var toDo = new ArrayList<Operand>();
-        var directPred = new HashMap<Operand,Operand>();
-        var loc = new HashMap<Operand,Operand>();
-        for (int i = 0; i <pcopy.sourceOperands.size(); i++) {
-            var a = pcopy.sourceOperands.get(i);
-            var b = pcopy.destOperands.get(i);
-            if (a.equals(b))
-                continue;
-            loc.put(a,a);
-            directPred.put(b,a);
-            toDo.add(b);
-        }
-        for (int i = 0; i <pcopy.sourceOperands.size(); i++) {
-            var a = pcopy.sourceOperands.get(i);
-            var b = pcopy.destOperands.get(i);
-            if (a == b)
-                continue;
-            if (loc.get(b) == null)
-                ready.add(b);
-        }
-        while (!toDo.isEmpty()) {
-            while (!ready.isEmpty()) {
-                var b = ready.removeLast();
-                var a = directPred.get(b);
-                var c = loc.get(a);
-                copyInstructions.add(new Instruction.Move(c,b));
-                loc.put(a,b);
-                if (a == c && directPred.get(a) != null)
-                    ready.add(a);
-            }
-            var b = toDo.removeLast();
-            if (b == loc.get(directPred.get(b))) {
-                var n = new Operand.RegisterOperand(function.registerPool.newTempReg(b.type));
-                copyInstructions.add(new Instruction.Move(b,n));
-                loc.put(b,n);
-                ready.add(b);
-            }
-        }
-        replaceInstruction(block,pcopy,copyInstructions);
+    private void sequenceParallelCopy(BasicBlock block, Instruction.ParallelCopyInstruction parallelCopyInstruction) {
+        var ctx = new MoveCtx(parallelCopyInstruction);
+        for (int i = 0; i < ctx.src.length; i++)
+            if (ctx.status[i] == MoveStatus.TO_MOVE)
+                moveOne(ctx,i);
+        replaceInstruction(block,parallelCopyInstruction,ctx.copyInstructions);
     }
 
+
     static final class PCopy {
         BasicBlock block;
         /**

From eb71a9b61dce3c19b3b168d199cf50e5172ec8b8 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Mon, 11 Aug 2025 10:18:11 +0100
Subject: [PATCH 107/119] #57 bug fix - the phi elimination must use simple
 copy

---
 .../com/compilerprogramming/ezlang/compiler/ExitSSA2.java   | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA2.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA2.java
index 0010abb..f9bcda8 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA2.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA2.java
@@ -105,14 +105,14 @@ private void removePhis() {
             var phis = block.phis();
             if (phis.isEmpty())
                 continue;
-            // Insert copy in predecessor
+            // Insert copy in predecessor, since we are in CSSA, this is
+            // a simple assignment from phi input to phi var
             for (var phi: phis) {
                 for (int j = 0; j < phi.numInputs(); j++) {
                     BasicBlock pred = block.predecessor(j);
-                    var pCopyBEnd = getParallelCopyAtEnd(pred);
                     var phiInput = phi.input(j);
                     var phiVar = phi.value();
-                    pCopyBEnd.addCopy(phiInput,new Operand.RegisterOperand(phiVar));
+                    insertAtEnd(pred,new Instruction.Move(phiInput,new Operand.RegisterOperand(phiVar)));
                 }
             }
             block.instructions.removeIf(instruction -> instruction instanceof Instruction.Phi);

From cc1d9bdc7aae70ccd9ff46aa91885e7278506b96 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Mon, 11 Aug 2025 10:24:27 +0100
Subject: [PATCH 108/119] #57 bug fix - set out of ssa flag

---
 .../java/com/compilerprogramming/ezlang/compiler/ExitSSA2.java   | 1 +
 1 file changed, 1 insertion(+)

diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA2.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA2.java
index f9bcda8..0e19d7c 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA2.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA2.java
@@ -19,6 +19,7 @@ public ExitSSA2(CompiledFunction function, EnumSet<Options> options) {
         makeConventionalSSA();
         removePhis();
         sequenceParallelCopies();
+        function.isSSA = false;
     }
 
     private void insertPCopiesForEachBlock() {

From e3ae2f7e318cb144988e52775888d334e499f917 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Mon, 11 Aug 2025 10:54:55 +0100
Subject: [PATCH 109/119] #57 refactor

---
 .../ezlang/compiler/ExitSSA.java              | 275 +----------------
 ...2.java => ExitSSABoissinotNoCoalesce.java} |  45 ++-
 .../ezlang/compiler/ExitSSABriggs.java        | 282 ++++++++++++++++++
 .../ezlang/compiler/Options.java              |   2 +
 4 files changed, 327 insertions(+), 277 deletions(-)
 rename optvm/src/main/java/com/compilerprogramming/ezlang/compiler/{ExitSSA2.java => ExitSSABoissinotNoCoalesce.java} (82%)
 create mode 100644 optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSABriggs.java

diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA.java
index af66aff..bbf86e8 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA.java
@@ -4,279 +4,12 @@
 
 /**
  * Converts from SSA form to non-SSA form.
- * Implementation is based on description in
- * 'Practical Improvements to the Construction and Destruction
- * of Static Single Assignment Form' by Preston Briggs.
- *
- * The JikesRVM LeaveSSA implements a version of the
- * same algorithm.
  */
 public class ExitSSA {
-
-    CompiledFunction function;
-    NameStack[] stacks;
-    DominatorTree tree;
-
     public ExitSSA(CompiledFunction function, EnumSet<Options> options) {
-        this.function = function;
-        if (!function.isSSA) throw new IllegalStateException();
-        function.livenessAnalysis();
-        if (options.contains(Options.DUMP_SSA_LIVENESS)) function.dumpIR(true, "SSA Liveness Analysis");
-        tree = new DominatorTree(function.entry);
-        if (options.contains(Options.DUMP_SSA_DOMTREE)) {
-            System.out.println("Pre SSA Dominator Tree");
-            System.out.println(tree.generateDotOutput());
-        }
-        initStack();
-        insertCopies(function.entry);
-        removePhis();
-        function.isSSA = false;
-        if (options.contains(Options.DUMP_POST_SSA_IR)) function.dumpIR(false, "After exiting SSA");
-    }
-
-    private void removePhis() {
-        for (BasicBlock block : tree.blocks) {
-            block.instructions.removeIf(instruction -> instruction instanceof Instruction.Phi);
-        }
-    }
-
-    /* Algorithm for iterating through blocks to perform phi replacement */
-    private void insertCopies(BasicBlock block) {
-        List<Integer> pushed = new ArrayList<>();
-        for (Instruction i: block.instructions) {
-            // replace all uses u with stacks[i]
-            replaceUses(i);
-        }
-        scheduleCopies(block, pushed);
-        for (BasicBlock c: block.dominatedChildren) {
-            insertCopies(c);
-        }
-        for (Integer name: pushed) {
-            stacks[name].pop();
-        }
-    }
-
-    /**
-     * replace all uses u with stacks[i]
-     */
-    private void replaceUses(Instruction i) {
-        if (i instanceof Instruction.Phi)
-            // FIXME check this can never be valid
-            // tests 8/9 in TestInterpreter invoke on Phi but
-            // replacements are same as existing inputs
-            return;
-        var oldUses = i.uses();
-        Register[] newUses = new Register[oldUses.size()];
-        for (int u = 0; u < oldUses.size(); u++) {
-            Register use = oldUses.get(u);
-            if (!stacks[use.id].isEmpty())
-                newUses[u] = stacks[use.id].top();
-            else
-                newUses[u] = use;
-        }
-        i.replaceUses(newUses);
-    }
-
-    static class CopyItem {
-        /** Phi input can be a register or a constant so we record the operand */
-        final Operand src;
-        /** The phi destination */
-        final Register dest;
-        /** The basic block where the phi was present */
-        final BasicBlock destBlock;
-        boolean removed;
-
-        public CopyItem(Operand src, Register dest, BasicBlock destBlock) {
-            this.src = src;
-            this.dest = dest;
-            this.destBlock = destBlock;
-            this.removed = false;
-        }
-    }
-
-    private void scheduleCopies(BasicBlock block, List<Integer> pushed) {
-        /* Pass 1 - Initialize data structures */
-        /* In this pass we count the number of times a name is used by other phi-nodes */
-        List<CopyItem> copySet = new ArrayList<>();
-        Map<Integer, Register> map = new HashMap<>();
-        BitSet usedByAnother = new BitSet(function.registerPool.numRegisters()*2);
-        for (BasicBlock s: block.successors) {
-            int j = s.whichPred(block);
-            for (Instruction.Phi phi: s.phis()) {
-                Register dst = phi.value();
-                Operand srcOperand = phi.input(j); // jth operand of phi node
-                if (srcOperand instanceof Operand.RegisterOperand srcRegisterOperand) {
-                    Register src = srcRegisterOperand.reg;
-                    map.put(src.id, src);
-                    usedByAnother.set(src.id);
-                }
-                copySet.add(new CopyItem(srcOperand, dst, s));
-                map.put(dst.id, dst);
-            }
-        }
-
-        /* Pass 2: setup up the worklist of initial copies */
-        /* In this pass we build a worklist of names that are not used in other phi nodes */
-        List<CopyItem> workList = new ArrayList<>();
-        for (CopyItem copyItem: copySet) {
-            if (usedByAnother.get(copyItem.dest.id) != true) {
-                copyItem.removed = true;
-                workList.add(copyItem);
-            }
-        }
-        copySet.removeIf(copyItem -> copyItem.removed);
-
-        /* Pass 3: iterate over the worklist, inserting copies */
-        /* Copy operations whose destinations are not used by other copy operations can be scheduled immediately */
-        /* Each time we insert a copy operation we add the source of that op to the worklist */
-        while (!workList.isEmpty() || !copySet.isEmpty()) {
-            while (!workList.isEmpty()) {
-                final CopyItem copyItem = workList.remove(0);
-                final Operand src = copyItem.src;
-                final Register dest = copyItem.dest;
-                final BasicBlock destBlock = copyItem.destBlock;
-                /* Engineering a Compiler: We can avoid the lost copy
-                   problem by checking the liveness of the target name
-                   for each copy that we try to insert. When we discover
-                   a copy target that is live, we must preserve the live
-                   value in a temporary name and rewrite subsequent uses to
-                   refer to the temporary name.
-
-                   This captures the cases when the result of a phi
-                   in a control successor is live on exit of the current block.
-                   This means that it is incorrect to simply insert a copy
-                   of the destination in the current block. So we rename
-                   the destination to a new temporary, and record the renaming
-                   so that the dominator blocks get the new name. Comment adapted
-                   from JikesRVM LeaveSSA
-                 */
-                if (block.liveOut.get(dest.id)) {
-                    /* Insert a copy from dest to a new temp t at phi node defining dest */
-                    final Register t = addMoveToTempAfterPhi(destBlock, dest);
-                    stacks[dest.id].push(t);    // record the temp name
-                    pushed.add(dest.id);
-                }
-                /* Insert a copy operation from map[src] to dest at end of BB */
-                if (src instanceof Operand.RegisterOperand srcRegisterOperand) {
-                    addMoveAtBBEnd(block, map.get(srcRegisterOperand.reg.id), dest);
-                    map.put(srcRegisterOperand.reg.id, dest);
-                    /* If src is the name of a dest in copySet add item to worklist */
-                    /* see comment on phi cycles below. */
-                    CopyItem item = isCycle(copySet, srcRegisterOperand.reg);
-                    if (item != null) {
-                        workList.add(item);
-                    }
-                }
-                else if (src instanceof Operand.ConstantOperand srcConstantOperand) {
-                    addMoveAtBBEnd(block, srcConstantOperand, dest);
-                }
-            }
-            /* Engineering a Compiler: To solve the swap problem
-               we can detect cases where phi functions reference the
-               targets of other phi functions in the same block. For each
-               cycle of references, it must insert a copy to a temporary
-               that breaks the cycle. Then we can schedule the copies to
-               respect the dependencies implied by the phi functions.
-
-               An empty work list with work remaining in the copy set
-               implies a cycle in the dependencies amongst copies. To break
-               the cycle copy the destination of an arbitrary member of the
-               copy set to a temporary. This destination has therefore been
-               saved and can be safely overwritten. So then add the copy to the
-               work list. Comment adapted from JikesRVM LeaveSSA.
-             */
-            if (!copySet.isEmpty()) {
-                CopyItem copyItem = copySet.remove(0);
-                /* Insert a copy from dst to new temp at the end of Block */
-                Register t = addMoveToTempAtBBEnd(block, copyItem.dest);
-                map.put(copyItem.dest.id, t);
-                workList.add(copyItem);
-            }
-        }
-    }
-
-    private void insertAtEnd(BasicBlock bb, Instruction i) {
-        assert bb.instructions.size() > 0;
-        // Last instruction is a branch - so new instruction will
-        // go before that
-        int pos = bb.instructions.size()-1;
-        bb.add(pos, i);
-    }
-
-    private void insertAfterPhi(BasicBlock bb, Register phiDef, Instruction newInst) {
-        assert bb.instructions.size() > 0;
-        int insertionPos = -1;
-        for (int pos = 0; pos < bb.instructions.size(); pos++) {
-            Instruction i = bb.instructions.get(pos);
-            if (i instanceof Instruction.Phi phi) {
-                if (phi.value().id == phiDef.id) {
-                    insertionPos = pos+1;   // After phi
-                    break;
-                }
-            }
-        }
-        if (insertionPos < 0) {
-            throw new IllegalStateException();
-        }
-        bb.add(insertionPos, newInst);
-    }
-
-    /* Insert a copy from dest to new temp at end of BB, and return temp */
-    private Register addMoveToTempAtBBEnd(BasicBlock block, Register dest) {
-        var temp = function.registerPool.newTempReg(dest.name(), dest.type);
-        var inst = new Instruction.Move(new Operand.RegisterOperand(dest), new Operand.RegisterOperand(temp));
-        insertAtEnd(block, inst);
-        return temp;
-    }
-
-    /* If src is the name of a dest in copySet remove the item */
-    private CopyItem isCycle(List<CopyItem> copySet, Register src) {
-        for (int i = 0; i < copySet.size(); i++) {
-            CopyItem copyItem = copySet.get(i);
-            if (copyItem.dest.id == src.id) {
-                copySet.remove(i);
-                return copyItem;
-            }
-        }
-        return null;
-    }
-
-    /* Insert a copy from src to dst at end of BB */
-    private void addMoveAtBBEnd(BasicBlock block, Register src, Register dest) {
-        var inst = new Instruction.Move(new Operand.RegisterOperand(src), new Operand.RegisterOperand(dest));
-        insertAtEnd(block, inst);
-        // If the copy instruction is followed by a cbr which uses the old var
-        // then we need to update the cbr instruction
-        // This is not specified in the Briggs paper but t
-        var brInst = block.instructions.getLast();
-        if (brInst instanceof Instruction.ConditionalBranch cbr) {
-            cbr.replaceUse(src,dest);
-        }
-    }
-    /* Insert a copy from constant src to dst at end of BB */
-    private void addMoveAtBBEnd(BasicBlock block, Operand.ConstantOperand src, Register dest) {
-        var inst = new Instruction.Move(src, new Operand.RegisterOperand(dest));
-        insertAtEnd(block, inst);
-    }
-    /* Insert a copy dest to a new temp at phi node defining dest, return temp */
-    private Register addMoveToTempAfterPhi(BasicBlock block, Register dest) {
-        var temp = function.registerPool.newTempReg(dest.name(), dest.type);
-        var inst = new Instruction.Move(new Operand.RegisterOperand(dest), new Operand.RegisterOperand(temp));
-        insertAfterPhi(block, dest, inst);
-        return temp;
-    }
-
-    private void initStack() {
-        stacks = new NameStack[function.registerPool.numRegisters()];
-        for (int i = 0; i < stacks.length; i++)
-            stacks[i] = new NameStack();
-    }
-
-    static class NameStack {
-        List<Register> stack = new ArrayList<>();
-        void push(Register r) { stack.add(r); }
-        Register top() { return stack.getLast(); }
-        void pop() { stack.removeLast(); }
-        boolean isEmpty() { return stack.isEmpty(); }
+        if (options.contains(Options.SSA_DESTRUCTION_BOISSINOT_NOCOALESCE))
+            new ExitSSABoissinotNoCoalesce(function,options);
+        else
+            new ExitSSABriggs(function,options);
     }
 }
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA2.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSABoissinotNoCoalesce.java
similarity index 82%
rename from optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA2.java
rename to optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSABoissinotNoCoalesce.java
index 0e19d7c..633cf7e 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA2.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSABoissinotNoCoalesce.java
@@ -5,25 +5,50 @@
 /**
  * Implement method to exit SSA by converting to conventional SSA,
  * without coalescing. This is the basic form.
+ *
+ * This is essentially Method 1 described by
+ * Translating Out of Static Single Assignment Form
+ * Vugranam C. Sreedhar, Roy Dz-Ching Ju, David M. Gillies, and Vatsa Santhanam
+ *
+ * However, Sreedhar left out details such as using parallel copies
+ * and sequencing of parallel copies.
+ *
+ * Revisiting Out-of-SSA Translation for Correctness, Code Quality, and Efficiency
+ * Benoit Boissinot, Alain Darte, Fabrice Rastello, Benoît Dupont de Dinechin, Christophe Guillon
+ *
+ * The Boissinot paper gives a more correct description, discussing the need for parallel copy
+ * and sequencing the parallel copy, but the paper describes a
+ * more complex approach that performs coalescing.
+ *
+ * Engineering a Compiler, 3rd edition, describes the simpler form - omitting the coalescing part.
+ *
+ * Our implementation is similar to EaC.
+ *
+ * We do not use the sequencing algo described in Boissinot paper. Instead, we use:
+ *
+ * https://xavierleroy.org/publi/parallel-move.pdf
+ * Tilting at windmills with Coq: formal verification of a compilation algorithm for parallel moves
+ * Laurence Rideau, Bernard Paul Serpette, Xavier Leroy
  */
-public class ExitSSA2 {
+public class ExitSSABoissinotNoCoalesce {
 
     CompiledFunction function;
     Map<BasicBlock,PCopy> parallelCopies = new HashMap<>();
     List<BasicBlock> allBlocks;
 
-    public ExitSSA2(CompiledFunction function, EnumSet<Options> options) {
+    public ExitSSABoissinotNoCoalesce(CompiledFunction function, EnumSet<Options> options) {
         this.function = function;
         allBlocks = function.getBlocks();
-        insertPCopiesForEachBlock();
+        init();
         makeConventionalSSA();
         removePhis();
         sequenceParallelCopies();
         function.isSSA = false;
+        if (options.contains(Options.DUMP_POST_SSA_IR)) function.dumpIR(false, "After exiting SSA");
     }
 
-    private void insertPCopiesForEachBlock() {
-        // We do not actually insert pcopy instruction until needed
+    private void init() {
+        // We do not actually insert parallel copy instruction until needed
         // but we create an auxiliary data structure to help us track these
         for (BasicBlock block: allBlocks) {
             parallelCopies.put(block,new PCopy(block));
@@ -73,8 +98,9 @@ private Instruction.ParallelCopyInstruction getParallelCopyAtBegin(BasicBlock bl
     }
 
     /**
-     * Isolate phi nodes to make SSA conventionalS
+     * Isolate phi nodes to make SSA conventional.
      * This is Phase 1 as described in Engineering a Compiler 3rd Edition, p490.
+     * It is also described as method 1 by Sreedhar, and explained in detail by Boissinot.
      */
     private void makeConventionalSSA() {
         var blocks = function.getBlocks();
@@ -100,6 +126,9 @@ private void makeConventionalSSA() {
         }
     }
 
+    /**
+     * Phase 2 in Engineering a Compiler
+     */
     private void removePhis() {
         var blocks = function.getBlocks();
         for (BasicBlock block: blocks) {
@@ -120,6 +149,9 @@ private void removePhis() {
         }
     }
 
+    /**
+     * Phase 3 in Engineering a Compiler.
+     */
     private void sequenceParallelCopies() {
         for (var block: function.getBlocks()) {
             var pcopy = parallelCopies.get(block);
@@ -146,6 +178,7 @@ private boolean isEqual(Operand op1, Operand op2) {
     // formal verification of a compilation algorithm
     // for parallel moves
     // Laurence Rideau, Bernard Paul Serpette, Xavier Leroy
+
     enum MoveStatus {
         TO_MOVE, BEING_MOVED, MOVED
     }
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSABriggs.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSABriggs.java
new file mode 100644
index 0000000..c7a4c9b
--- /dev/null
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSABriggs.java
@@ -0,0 +1,282 @@
+package com.compilerprogramming.ezlang.compiler;
+
+import java.util.*;
+
+/**
+ * Converts from SSA form to non-SSA form.
+ * Implementation is based on description in
+ * 'Practical Improvements to the Construction and Destruction
+ * of Static Single Assignment Form' by Preston Briggs.
+ *
+ * The JikesRVM LeaveSSA implements a version of the
+ * same algorithm.
+ */
+public class ExitSSABriggs {
+
+    CompiledFunction function;
+    NameStack[] stacks;
+    DominatorTree tree;
+
+    public ExitSSABriggs(CompiledFunction function, EnumSet<Options> options) {
+        this.function = function;
+        if (!function.isSSA) throw new IllegalStateException();
+        function.livenessAnalysis();
+        if (options.contains(Options.DUMP_SSA_LIVENESS)) function.dumpIR(true, "SSA Liveness Analysis");
+        tree = new DominatorTree(function.entry);
+        if (options.contains(Options.DUMP_SSA_DOMTREE)) {
+            System.out.println("Pre SSA Dominator Tree");
+            System.out.println(tree.generateDotOutput());
+        }
+        initStack();
+        insertCopies(function.entry);
+        removePhis();
+        function.isSSA = false;
+        if (options.contains(Options.DUMP_POST_SSA_IR)) function.dumpIR(false, "After exiting SSA");
+    }
+
+    private void removePhis() {
+        for (BasicBlock block : tree.blocks) {
+            block.instructions.removeIf(instruction -> instruction instanceof Instruction.Phi);
+        }
+    }
+
+    /* Algorithm for iterating through blocks to perform phi replacement */
+    private void insertCopies(BasicBlock block) {
+        List<Integer> pushed = new ArrayList<>();
+        for (Instruction i: block.instructions) {
+            // replace all uses u with stacks[i]
+            replaceUses(i);
+        }
+        scheduleCopies(block, pushed);
+        for (BasicBlock c: block.dominatedChildren) {
+            insertCopies(c);
+        }
+        for (Integer name: pushed) {
+            stacks[name].pop();
+        }
+    }
+
+    /**
+     * replace all uses u with stacks[i]
+     */
+    private void replaceUses(Instruction i) {
+        if (i instanceof Instruction.Phi)
+            // FIXME check this can never be valid
+            // tests 8/9 in TestInterpreter invoke on Phi but
+            // replacements are same as existing inputs
+            return;
+        var oldUses = i.uses();
+        Register[] newUses = new Register[oldUses.size()];
+        for (int u = 0; u < oldUses.size(); u++) {
+            Register use = oldUses.get(u);
+            if (!stacks[use.id].isEmpty())
+                newUses[u] = stacks[use.id].top();
+            else
+                newUses[u] = use;
+        }
+        i.replaceUses(newUses);
+    }
+
+    static class CopyItem {
+        /** Phi input can be a register or a constant so we record the operand */
+        final Operand src;
+        /** The phi destination */
+        final Register dest;
+        /** The basic block where the phi was present */
+        final BasicBlock destBlock;
+        boolean removed;
+
+        public CopyItem(Operand src, Register dest, BasicBlock destBlock) {
+            this.src = src;
+            this.dest = dest;
+            this.destBlock = destBlock;
+            this.removed = false;
+        }
+    }
+
+    private void scheduleCopies(BasicBlock block, List<Integer> pushed) {
+        /* Pass 1 - Initialize data structures */
+        /* In this pass we count the number of times a name is used by other phi-nodes */
+        List<CopyItem> copySet = new ArrayList<>();
+        Map<Integer, Register> map = new HashMap<>();
+        BitSet usedByAnother = new BitSet(function.registerPool.numRegisters()*2);
+        for (BasicBlock s: block.successors) {
+            int j = s.whichPred(block);
+            for (Instruction.Phi phi: s.phis()) {
+                Register dst = phi.value();
+                Operand srcOperand = phi.input(j); // jth operand of phi node
+                if (srcOperand instanceof Operand.RegisterOperand srcRegisterOperand) {
+                    Register src = srcRegisterOperand.reg;
+                    map.put(src.id, src);
+                    usedByAnother.set(src.id);
+                }
+                copySet.add(new CopyItem(srcOperand, dst, s));
+                map.put(dst.id, dst);
+            }
+        }
+
+        /* Pass 2: setup up the worklist of initial copies */
+        /* In this pass we build a worklist of names that are not used in other phi nodes */
+        List<CopyItem> workList = new ArrayList<>();
+        for (CopyItem copyItem: copySet) {
+            if (usedByAnother.get(copyItem.dest.id) != true) {
+                copyItem.removed = true;
+                workList.add(copyItem);
+            }
+        }
+        copySet.removeIf(copyItem -> copyItem.removed);
+
+        /* Pass 3: iterate over the worklist, inserting copies */
+        /* Copy operations whose destinations are not used by other copy operations can be scheduled immediately */
+        /* Each time we insert a copy operation we add the source of that op to the worklist */
+        while (!workList.isEmpty() || !copySet.isEmpty()) {
+            while (!workList.isEmpty()) {
+                final CopyItem copyItem = workList.remove(0);
+                final Operand src = copyItem.src;
+                final Register dest = copyItem.dest;
+                final BasicBlock destBlock = copyItem.destBlock;
+                /* Engineering a Compiler: We can avoid the lost copy
+                   problem by checking the liveness of the target name
+                   for each copy that we try to insert. When we discover
+                   a copy target that is live, we must preserve the live
+                   value in a temporary name and rewrite subsequent uses to
+                   refer to the temporary name.
+
+                   This captures the cases when the result of a phi
+                   in a control successor is live on exit of the current block.
+                   This means that it is incorrect to simply insert a copy
+                   of the destination in the current block. So we rename
+                   the destination to a new temporary, and record the renaming
+                   so that the dominator blocks get the new name. Comment adapted
+                   from JikesRVM LeaveSSA
+                 */
+                if (block.liveOut.get(dest.id)) {
+                    /* Insert a copy from dest to a new temp t at phi node defining dest */
+                    final Register t = addMoveToTempAfterPhi(destBlock, dest);
+                    stacks[dest.id].push(t);    // record the temp name
+                    pushed.add(dest.id);
+                }
+                /* Insert a copy operation from map[src] to dest at end of BB */
+                if (src instanceof Operand.RegisterOperand srcRegisterOperand) {
+                    addMoveAtBBEnd(block, map.get(srcRegisterOperand.reg.id), dest);
+                    map.put(srcRegisterOperand.reg.id, dest);
+                    /* If src is the name of a dest in copySet add item to worklist */
+                    /* see comment on phi cycles below. */
+                    CopyItem item = isCycle(copySet, srcRegisterOperand.reg);
+                    if (item != null) {
+                        workList.add(item);
+                    }
+                }
+                else if (src instanceof Operand.ConstantOperand srcConstantOperand) {
+                    addMoveAtBBEnd(block, srcConstantOperand, dest);
+                }
+            }
+            /* Engineering a Compiler: To solve the swap problem
+               we can detect cases where phi functions reference the
+               targets of other phi functions in the same block. For each
+               cycle of references, it must insert a copy to a temporary
+               that breaks the cycle. Then we can schedule the copies to
+               respect the dependencies implied by the phi functions.
+
+               An empty work list with work remaining in the copy set
+               implies a cycle in the dependencies amongst copies. To break
+               the cycle copy the destination of an arbitrary member of the
+               copy set to a temporary. This destination has therefore been
+               saved and can be safely overwritten. So then add the copy to the
+               work list. Comment adapted from JikesRVM LeaveSSA.
+             */
+            if (!copySet.isEmpty()) {
+                CopyItem copyItem = copySet.remove(0);
+                /* Insert a copy from dst to new temp at the end of Block */
+                Register t = addMoveToTempAtBBEnd(block, copyItem.dest);
+                map.put(copyItem.dest.id, t);
+                workList.add(copyItem);
+            }
+        }
+    }
+
+    private void insertAtEnd(BasicBlock bb, Instruction i) {
+        assert bb.instructions.size() > 0;
+        // Last instruction is a branch - so new instruction will
+        // go before that
+        int pos = bb.instructions.size()-1;
+        bb.add(pos, i);
+    }
+
+    private void insertAfterPhi(BasicBlock bb, Register phiDef, Instruction newInst) {
+        assert bb.instructions.size() > 0;
+        int insertionPos = -1;
+        for (int pos = 0; pos < bb.instructions.size(); pos++) {
+            Instruction i = bb.instructions.get(pos);
+            if (i instanceof Instruction.Phi phi) {
+                if (phi.value().id == phiDef.id) {
+                    insertionPos = pos+1;   // After phi
+                    break;
+                }
+            }
+        }
+        if (insertionPos < 0) {
+            throw new IllegalStateException();
+        }
+        bb.add(insertionPos, newInst);
+    }
+
+    /* Insert a copy from dest to new temp at end of BB, and return temp */
+    private Register addMoveToTempAtBBEnd(BasicBlock block, Register dest) {
+        var temp = function.registerPool.newTempReg(dest.name(), dest.type);
+        var inst = new Instruction.Move(new Operand.RegisterOperand(dest), new Operand.RegisterOperand(temp));
+        insertAtEnd(block, inst);
+        return temp;
+    }
+
+    /* If src is the name of a dest in copySet remove the item */
+    private CopyItem isCycle(List<CopyItem> copySet, Register src) {
+        for (int i = 0; i < copySet.size(); i++) {
+            CopyItem copyItem = copySet.get(i);
+            if (copyItem.dest.id == src.id) {
+                copySet.remove(i);
+                return copyItem;
+            }
+        }
+        return null;
+    }
+
+    /* Insert a copy from src to dst at end of BB */
+    private void addMoveAtBBEnd(BasicBlock block, Register src, Register dest) {
+        var inst = new Instruction.Move(new Operand.RegisterOperand(src), new Operand.RegisterOperand(dest));
+        insertAtEnd(block, inst);
+        // If the copy instruction is followed by a cbr which uses the old var
+        // then we need to update the cbr instruction
+        // This is not specified in the Briggs paper but t
+        var brInst = block.instructions.getLast();
+        if (brInst instanceof Instruction.ConditionalBranch cbr) {
+            cbr.replaceUse(src,dest);
+        }
+    }
+    /* Insert a copy from constant src to dst at end of BB */
+    private void addMoveAtBBEnd(BasicBlock block, Operand.ConstantOperand src, Register dest) {
+        var inst = new Instruction.Move(src, new Operand.RegisterOperand(dest));
+        insertAtEnd(block, inst);
+    }
+    /* Insert a copy dest to a new temp at phi node defining dest, return temp */
+    private Register addMoveToTempAfterPhi(BasicBlock block, Register dest) {
+        var temp = function.registerPool.newTempReg(dest.name(), dest.type);
+        var inst = new Instruction.Move(new Operand.RegisterOperand(dest), new Operand.RegisterOperand(temp));
+        insertAfterPhi(block, dest, inst);
+        return temp;
+    }
+
+    private void initStack() {
+        stacks = new NameStack[function.registerPool.numRegisters()];
+        for (int i = 0; i < stacks.length; i++)
+            stacks[i] = new NameStack();
+    }
+
+    static class NameStack {
+        List<Register> stack = new ArrayList<>();
+        void push(Register r) { stack.add(r); }
+        Register top() { return stack.getLast(); }
+        void pop() { stack.removeLast(); }
+        boolean isEmpty() { return stack.isEmpty(); }
+    }
+}
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Options.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Options.java
index b53c8cd..2af20d9 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Options.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Options.java
@@ -5,6 +5,8 @@
 public enum Options {
     ISSA, // Incremental SSA
     OPTIMIZE,
+    SSA_DESTRUCTION_BRIGGS,
+    SSA_DESTRUCTION_BOISSINOT_NOCOALESCE,
     SCCP,
     CCP, // constant comparison propagation
     REGALLOC,

From d71d2a76451b885aba4b8323a2eb49af2d37b915 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Tue, 12 Aug 2025 10:09:09 +0100
Subject: [PATCH 110/119] #57 The eq() function illustrates a scenario that
 causes rename() call to interference graph for non-existent interference.
 This occurs with the Boissinot simple SSA destruction but not with the Briggs
 version.

---
 .../ezlang/compiler/TestSSATransform.java     | 117 ++++++++++++++++++
 1 file changed, 117 insertions(+)

diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
index 5e926e3..e652b89 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java
@@ -3683,4 +3683,121 @@ func main()->Int
 L1:
 """, result);
     }
+
+    @Test
+    public void testSSA21()
+    {
+        String src = """
+func eq(a: [Int], b: [Int], n: Int)->Int
+{
+    var result = 1
+    var i = 0
+    while (i < n)
+    {
+        if (a[i] != b[i])
+        {
+            result = 0
+            break
+        }
+        i = i + 1
+    } 
+    return result
+}
+                """;
+        String result = compileSrc(src);
+        Assert.assertEquals("""
+func eq
+Before SSA
+==========
+L0:
+    arg a
+    arg b
+    arg n
+    result = 1
+    i = 0
+    goto  L2
+L2:
+    %t5 = i<n
+    if %t5 goto L3 else goto L4
+L3:
+    %t6 = a[i]
+    %t7 = b[i]
+    %t8 = %t6!=%t7
+    if %t8 goto L5 else goto L6
+L5:
+    result = 0
+    goto  L4
+L4:
+    ret result
+    goto  L1
+L1:
+L6:
+    %t9 = i+1
+    i = %t9
+    goto  L2
+After SSA
+=========
+L0:
+    arg a_0
+    arg b_0
+    arg n_0
+    result_0 = 1
+    i_0 = 0
+    goto  L2
+L2:
+    i_1 = phi(i_0, i_2)
+    %t5_0 = i_1<n_0
+    if %t5_0 goto L3 else goto L4
+L3:
+    %t6_0 = a_0[i_1]
+    %t7_0 = b_0[i_1]
+    %t8_0 = %t6_0!=%t7_0
+    if %t8_0 goto L5 else goto L6
+L5:
+    result_1 = 0
+    goto  L4
+L4:
+    result_2 = phi(result_0, result_1)
+    ret result_2
+    goto  L1
+L1:
+L6:
+    %t9_0 = i_1+1
+    i_2 = %t9_0
+    goto  L2
+After exiting SSA
+=================
+L0:
+    arg a_0
+    arg b_0
+    arg n_0
+    result_0 = 1
+    i_0 = 0
+    i_1 = i_0
+    goto  L2
+L2:
+    %t5_0 = i_1<n_0
+    result_2 = result_0
+    if %t5_0 goto L3 else goto L4
+L3:
+    %t6_0 = a_0[i_1]
+    %t7_0 = b_0[i_1]
+    %t8_0 = %t6_0!=%t7_0
+    if %t8_0 goto L5 else goto L6
+L5:
+    result_1 = 0
+    result_2 = result_1
+    goto  L4
+L4:
+    ret result_2
+    goto  L1
+L1:
+L6:
+    %t9_0 = i_1+1
+    i_2 = %t9_0
+    i_1 = i_2
+    goto  L2
+""", result);
+    }
+
 }
\ No newline at end of file

From e922fb9e895806540281ca35aeaa2eee40acd7a7 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Tue, 12 Aug 2025 10:09:59 +0100
Subject: [PATCH 111/119] #57 If the interference does not exist then return
 early. The eq() function illustrates a scenario that causes rename() call to
 interference graph for non-existent interference. This occurs with the
 Boissinot simple SSA destruction but not with the Briggs version.

---
 .../ezlang/compiler/InterferenceGraph.java                  | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/InterferenceGraph.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/InterferenceGraph.java
index 1749d49..52c7fe3 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/InterferenceGraph.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/InterferenceGraph.java
@@ -62,6 +62,12 @@ public boolean interfere(Integer from, Integer to) {
     public void rename(Integer source, Integer target) {
         // Move all interferences
         var fromSet = edges.remove(source);
+        if (fromSet == null) {
+            // FIXME figure out why
+            // Test case testSSA21 / when run using Boissinot SSA Destruction without coalescing
+            // This is eq() function in mergesort test case
+            return;
+        }
         var toSet = edges.get(target);
         if (toSet == null) {
             //throw new RuntimeException("Cannot find edge " + target + " from " + source);

From 2a8d0e5a738916c541420c8cc5f1f1d75cc45a91 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Tue, 12 Aug 2025 10:23:32 +0100
Subject: [PATCH 112/119] #57 add options to dump the IR after intermediate
 steps

---
 .../ezlang/compiler/ExitSSABoissinotNoCoalesce.java           | 4 +++-
 .../java/com/compilerprogramming/ezlang/compiler/Options.java | 2 ++
 2 files changed, 5 insertions(+), 1 deletion(-)

diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSABoissinotNoCoalesce.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSABoissinotNoCoalesce.java
index 633cf7e..8b1eefe 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSABoissinotNoCoalesce.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSABoissinotNoCoalesce.java
@@ -41,10 +41,12 @@ public ExitSSABoissinotNoCoalesce(CompiledFunction function, EnumSet<Options> op
         allBlocks = function.getBlocks();
         init();
         makeConventionalSSA();
+        if (options.contains(Options.DUMP_SSA_TO_CSSA)) function.dumpIR(false, "After converting from SSA to CSSA");
         removePhis();
+        if (options.contains(Options.DUMP_CSSA_PHI_REMOVAL)) function.dumpIR(false, "After removing phis from CSSA");
         sequenceParallelCopies();
         function.isSSA = false;
-        if (options.contains(Options.DUMP_POST_SSA_IR)) function.dumpIR(false, "After exiting SSA");
+        if (options.contains(Options.DUMP_POST_SSA_IR)) function.dumpIR(false, "After exiting SSA (Boissinot method)");
     }
 
     private void init() {
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Options.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Options.java
index 2af20d9..368a9c1 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Options.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Options.java
@@ -20,6 +20,8 @@ public enum Options {
     DUMP_CCP_POSTAPPLY,
     DUMP_SSA_LIVENESS,
     DUMP_SSA_DOMTREE,
+    DUMP_SSA_TO_CSSA,
+    DUMP_CSSA_PHI_REMOVAL,
     DUMP_POST_SSA_IR,
     DUMP_INTERFERENCE_GRAPH,
     DUMP_CHAITIN_COALESCE,

From 92cd996f48473c31a12bf5ba843ef475691607de Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Tue, 12 Aug 2025 10:24:06 +0100
Subject: [PATCH 113/119] #57 add the Boissinot SSA destruction approach to
 interpreter tests

---
 .../java/com/compilerprogramming/ezlang/compiler/Options.java   | 2 ++
 .../compilerprogramming/ezlang/interpreter/TestInterpreter.java | 2 ++
 2 files changed, 4 insertions(+)

diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Options.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Options.java
index 368a9c1..6162b6b 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Options.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Options.java
@@ -29,7 +29,9 @@ public enum Options {
 
     public static final EnumSet<Options> NONE = EnumSet.noneOf(Options.class);
     public static final EnumSet<Options> OPT = EnumSet.of(Options.OPTIMIZE,Options.SCCP,Options.CCP,Options.REGALLOC);
+    public static final EnumSet<Options> OPT_B = EnumSet.of(Options.OPTIMIZE,Options.SCCP,Options.CCP,Options.REGALLOC,Options.SSA_DESTRUCTION_BOISSINOT_NOCOALESCE);
     public static final EnumSet<Options> OPT_ISSA = EnumSet.of(Options.OPTIMIZE,Options.ISSA,Options.SCCP,Options.CCP,Options.REGALLOC);
+    public static final EnumSet<Options> OPT_ISSA_B = EnumSet.of(Options.OPTIMIZE,Options.ISSA,Options.SCCP,Options.CCP,Options.REGALLOC,Options.SSA_DESTRUCTION_BOISSINOT_NOCOALESCE);
     public static final EnumSet<Options> VERBOSE = EnumSet.range(DUMP_INITIAL_IR, DUMP_POST_CHAITIN_IR);
     public static final EnumSet<Options> OPT_VERBOSE = EnumSet.range(OPTIMIZE, DUMP_POST_CHAITIN_IR);
 }
diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java b/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java
index 9e54bfa..136ef72 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java
@@ -36,7 +36,9 @@ public static Collection<Object[]> data() {
         List<Object[]> parameters = new ArrayList<>();
         parameters.add(new Object[] { Options.NONE });
         parameters.add(new Object[] { Options.OPT });
+        parameters.add(new Object[] { Options.OPT_B });
         parameters.add(new Object[] { Options.OPT_ISSA });
+        parameters.add(new Object[] { Options.OPT_ISSA_B });
         return parameters;
     }
 

From a8022e13870494fe7cd6b293cf7f7861c9cf4499 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Tue, 12 Aug 2025 11:02:18 +0100
Subject: [PATCH 114/119] #57 add ability to dump to a string buffer

---
 .../ezlang/compiler/CompiledFunction.java        | 16 +++++++++++++---
 1 file changed, 13 insertions(+), 3 deletions(-)

diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
index d2c1d40..b8f6cfc 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java
@@ -27,6 +27,8 @@ public class CompiledFunction {
     public boolean hasLiveness;
     private final IncrementalSSA issa;
 
+    private StringBuilder dumpTarget;
+
     /**
      * We essentially do a form of abstract interpretation as we generate
      * the bytecode instructions. For this purpose we use a virtual operand stack.
@@ -72,7 +74,9 @@ public CompiledFunction(EZType.EZTypeFunction functionType, TypeDictionary typeD
         issa.finish(null);
         this.frameSlots = registerPool.numRegisters();
     }
-
+    public void setDumpTarget(StringBuilder dumpTarget) {
+        this.dumpTarget = dumpTarget;
+    }
     private void generateArgInstructions(Scope scope) {
         if (scope.isFunctionParameterScope) {
             for (Symbol symbol: scope.getLocalSymbols()) {
@@ -814,8 +818,14 @@ public StringBuilder toStr(StringBuilder sb, boolean verbose) {
     }
 
     public void dumpIR(boolean verbose, String title) {
-        System.out.println(title);
-        System.out.println(toStr(new StringBuilder(), verbose));
+        if (dumpTarget != null) {
+            dumpTarget.append(title).append("\n");
+            toStr(dumpTarget,verbose);
+        }
+        else {
+            System.out.println(title);
+            System.out.println(toStr(new StringBuilder(), verbose));
+        }
     }
 
     public void livenessAnalysis() {

From 980fc8f698fa7cdbd780cc73ed41eb0c12ce2e97 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Tue, 12 Aug 2025 11:03:05 +0100
Subject: [PATCH 115/119] #57 add a version of the ssa transform test suite
 that runs the Boissinot SSA destruction without coalescing

---
 .../ezlang/compiler/TestSSADestructB.java     | 6270 +++++++++++++++++
 1 file changed, 6270 insertions(+)
 create mode 100644 optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSADestructB.java

diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSADestructB.java b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSADestructB.java
new file mode 100644
index 0000000..d686f79
--- /dev/null
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSADestructB.java
@@ -0,0 +1,6270 @@
+package com.compilerprogramming.ezlang.compiler;
+
+import com.compilerprogramming.ezlang.types.EZType;
+import com.compilerprogramming.ezlang.types.Symbol;
+import com.compilerprogramming.ezlang.types.TypeDictionary;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.BitSet;
+import java.util.EnumSet;
+
+public class TestSSADestructB {
+
+    static EnumSet<Options> ssaExitOptions = EnumSet.of(Options.SSA_DESTRUCTION_BOISSINOT_NOCOALESCE,Options.DUMP_SSA_TO_CSSA,Options.DUMP_CSSA_PHI_REMOVAL);
+
+    String compileSrc(String src) {
+        var compiler = new Compiler();
+        var typeDict = compiler.compileSrc(src);
+        StringBuilder sb = new StringBuilder();
+        for (Symbol s : typeDict.bindings.values()) {
+            if (s instanceof Symbol.FunctionTypeSymbol f) {
+                var functionBuilder = (CompiledFunction) f.code();
+                functionBuilder.setDumpTarget(sb);
+                sb.append("func ").append(f.name).append("\n");
+                sb.append("Before SSA\n");
+                sb.append("==========\n");
+                BasicBlock.toStr(sb, functionBuilder.entry, new BitSet(), false);
+                //functionBuilder.toDot(sb,false);
+                new EnterSSA(functionBuilder, Options.NONE);
+                //new EnterSSA(functionBuilder, EnumSet.of(DUMP_PRE_SSA_DOMFRONTIERS));
+                sb.append("After SSA\n");
+                sb.append("=========\n");
+                BasicBlock.toStr(sb, functionBuilder.entry, new BitSet(), false);
+                //functionBuilder.toDot(sb,false);
+                new ExitSSA(functionBuilder, ssaExitOptions);
+                sb.append("After exiting SSA\n");
+                sb.append("=================\n");
+                BasicBlock.toStr(sb, functionBuilder.entry, new BitSet(), false);
+            }
+        }
+        return sb.toString();
+    }
+
+    @Test
+    public void test1() {
+        String src = """
+                func foo(d: Int) {
+                    var a = 42;
+                    var b = a;
+                    var c = a + b;
+                    a = c + 23;
+                    c = a + d;
+                }
+                """;
+        String result = compileSrc(src);
+        Assert.assertEquals("""
+func foo
+Before SSA
+==========
+L0:
+    arg d
+    a = 42
+    b = a
+    %t4 = a+b
+    c = %t4
+    %t5 = c+23
+    a = %t5
+    %t6 = a+d
+    c = %t6
+    goto  L1
+L1:
+After SSA
+=========
+L0:
+    arg d_0
+    a_0 = 42
+    b_0 = a_0
+    %t4_0 = a_0+b_0
+    c_0 = %t4_0
+    %t5_0 = c_0+23
+    a_1 = %t5_0
+    %t6_0 = a_1+d_0
+    c_1 = %t6_0
+    goto  L1
+L1:
+After converting from SSA to CSSA
+L0:
+    arg d_0
+    a_0 = 42
+    b_0 = a_0
+    %t4_0 = a_0+b_0
+    c_0 = %t4_0
+    %t5_0 = c_0+23
+    a_1 = %t5_0
+    %t6_0 = a_1+d_0
+    c_1 = %t6_0
+    goto  L1
+L1:
+After removing phis from CSSA
+L0:
+    arg d_0
+    a_0 = 42
+    b_0 = a_0
+    %t4_0 = a_0+b_0
+    c_0 = %t4_0
+    %t5_0 = c_0+23
+    a_1 = %t5_0
+    %t6_0 = a_1+d_0
+    c_1 = %t6_0
+    goto  L1
+L1:
+After exiting SSA
+=================
+L0:
+    arg d_0
+    a_0 = 42
+    b_0 = a_0
+    %t4_0 = a_0+b_0
+    c_0 = %t4_0
+    %t5_0 = c_0+23
+    a_1 = %t5_0
+    %t6_0 = a_1+d_0
+    c_1 = %t6_0
+    goto  L1
+L1:
+""", result);
+
+    }
+
+    @Test
+    public void test2() {
+        String src = """
+                func foo(d: Int)->Int {
+                    var a = 42
+                    if (d)
+                    {
+                      a = a + 1
+                    }
+                    else
+                    {
+                      a = a - 1
+                    }
+                    return a
+                }
+                """;
+        String result = compileSrc(src);
+        Assert.assertEquals("""
+func foo
+Before SSA
+==========
+L0:
+    arg d
+    a = 42
+    if d goto L2 else goto L3
+L2:
+    %t2 = a+1
+    a = %t2
+    goto  L4
+L4:
+    ret a
+    goto  L1
+L1:
+L3:
+    %t3 = a-1
+    a = %t3
+    goto  L4
+After SSA
+=========
+L0:
+    arg d_0
+    a_0 = 42
+    if d_0 goto L2 else goto L3
+L2:
+    %t2_0 = a_0+1
+    a_2 = %t2_0
+    goto  L4
+L4:
+    a_3 = phi(a_2, a_1)
+    ret a_3
+    goto  L1
+L1:
+L3:
+    %t3_0 = a_0-1
+    a_1 = %t3_0
+    goto  L4
+After converting from SSA to CSSA
+L0:
+    arg d_0
+    a_0 = 42
+    if d_0 goto L2 else goto L3
+L2:
+    %t2_0 = a_0+1
+    a_2 = %t2_0
+    (%t11) = (a_2)
+    goto  L4
+L4:
+    %t13 = phi(%t11, %t12)
+    (a_3) = (%t13)
+    ret a_3
+    goto  L1
+L1:
+L3:
+    %t3_0 = a_0-1
+    a_1 = %t3_0
+    (%t12) = (a_1)
+    goto  L4
+After removing phis from CSSA
+L0:
+    arg d_0
+    a_0 = 42
+    if d_0 goto L2 else goto L3
+L2:
+    %t2_0 = a_0+1
+    a_2 = %t2_0
+    (%t11) = (a_2)
+    %t13 = %t11
+    goto  L4
+L4:
+    (a_3) = (%t13)
+    ret a_3
+    goto  L1
+L1:
+L3:
+    %t3_0 = a_0-1
+    a_1 = %t3_0
+    (%t12) = (a_1)
+    %t13 = %t12
+    goto  L4
+After exiting SSA
+=================
+L0:
+    arg d_0
+    a_0 = 42
+    if d_0 goto L2 else goto L3
+L2:
+    %t2_0 = a_0+1
+    a_2 = %t2_0
+    %t11 = a_2
+    %t13 = %t11
+    goto  L4
+L4:
+    a_3 = %t13
+    ret a_3
+    goto  L1
+L1:
+L3:
+    %t3_0 = a_0-1
+    a_1 = %t3_0
+    %t12 = a_1
+    %t13 = %t12
+    goto  L4
+""", result);
+
+    }
+
+    @Test
+    public void test3() {
+        String src = """
+                func factorial(num: Int)->Int {
+                    var result = 1
+                    while (num > 1)
+                    {
+                      result = result * num
+                      num = num - 1
+                    }
+                    return result
+                }
+                """;
+        String result = compileSrc(src);
+        Assert.assertEquals("""
+func factorial
+Before SSA
+==========
+L0:
+    arg num
+    result = 1
+    goto  L2
+L2:
+    %t2 = num>1
+    if %t2 goto L3 else goto L4
+L3:
+    %t3 = result*num
+    result = %t3
+    %t4 = num-1
+    num = %t4
+    goto  L2
+L4:
+    ret result
+    goto  L1
+L1:
+After SSA
+=========
+L0:
+    arg num_0
+    result_0 = 1
+    goto  L2
+L2:
+    result_1 = phi(result_0, result_2)
+    num_1 = phi(num_0, num_2)
+    %t2_0 = num_1>1
+    if %t2_0 goto L3 else goto L4
+L3:
+    %t3_0 = result_1*num_1
+    result_2 = %t3_0
+    %t4_0 = num_1-1
+    num_2 = %t4_0
+    goto  L2
+L4:
+    ret result_1
+    goto  L1
+L1:
+After converting from SSA to CSSA
+L0:
+    arg num_0
+    result_0 = 1
+    (%t14,%t17) = (result_0,num_0)
+    goto  L2
+L2:
+    %t16 = phi(%t14, %t15)
+    %t19 = phi(%t17, %t18)
+    (result_1,num_1) = (%t16,%t19)
+    %t2_0 = num_1>1
+    if %t2_0 goto L3 else goto L4
+L3:
+    %t3_0 = result_1*num_1
+    result_2 = %t3_0
+    %t4_0 = num_1-1
+    num_2 = %t4_0
+    (%t15,%t18) = (result_2,num_2)
+    goto  L2
+L4:
+    ret result_1
+    goto  L1
+L1:
+After removing phis from CSSA
+L0:
+    arg num_0
+    result_0 = 1
+    (%t14,%t17) = (result_0,num_0)
+    %t16 = %t14
+    %t19 = %t17
+    goto  L2
+L2:
+    (result_1,num_1) = (%t16,%t19)
+    %t2_0 = num_1>1
+    if %t2_0 goto L3 else goto L4
+L3:
+    %t3_0 = result_1*num_1
+    result_2 = %t3_0
+    %t4_0 = num_1-1
+    num_2 = %t4_0
+    (%t15,%t18) = (result_2,num_2)
+    %t16 = %t15
+    %t19 = %t18
+    goto  L2
+L4:
+    ret result_1
+    goto  L1
+L1:
+After exiting SSA
+=================
+L0:
+    arg num_0
+    result_0 = 1
+    %t14 = result_0
+    %t17 = num_0
+    %t16 = %t14
+    %t19 = %t17
+    goto  L2
+L2:
+    result_1 = %t16
+    num_1 = %t19
+    %t2_0 = num_1>1
+    if %t2_0 goto L3 else goto L4
+L3:
+    %t3_0 = result_1*num_1
+    result_2 = %t3_0
+    %t4_0 = num_1-1
+    num_2 = %t4_0
+    %t15 = result_2
+    %t18 = num_2
+    %t16 = %t15
+    %t19 = %t18
+    goto  L2
+L4:
+    ret result_1
+    goto  L1
+L1:
+""", result);
+
+    }
+
+    @Test
+    public void test4() {
+        String src = """
+                func print(a: Int, b: Int, c:Int, d:Int) {}
+                func example14_66(p: Int, q: Int, r: Int, s: Int, t: Int) {
+                    var i = 1
+                    var j = 1
+                    var k = 1
+                    var l = 1
+                    while (1) {
+                        if (p) {
+                            j = i
+                            if (q) {
+                                l = 2
+                            }
+                            else {
+                                l = 3
+                            }
+                            k = k + 1
+                        }
+                        else {
+                            k = k + 2
+                        }
+                        print(i,j,k,l)
+                        while (1) {
+                            if (r) {
+                                l = l + 4
+                            }
+                            if (!s)
+                                break
+                        }
+                        i = i + 6
+                        if (!t)
+                            break
+                    }
+                }
+                """;
+        String result = compileSrc(src);
+        Assert.assertEquals("""
+func print
+Before SSA
+==========
+L0:
+    arg a
+    arg b
+    arg c
+    arg d
+    goto  L1
+L1:
+After SSA
+=========
+L0:
+    arg a_0
+    arg b_0
+    arg c_0
+    arg d_0
+    goto  L1
+L1:
+After converting from SSA to CSSA
+L0:
+    arg a_0
+    arg b_0
+    arg c_0
+    arg d_0
+    goto  L1
+L1:
+After removing phis from CSSA
+L0:
+    arg a_0
+    arg b_0
+    arg c_0
+    arg d_0
+    goto  L1
+L1:
+After exiting SSA
+=================
+L0:
+    arg a_0
+    arg b_0
+    arg c_0
+    arg d_0
+    goto  L1
+L1:
+func example14_66
+Before SSA
+==========
+L0:
+    arg p
+    arg q
+    arg r
+    arg s
+    arg t
+    i = 1
+    j = 1
+    k = 1
+    l = 1
+    goto  L2
+L2:
+    if 1 goto L3 else goto L4
+L3:
+    if p goto L5 else goto L6
+L5:
+    j = i
+    if q goto L8 else goto L9
+L8:
+    l = 2
+    goto  L10
+L10:
+    %t9 = k+1
+    k = %t9
+    goto  L7
+L7:
+    %t11 = i
+    %t12 = j
+    %t13 = k
+    %t14 = l
+    call print params %t11, %t12, %t13, %t14
+    goto  L11
+L11:
+    if 1 goto L12 else goto L13
+L12:
+    if r goto L14 else goto L15
+L14:
+    %t15 = l+4
+    l = %t15
+    goto  L15
+L15:
+    %t16 = !s
+    if %t16 goto L16 else goto L17
+L16:
+    goto  L13
+L13:
+    %t17 = i+6
+    i = %t17
+    %t18 = !t
+    if %t18 goto L18 else goto L19
+L18:
+    goto  L4
+L4:
+    goto  L1
+L1:
+L19:
+    goto  L2
+L17:
+    goto  L11
+L9:
+    l = 3
+    goto  L10
+L6:
+    %t10 = k+2
+    k = %t10
+    goto  L7
+After SSA
+=========
+L0:
+    arg p_0
+    arg q_0
+    arg r_0
+    arg s_0
+    arg t_0
+    i_0 = 1
+    j_0 = 1
+    k_0 = 1
+    l_0 = 1
+    goto  L2
+L2:
+    l_1 = phi(l_0, l_9)
+    k_1 = phi(k_0, k_4)
+    j_1 = phi(j_0, j_3)
+    i_1 = phi(i_0, i_2)
+    if 1 goto L3 else goto L4
+L3:
+    if p_0 goto L5 else goto L6
+L5:
+    j_2 = i_1
+    if q_0 goto L8 else goto L9
+L8:
+    l_3 = 2
+    goto  L10
+L10:
+    l_4 = phi(l_3, l_2)
+    %t9_0 = k_1+1
+    k_3 = %t9_0
+    goto  L7
+L7:
+    l_5 = phi(l_4, l_1)
+    k_4 = phi(k_3, k_2)
+    j_3 = phi(j_2, j_1)
+    %t11_0 = i_1
+    %t12_0 = j_3
+    %t13_0 = k_4
+    %t14_0 = l_5
+    call print params %t11_0, %t12_0, %t13_0, %t14_0
+    goto  L11
+L11:
+    l_6 = phi(l_5, l_8)
+    if 1 goto L12 else goto L13
+L12:
+    if r_0 goto L14 else goto L15
+L14:
+    %t15_0 = l_6+4
+    l_7 = %t15_0
+    goto  L15
+L15:
+    l_8 = phi(l_6, l_7)
+    %t16_0 = !s_0
+    if %t16_0 goto L16 else goto L17
+L16:
+    goto  L13
+L13:
+    l_9 = phi(l_6, l_8)
+    %t17_0 = i_1+6
+    i_2 = %t17_0
+    %t18_0 = !t_0
+    if %t18_0 goto L18 else goto L19
+L18:
+    goto  L4
+L4:
+    goto  L1
+L1:
+L19:
+    goto  L2
+L17:
+    goto  L11
+L9:
+    l_2 = 3
+    goto  L10
+L6:
+    %t10_0 = k_1+2
+    k_2 = %t10_0
+    goto  L7
+After converting from SSA to CSSA
+L0:
+    arg p_0
+    arg q_0
+    arg r_0
+    arg s_0
+    arg t_0
+    i_0 = 1
+    j_0 = 1
+    k_0 = 1
+    l_0 = 1
+    (%t77,%t80,%t83,%t86) = (l_0,k_0,j_0,i_0)
+    goto  L2
+L2:
+    %t79 = phi(%t77, %t78)
+    %t82 = phi(%t80, %t81)
+    %t85 = phi(%t83, %t84)
+    %t88 = phi(%t86, %t87)
+    (l_1,k_1,j_1,i_1) = (%t79,%t82,%t85,%t88)
+    if 1 goto L3 else goto L4
+L3:
+    if p_0 goto L5 else goto L6
+L5:
+    j_2 = i_1
+    if q_0 goto L8 else goto L9
+L8:
+    l_3 = 2
+    (%t74) = (l_3)
+    goto  L10
+L10:
+    %t76 = phi(%t74, %t75)
+    (l_4) = (%t76)
+    %t9_0 = k_1+1
+    k_3 = %t9_0
+    (%t65,%t68,%t71) = (l_4,k_3,j_2)
+    goto  L7
+L7:
+    %t67 = phi(%t65, %t66)
+    %t70 = phi(%t68, %t69)
+    %t73 = phi(%t71, %t72)
+    (l_5,k_4,j_3) = (%t67,%t70,%t73)
+    %t11_0 = i_1
+    %t12_0 = j_3
+    %t13_0 = k_4
+    %t14_0 = l_5
+    call print params %t11_0, %t12_0, %t13_0, %t14_0
+    (%t62) = (l_5)
+    goto  L11
+L11:
+    %t64 = phi(%t62, %t63)
+    (l_6) = (%t64)
+    (%t56) = (l_6)
+    if 1 goto L12 else goto L13
+L12:
+    (%t59) = (l_6)
+    if r_0 goto L14 else goto L15
+L14:
+    %t15_0 = l_6+4
+    l_7 = %t15_0
+    (%t60) = (l_7)
+    goto  L15
+L15:
+    %t61 = phi(%t59, %t60)
+    (l_8) = (%t61)
+    %t16_0 = !s_0
+    if %t16_0 goto L16 else goto L17
+L16:
+    (%t57) = (l_8)
+    goto  L13
+L13:
+    %t58 = phi(%t56, %t57)
+    (l_9) = (%t58)
+    %t17_0 = i_1+6
+    i_2 = %t17_0
+    %t18_0 = !t_0
+    if %t18_0 goto L18 else goto L19
+L18:
+    goto  L4
+L4:
+    goto  L1
+L1:
+L19:
+    (%t78,%t81,%t84,%t87) = (l_9,k_4,j_3,i_2)
+    goto  L2
+L17:
+    (%t63) = (l_8)
+    goto  L11
+L9:
+    l_2 = 3
+    (%t75) = (l_2)
+    goto  L10
+L6:
+    %t10_0 = k_1+2
+    k_2 = %t10_0
+    (%t66,%t69,%t72) = (l_1,k_2,j_1)
+    goto  L7
+After removing phis from CSSA
+L0:
+    arg p_0
+    arg q_0
+    arg r_0
+    arg s_0
+    arg t_0
+    i_0 = 1
+    j_0 = 1
+    k_0 = 1
+    l_0 = 1
+    (%t77,%t80,%t83,%t86) = (l_0,k_0,j_0,i_0)
+    %t79 = %t77
+    %t82 = %t80
+    %t85 = %t83
+    %t88 = %t86
+    goto  L2
+L2:
+    (l_1,k_1,j_1,i_1) = (%t79,%t82,%t85,%t88)
+    if 1 goto L3 else goto L4
+L3:
+    if p_0 goto L5 else goto L6
+L5:
+    j_2 = i_1
+    if q_0 goto L8 else goto L9
+L8:
+    l_3 = 2
+    (%t74) = (l_3)
+    %t76 = %t74
+    goto  L10
+L10:
+    (l_4) = (%t76)
+    %t9_0 = k_1+1
+    k_3 = %t9_0
+    (%t65,%t68,%t71) = (l_4,k_3,j_2)
+    %t67 = %t65
+    %t70 = %t68
+    %t73 = %t71
+    goto  L7
+L7:
+    (l_5,k_4,j_3) = (%t67,%t70,%t73)
+    %t11_0 = i_1
+    %t12_0 = j_3
+    %t13_0 = k_4
+    %t14_0 = l_5
+    call print params %t11_0, %t12_0, %t13_0, %t14_0
+    (%t62) = (l_5)
+    %t64 = %t62
+    goto  L11
+L11:
+    (l_6) = (%t64)
+    (%t56) = (l_6)
+    %t58 = %t56
+    if 1 goto L12 else goto L13
+L12:
+    (%t59) = (l_6)
+    %t61 = %t59
+    if r_0 goto L14 else goto L15
+L14:
+    %t15_0 = l_6+4
+    l_7 = %t15_0
+    (%t60) = (l_7)
+    %t61 = %t60
+    goto  L15
+L15:
+    (l_8) = (%t61)
+    %t16_0 = !s_0
+    if %t16_0 goto L16 else goto L17
+L16:
+    (%t57) = (l_8)
+    %t58 = %t57
+    goto  L13
+L13:
+    (l_9) = (%t58)
+    %t17_0 = i_1+6
+    i_2 = %t17_0
+    %t18_0 = !t_0
+    if %t18_0 goto L18 else goto L19
+L18:
+    goto  L4
+L4:
+    goto  L1
+L1:
+L19:
+    (%t78,%t81,%t84,%t87) = (l_9,k_4,j_3,i_2)
+    %t79 = %t78
+    %t82 = %t81
+    %t85 = %t84
+    %t88 = %t87
+    goto  L2
+L17:
+    (%t63) = (l_8)
+    %t64 = %t63
+    goto  L11
+L9:
+    l_2 = 3
+    (%t75) = (l_2)
+    %t76 = %t75
+    goto  L10
+L6:
+    %t10_0 = k_1+2
+    k_2 = %t10_0
+    (%t66,%t69,%t72) = (l_1,k_2,j_1)
+    %t67 = %t66
+    %t70 = %t69
+    %t73 = %t72
+    goto  L7
+After exiting SSA
+=================
+L0:
+    arg p_0
+    arg q_0
+    arg r_0
+    arg s_0
+    arg t_0
+    i_0 = 1
+    j_0 = 1
+    k_0 = 1
+    l_0 = 1
+    %t77 = l_0
+    %t80 = k_0
+    %t83 = j_0
+    %t86 = i_0
+    %t79 = %t77
+    %t82 = %t80
+    %t85 = %t83
+    %t88 = %t86
+    goto  L2
+L2:
+    l_1 = %t79
+    k_1 = %t82
+    j_1 = %t85
+    i_1 = %t88
+    if 1 goto L3 else goto L4
+L3:
+    if p_0 goto L5 else goto L6
+L5:
+    j_2 = i_1
+    if q_0 goto L8 else goto L9
+L8:
+    l_3 = 2
+    %t74 = l_3
+    %t76 = %t74
+    goto  L10
+L10:
+    l_4 = %t76
+    %t9_0 = k_1+1
+    k_3 = %t9_0
+    %t65 = l_4
+    %t68 = k_3
+    %t71 = j_2
+    %t67 = %t65
+    %t70 = %t68
+    %t73 = %t71
+    goto  L7
+L7:
+    l_5 = %t67
+    k_4 = %t70
+    j_3 = %t73
+    %t11_0 = i_1
+    %t12_0 = j_3
+    %t13_0 = k_4
+    %t14_0 = l_5
+    call print params %t11_0, %t12_0, %t13_0, %t14_0
+    %t62 = l_5
+    %t64 = %t62
+    goto  L11
+L11:
+    l_6 = %t64
+    %t56 = l_6
+    %t58 = %t56
+    if 1 goto L12 else goto L13
+L12:
+    %t59 = l_6
+    %t61 = %t59
+    if r_0 goto L14 else goto L15
+L14:
+    %t15_0 = l_6+4
+    l_7 = %t15_0
+    %t60 = l_7
+    %t61 = %t60
+    goto  L15
+L15:
+    l_8 = %t61
+    %t16_0 = !s_0
+    if %t16_0 goto L16 else goto L17
+L16:
+    %t57 = l_8
+    %t58 = %t57
+    goto  L13
+L13:
+    l_9 = %t58
+    %t17_0 = i_1+6
+    i_2 = %t17_0
+    %t18_0 = !t_0
+    if %t18_0 goto L18 else goto L19
+L18:
+    goto  L4
+L4:
+    goto  L1
+L1:
+L19:
+    %t78 = l_9
+    %t81 = k_4
+    %t84 = j_3
+    %t87 = i_2
+    %t79 = %t78
+    %t82 = %t81
+    %t85 = %t84
+    %t88 = %t87
+    goto  L2
+L17:
+    %t63 = l_8
+    %t64 = %t63
+    goto  L11
+L9:
+    l_2 = 3
+    %t75 = l_2
+    %t76 = %t75
+    goto  L10
+L6:
+    %t10_0 = k_1+2
+    k_2 = %t10_0
+    %t66 = l_1
+    %t69 = k_2
+    %t72 = j_1
+    %t67 = %t66
+    %t70 = %t69
+    %t73 = %t72
+    goto  L7
+""", result);
+    }
+
+    @Test
+    public void test5() {
+        String src = """
+                func bar(arg: Int)->Int {
+                    if (arg)
+                        return 42;
+                    return 0;
+                }
+                """;
+        String result = compileSrc(src);
+        Assert.assertEquals("""
+func bar
+Before SSA
+==========
+L0:
+    arg arg
+    if arg goto L2 else goto L3
+L2:
+    ret 42
+    goto  L1
+L1:
+L3:
+    ret 0
+    goto  L1
+After SSA
+=========
+L0:
+    arg arg_0
+    if arg_0 goto L2 else goto L3
+L2:
+    ret 42
+    goto  L1
+L1:
+L3:
+    ret 0
+    goto  L1
+After converting from SSA to CSSA
+L0:
+    arg arg_0
+    if arg_0 goto L2 else goto L3
+L2:
+    ret 42
+    goto  L1
+L1:
+L3:
+    ret 0
+    goto  L1
+After removing phis from CSSA
+L0:
+    arg arg_0
+    if arg_0 goto L2 else goto L3
+L2:
+    ret 42
+    goto  L1
+L1:
+L3:
+    ret 0
+    goto  L1
+After exiting SSA
+=================
+L0:
+    arg arg_0
+    if arg_0 goto L2 else goto L3
+L2:
+    ret 42
+    goto  L1
+L1:
+L3:
+    ret 0
+    goto  L1
+""", result);
+    }
+
+    /**
+     * This test case is based on the example snippet from Briggs paper
+     * illustrating the lost copy problem.
+     */
+    static CompiledFunction buildLostCopyTest() {
+        TypeDictionary typeDictionary = new TypeDictionary();
+        EZType.EZTypeFunction functionType = new EZType.EZTypeFunction("foo");
+        var argSymbol = new Symbol.ParameterSymbol("p", typeDictionary.INT);
+        functionType.addArg(argSymbol);
+        functionType.setReturnType(typeDictionary.INT);
+        CompiledFunction function = new CompiledFunction(functionType, typeDictionary);
+        RegisterPool regPool = function.registerPool;
+        Register p = regPool.newReg("p", typeDictionary.INT);
+        Register x1 = regPool.newReg("x1", typeDictionary.INT);
+        function.code(new Instruction.ArgInstruction(new Operand.LocalRegisterOperand(p, argSymbol)));
+        function.code(new Instruction.Move(
+                new Operand.ConstantOperand(1, typeDictionary.INT),
+                new Operand.RegisterOperand(x1)));
+        BasicBlock B2 = function.createBlock();
+        function.startBlock(B2);
+        Register x3 = regPool.newReg("x3", typeDictionary.INT);
+        Register x2 = regPool.newReg("x2", typeDictionary.INT);
+        function.code(new Instruction.Phi(x2, Arrays.asList(x1, x3)));
+        function.code(new Instruction.Binary("+",
+                new Operand.RegisterOperand(x3),
+                new Operand.RegisterOperand(x2),
+                new Operand.ConstantOperand(1, typeDictionary.INT)));
+        function.code(new Instruction.ConditionalBranch(B2,
+                new Operand.RegisterOperand(p), B2, function.exit));
+        function.currentBlock.addSuccessor(B2);
+        function.currentBlock.addSuccessor(function.exit);
+        function.startBlock(function.exit);
+        function.code(new Instruction.Ret(new Operand.RegisterOperand(x2)));
+        function.isSSA = true;
+        return function;
+    }
+
+    @Test
+    public void testLostCopyProblem() {
+        CompiledFunction function = buildLostCopyTest();
+        String expected = """
+L0:
+    arg p
+    x1 = 1
+    goto  L2
+L2:
+    x2 = phi(x1, x3)
+    x3 = x2+1
+    if p goto L2 else goto L1
+L1:
+    ret x2
+""";
+        Assert.assertEquals(expected, function.toStr(new StringBuilder(), false).toString());
+        new ExitSSA(function, ssaExitOptions);
+        expected = """
+L0:
+    arg p
+    x1 = 1
+    %t4 = x1
+    %t6 = %t4
+    goto  L2
+L2:
+    x2 = %t6
+    x3 = x2+1
+    %t5 = x3
+    %t6 = %t5
+    if p goto L2 else goto L1
+L1:
+    ret x2
+""";
+        Assert.assertEquals(expected, function.toStr(new StringBuilder(), false).toString());
+    }
+
+    /**
+     * This test case is based on the example snippet from Briggs paper
+     * illustrating the swap problem.
+     */
+    static CompiledFunction buildSwapTest() {
+        TypeDictionary typeDictionary = new TypeDictionary();
+        EZType.EZTypeFunction functionType = new EZType.EZTypeFunction("foo");
+        var argSymbol = new Symbol.ParameterSymbol("p", typeDictionary.INT);
+        functionType.addArg(argSymbol);
+        functionType.setReturnType(typeDictionary.VOID);
+        CompiledFunction function = new CompiledFunction(functionType, typeDictionary);
+        RegisterPool regPool = function.registerPool;
+        Register p = regPool.newReg("p", typeDictionary.INT);
+        Register a1 = regPool.newReg("a1", typeDictionary.INT);
+        Register a2 = regPool.newReg("a2", typeDictionary.INT);
+        Register b1 = regPool.newReg("b1", typeDictionary.INT);
+        Register b2 = regPool.newReg("b2", typeDictionary.INT);
+        function.code(new Instruction.ArgInstruction(new Operand.LocalRegisterOperand(p, argSymbol)));
+        function.code(new Instruction.Move(
+                new Operand.ConstantOperand(42, typeDictionary.INT),
+                new Operand.RegisterOperand(a1)));
+        function.code(new Instruction.Move(
+                new Operand.ConstantOperand(24, typeDictionary.INT),
+                new Operand.RegisterOperand(b1)));
+        BasicBlock B2 = function.createBlock();
+        function.startBlock(B2);
+        function.code(new Instruction.Phi(a2, Arrays.asList(a1, b2)));
+        function.code(new Instruction.Phi(b2, Arrays.asList(b1, a2)));
+        function.code(new Instruction.ConditionalBranch(B2,
+                new Operand.RegisterOperand(p), B2, function.exit));
+        function.currentBlock.addSuccessor(B2);
+        function.currentBlock.addSuccessor(function.exit);
+        function.startBlock(function.exit);
+        function.isSSA = true;
+        return function;
+    }
+
+    @Test
+    public void testSwapProblem() {
+        CompiledFunction function = buildSwapTest();
+        String expected = """
+L0:
+    arg p
+    a1 = 42
+    b1 = 24
+    goto  L2
+L2:
+    a2 = phi(a1, b2)
+    b2 = phi(b1, a2)
+    if p goto L2 else goto L1
+L1:
+""";
+        Assert.assertEquals(expected, function.toStr(new StringBuilder(), false).toString());
+        new ExitSSA(function, ssaExitOptions);
+        expected = """
+L0:
+    arg p
+    a1 = 42
+    b1 = 24
+    %t5 = a1
+    %t8 = b1
+    %t7 = %t5
+    %t10 = %t8
+    goto  L2
+L2:
+    a2 = %t7
+    b2 = %t10
+    %t6 = b2
+    %t9 = a2
+    %t7 = %t6
+    %t10 = %t9
+    if p goto L2 else goto L1
+L1:
+""";
+        Assert.assertEquals(expected, function.toStr(new StringBuilder(), false).toString());
+    }
+
+    @Test
+    public void testLiveness() {
+        String src = """
+                func bar(x: Int)->Int {
+                    var y = 0
+                    var z = 0
+                    while( x>1 ){
+                        y = x/2;
+                        if (y > 3) {
+                            x = x-y;
+                        }
+                        z = x-4;
+                        if (z > 0) {
+                            x = x/2;
+                        }
+                        z = z-1;
+                    }
+                    return x;
+                }
+
+                func foo()->Int {
+                    return bar(10);
+                }
+                """;
+        String result = compileSrc(src);
+        System.out.println(result);
+    }
+
+    @Test
+    public void testInit() {
+        // see issue #16
+        String src = """
+                func foo(x: Int) {
+                    var z: Int
+                    while (x > 0) {
+                        z = 5
+                        if (x == 1)
+                            z = z+1
+                        x = x - 1
+                    }
+                }
+                """;
+        String result = compileSrc(src);
+        Assert.assertEquals("""
+func foo
+Before SSA
+==========
+L0:
+    arg x
+    goto  L2
+L2:
+    %t2 = x>0
+    if %t2 goto L3 else goto L4
+L3:
+    z = 5
+    %t3 = x==1
+    if %t3 goto L5 else goto L6
+L5:
+    %t4 = z+1
+    z = %t4
+    goto  L6
+L6:
+    %t5 = x-1
+    x = %t5
+    goto  L2
+L4:
+    goto  L1
+L1:
+After SSA
+=========
+L0:
+    arg x_0
+    goto  L2
+L2:
+    x_1 = phi(x_0, x_2)
+    %t2_0 = x_1>0
+    if %t2_0 goto L3 else goto L4
+L3:
+    z_0 = 5
+    %t3_0 = x_1==1
+    if %t3_0 goto L5 else goto L6
+L5:
+    %t4_0 = z_0+1
+    z_1 = %t4_0
+    goto  L6
+L6:
+    %t5_0 = x_1-1
+    x_2 = %t5_0
+    goto  L2
+L4:
+    goto  L1
+L1:
+After converting from SSA to CSSA
+L0:
+    arg x_0
+    (%t15) = (x_0)
+    goto  L2
+L2:
+    %t17 = phi(%t15, %t16)
+    (x_1) = (%t17)
+    %t2_0 = x_1>0
+    if %t2_0 goto L3 else goto L4
+L3:
+    z_0 = 5
+    %t3_0 = x_1==1
+    if %t3_0 goto L5 else goto L6
+L5:
+    %t4_0 = z_0+1
+    z_1 = %t4_0
+    goto  L6
+L6:
+    %t5_0 = x_1-1
+    x_2 = %t5_0
+    (%t16) = (x_2)
+    goto  L2
+L4:
+    goto  L1
+L1:
+After removing phis from CSSA
+L0:
+    arg x_0
+    (%t15) = (x_0)
+    %t17 = %t15
+    goto  L2
+L2:
+    (x_1) = (%t17)
+    %t2_0 = x_1>0
+    if %t2_0 goto L3 else goto L4
+L3:
+    z_0 = 5
+    %t3_0 = x_1==1
+    if %t3_0 goto L5 else goto L6
+L5:
+    %t4_0 = z_0+1
+    z_1 = %t4_0
+    goto  L6
+L6:
+    %t5_0 = x_1-1
+    x_2 = %t5_0
+    (%t16) = (x_2)
+    %t17 = %t16
+    goto  L2
+L4:
+    goto  L1
+L1:
+After exiting SSA
+=================
+L0:
+    arg x_0
+    %t15 = x_0
+    %t17 = %t15
+    goto  L2
+L2:
+    x_1 = %t17
+    %t2_0 = x_1>0
+    if %t2_0 goto L3 else goto L4
+L3:
+    z_0 = 5
+    %t3_0 = x_1==1
+    if %t3_0 goto L5 else goto L6
+L5:
+    %t4_0 = z_0+1
+    z_1 = %t4_0
+    goto  L6
+L6:
+    %t5_0 = x_1-1
+    x_2 = %t5_0
+    %t16 = x_2
+    %t17 = %t16
+    goto  L2
+L4:
+    goto  L1
+L1:
+""", result);
+    }
+
+    // http://users.csc.calpoly.edu/~akeen/courses/csc431/handouts/references/ssa_example.pdf
+    @Test
+    public void testSSAExample() {
+        String src = """
+func foo(x: Int, y: Int)->Int {
+   var sum: Int
+   
+   if (x >= y)
+      return 0
+      
+   sum = 0;
+   while (x < y) {
+      if (x / 2 * 2 == x) {
+         sum = sum + 1
+      }
+      x = x + 1
+   }
+   return sum
+}
+                """;
+        String result = compileSrc(src);
+        Assert.assertEquals("""
+func foo
+Before SSA
+==========
+L0:
+    arg x
+    arg y
+    %t3 = x>=y
+    if %t3 goto L2 else goto L3
+L2:
+    ret 0
+    goto  L1
+L1:
+L3:
+    sum = 0
+    goto  L4
+L4:
+    %t4 = x<y
+    if %t4 goto L5 else goto L6
+L5:
+    %t5 = x/2
+    %t6 = %t5*2
+    %t7 = %t6==x
+    if %t7 goto L7 else goto L8
+L7:
+    %t8 = sum+1
+    sum = %t8
+    goto  L8
+L8:
+    %t9 = x+1
+    x = %t9
+    goto  L4
+L6:
+    ret sum
+    goto  L1
+After SSA
+=========
+L0:
+    arg x_0
+    arg y_0
+    %t3_0 = x_0>=y_0
+    if %t3_0 goto L2 else goto L3
+L2:
+    ret 0
+    goto  L1
+L1:
+L3:
+    sum_0 = 0
+    goto  L4
+L4:
+    sum_1 = phi(sum_0, sum_3)
+    x_1 = phi(x_0, x_2)
+    %t4_0 = x_1<y_0
+    if %t4_0 goto L5 else goto L6
+L5:
+    %t5_0 = x_1/2
+    %t6_0 = %t5_0*2
+    %t7_0 = %t6_0==x_1
+    if %t7_0 goto L7 else goto L8
+L7:
+    %t8_0 = sum_1+1
+    sum_2 = %t8_0
+    goto  L8
+L8:
+    sum_3 = phi(sum_1, sum_2)
+    %t9_0 = x_1+1
+    x_2 = %t9_0
+    goto  L4
+L6:
+    ret sum_1
+    goto  L1
+After converting from SSA to CSSA
+L0:
+    arg x_0
+    arg y_0
+    %t3_0 = x_0>=y_0
+    if %t3_0 goto L2 else goto L3
+L2:
+    ret 0
+    goto  L1
+L1:
+L3:
+    sum_0 = 0
+    (%t28,%t31) = (sum_0,x_0)
+    goto  L4
+L4:
+    %t30 = phi(%t28, %t29)
+    %t33 = phi(%t31, %t32)
+    (sum_1,x_1) = (%t30,%t33)
+    %t4_0 = x_1<y_0
+    if %t4_0 goto L5 else goto L6
+L5:
+    %t5_0 = x_1/2
+    %t6_0 = %t5_0*2
+    %t7_0 = %t6_0==x_1
+    (%t25) = (sum_1)
+    if %t7_0 goto L7 else goto L8
+L7:
+    %t8_0 = sum_1+1
+    sum_2 = %t8_0
+    (%t26) = (sum_2)
+    goto  L8
+L8:
+    %t27 = phi(%t25, %t26)
+    (sum_3) = (%t27)
+    %t9_0 = x_1+1
+    x_2 = %t9_0
+    (%t29,%t32) = (sum_3,x_2)
+    goto  L4
+L6:
+    ret sum_1
+    goto  L1
+After removing phis from CSSA
+L0:
+    arg x_0
+    arg y_0
+    %t3_0 = x_0>=y_0
+    if %t3_0 goto L2 else goto L3
+L2:
+    ret 0
+    goto  L1
+L1:
+L3:
+    sum_0 = 0
+    (%t28,%t31) = (sum_0,x_0)
+    %t30 = %t28
+    %t33 = %t31
+    goto  L4
+L4:
+    (sum_1,x_1) = (%t30,%t33)
+    %t4_0 = x_1<y_0
+    if %t4_0 goto L5 else goto L6
+L5:
+    %t5_0 = x_1/2
+    %t6_0 = %t5_0*2
+    %t7_0 = %t6_0==x_1
+    (%t25) = (sum_1)
+    %t27 = %t25
+    if %t7_0 goto L7 else goto L8
+L7:
+    %t8_0 = sum_1+1
+    sum_2 = %t8_0
+    (%t26) = (sum_2)
+    %t27 = %t26
+    goto  L8
+L8:
+    (sum_3) = (%t27)
+    %t9_0 = x_1+1
+    x_2 = %t9_0
+    (%t29,%t32) = (sum_3,x_2)
+    %t30 = %t29
+    %t33 = %t32
+    goto  L4
+L6:
+    ret sum_1
+    goto  L1
+After exiting SSA
+=================
+L0:
+    arg x_0
+    arg y_0
+    %t3_0 = x_0>=y_0
+    if %t3_0 goto L2 else goto L3
+L2:
+    ret 0
+    goto  L1
+L1:
+L3:
+    sum_0 = 0
+    %t28 = sum_0
+    %t31 = x_0
+    %t30 = %t28
+    %t33 = %t31
+    goto  L4
+L4:
+    sum_1 = %t30
+    x_1 = %t33
+    %t4_0 = x_1<y_0
+    if %t4_0 goto L5 else goto L6
+L5:
+    %t5_0 = x_1/2
+    %t6_0 = %t5_0*2
+    %t7_0 = %t6_0==x_1
+    %t25 = sum_1
+    %t27 = %t25
+    if %t7_0 goto L7 else goto L8
+L7:
+    %t8_0 = sum_1+1
+    sum_2 = %t8_0
+    %t26 = sum_2
+    %t27 = %t26
+    goto  L8
+L8:
+    sum_3 = %t27
+    %t9_0 = x_1+1
+    x_2 = %t9_0
+    %t29 = sum_3
+    %t32 = x_2
+    %t30 = %t29
+    %t33 = %t32
+    goto  L4
+L6:
+    ret sum_1
+    goto  L1
+""", result);
+    }
+
+    @Test
+    public void testContinue() {
+        String src = """
+func foo(x: Int)->Int {
+   var sum = 0
+   var i = 0
+   while (i < x) {
+      if (i % 2 == 0)
+        continue
+      if (i / 3 == 1)
+        continue
+      sum = sum + 1
+      i = i + 1
+   }
+   return sum
+}
+                """;
+        String result = compileSrc(src);
+        Assert.assertEquals("""
+func foo
+Before SSA
+==========
+L0:
+    arg x
+    sum = 0
+    i = 0
+    goto  L2
+L2:
+    %t3 = i<x
+    if %t3 goto L3 else goto L4
+L3:
+    %t4 = i%2
+    %t5 = %t4==0
+    if %t5 goto L5 else goto L6
+L5:
+    goto  L2
+L6:
+    %t6 = i/3
+    %t7 = %t6==1
+    if %t7 goto L7 else goto L8
+L7:
+    goto  L2
+L8:
+    %t8 = sum+1
+    sum = %t8
+    %t9 = i+1
+    i = %t9
+    goto  L2
+L4:
+    ret sum
+    goto  L1
+L1:
+After SSA
+=========
+L0:
+    arg x_0
+    sum_0 = 0
+    i_0 = 0
+    goto  L2
+L2:
+    i_1 = phi(i_0, i_1, i_1, i_2)
+    sum_1 = phi(sum_0, sum_1, sum_1, sum_2)
+    %t3_0 = i_1<x_0
+    if %t3_0 goto L3 else goto L4
+L3:
+    %t4_0 = i_1%2
+    %t5_0 = %t4_0==0
+    if %t5_0 goto L5 else goto L6
+L5:
+    goto  L2
+L6:
+    %t6_0 = i_1/3
+    %t7_0 = %t6_0==1
+    if %t7_0 goto L7 else goto L8
+L7:
+    goto  L2
+L8:
+    %t8_0 = sum_1+1
+    sum_2 = %t8_0
+    %t9_0 = i_1+1
+    i_2 = %t9_0
+    goto  L2
+L4:
+    ret sum_1
+    goto  L1
+L1:
+After converting from SSA to CSSA
+L0:
+    arg x_0
+    sum_0 = 0
+    i_0 = 0
+    (%t24,%t29) = (i_0,sum_0)
+    goto  L2
+L2:
+    %t28 = phi(%t24, %t25, %t26, %t27)
+    %t33 = phi(%t29, %t30, %t31, %t32)
+    (i_1,sum_1) = (%t28,%t33)
+    %t3_0 = i_1<x_0
+    if %t3_0 goto L3 else goto L4
+L3:
+    %t4_0 = i_1%2
+    %t5_0 = %t4_0==0
+    if %t5_0 goto L5 else goto L6
+L5:
+    (%t25,%t30) = (i_1,sum_1)
+    goto  L2
+L6:
+    %t6_0 = i_1/3
+    %t7_0 = %t6_0==1
+    if %t7_0 goto L7 else goto L8
+L7:
+    (%t26,%t31) = (i_1,sum_1)
+    goto  L2
+L8:
+    %t8_0 = sum_1+1
+    sum_2 = %t8_0
+    %t9_0 = i_1+1
+    i_2 = %t9_0
+    (%t27,%t32) = (i_2,sum_2)
+    goto  L2
+L4:
+    ret sum_1
+    goto  L1
+L1:
+After removing phis from CSSA
+L0:
+    arg x_0
+    sum_0 = 0
+    i_0 = 0
+    (%t24,%t29) = (i_0,sum_0)
+    %t28 = %t24
+    %t33 = %t29
+    goto  L2
+L2:
+    (i_1,sum_1) = (%t28,%t33)
+    %t3_0 = i_1<x_0
+    if %t3_0 goto L3 else goto L4
+L3:
+    %t4_0 = i_1%2
+    %t5_0 = %t4_0==0
+    if %t5_0 goto L5 else goto L6
+L5:
+    (%t25,%t30) = (i_1,sum_1)
+    %t28 = %t25
+    %t33 = %t30
+    goto  L2
+L6:
+    %t6_0 = i_1/3
+    %t7_0 = %t6_0==1
+    if %t7_0 goto L7 else goto L8
+L7:
+    (%t26,%t31) = (i_1,sum_1)
+    %t28 = %t26
+    %t33 = %t31
+    goto  L2
+L8:
+    %t8_0 = sum_1+1
+    sum_2 = %t8_0
+    %t9_0 = i_1+1
+    i_2 = %t9_0
+    (%t27,%t32) = (i_2,sum_2)
+    %t28 = %t27
+    %t33 = %t32
+    goto  L2
+L4:
+    ret sum_1
+    goto  L1
+L1:
+After exiting SSA
+=================
+L0:
+    arg x_0
+    sum_0 = 0
+    i_0 = 0
+    %t24 = i_0
+    %t29 = sum_0
+    %t28 = %t24
+    %t33 = %t29
+    goto  L2
+L2:
+    i_1 = %t28
+    sum_1 = %t33
+    %t3_0 = i_1<x_0
+    if %t3_0 goto L3 else goto L4
+L3:
+    %t4_0 = i_1%2
+    %t5_0 = %t4_0==0
+    if %t5_0 goto L5 else goto L6
+L5:
+    %t25 = i_1
+    %t30 = sum_1
+    %t28 = %t25
+    %t33 = %t30
+    goto  L2
+L6:
+    %t6_0 = i_1/3
+    %t7_0 = %t6_0==1
+    if %t7_0 goto L7 else goto L8
+L7:
+    %t26 = i_1
+    %t31 = sum_1
+    %t28 = %t26
+    %t33 = %t31
+    goto  L2
+L8:
+    %t8_0 = sum_1+1
+    sum_2 = %t8_0
+    %t9_0 = i_1+1
+    i_2 = %t9_0
+    %t27 = i_2
+    %t32 = sum_2
+    %t28 = %t27
+    %t33 = %t32
+    goto  L2
+L4:
+    ret sum_1
+    goto  L1
+L1:
+""",
+                result);
+
+    }
+
+    @Test
+    public void testInfiniteLoop() {
+        String src = """
+func foo() {
+   var i = 0
+   while (1) {
+        if (1)
+            continue;
+        else 
+            continue;
+   }
+}
+                """;
+        String result = compileSrc(src);
+        Assert.assertEquals("""
+func foo
+Before SSA
+==========
+L0:
+    i = 0
+    goto  L2
+L2:
+    if 1 goto L3 else goto L4
+L3:
+    if 1 goto L5 else goto L6
+L5:
+    goto  L2
+L6:
+    goto  L2
+L4:
+    goto  L1
+L1:
+After SSA
+=========
+L0:
+    i_0 = 0
+    goto  L2
+L2:
+    if 1 goto L3 else goto L4
+L3:
+    if 1 goto L5 else goto L6
+L5:
+    goto  L2
+L6:
+    goto  L2
+L4:
+    goto  L1
+L1:
+After converting from SSA to CSSA
+L0:
+    i_0 = 0
+    goto  L2
+L2:
+    if 1 goto L3 else goto L4
+L3:
+    if 1 goto L5 else goto L6
+L5:
+    goto  L2
+L6:
+    goto  L2
+L4:
+    goto  L1
+L1:
+After removing phis from CSSA
+L0:
+    i_0 = 0
+    goto  L2
+L2:
+    if 1 goto L3 else goto L4
+L3:
+    if 1 goto L5 else goto L6
+L5:
+    goto  L2
+L6:
+    goto  L2
+L4:
+    goto  L1
+L1:
+After exiting SSA
+=================
+L0:
+    i_0 = 0
+    goto  L2
+L2:
+    if 1 goto L3 else goto L4
+L3:
+    if 1 goto L5 else goto L6
+L5:
+    goto  L2
+L6:
+    goto  L2
+L4:
+    goto  L1
+L1:
+""",
+        result);
+
+    }
+
+    @Test
+    public void testParallelAssign() {
+        String src = """
+                func foo(n: Int)->Int {
+                   var a = 1
+                   var b = 2
+                   
+                   while (n > 0) {
+                        var t = a
+                        a = b
+                        b = t
+                        n = n - 1
+                   }
+                   return a
+                }
+                """;
+        String result = compileSrc(src);
+        Assert.assertEquals("""
+func foo
+Before SSA
+==========
+L0:
+    arg n
+    a = 1
+    b = 2
+    goto  L2
+L2:
+    %t4 = n>0
+    if %t4 goto L3 else goto L4
+L3:
+    t = a
+    a = b
+    b = t
+    %t5 = n-1
+    n = %t5
+    goto  L2
+L4:
+    ret a
+    goto  L1
+L1:
+After SSA
+=========
+L0:
+    arg n_0
+    a_0 = 1
+    b_0 = 2
+    goto  L2
+L2:
+    b_1 = phi(b_0, b_2)
+    a_1 = phi(a_0, a_2)
+    n_1 = phi(n_0, n_2)
+    %t4_0 = n_1>0
+    if %t4_0 goto L3 else goto L4
+L3:
+    t_0 = a_1
+    a_2 = b_1
+    b_2 = t_0
+    %t5_0 = n_1-1
+    n_2 = %t5_0
+    goto  L2
+L4:
+    ret a_1
+    goto  L1
+L1:
+After converting from SSA to CSSA
+L0:
+    arg n_0
+    a_0 = 1
+    b_0 = 2
+    (%t18,%t21,%t24) = (b_0,a_0,n_0)
+    goto  L2
+L2:
+    %t20 = phi(%t18, %t19)
+    %t23 = phi(%t21, %t22)
+    %t26 = phi(%t24, %t25)
+    (b_1,a_1,n_1) = (%t20,%t23,%t26)
+    %t4_0 = n_1>0
+    if %t4_0 goto L3 else goto L4
+L3:
+    t_0 = a_1
+    a_2 = b_1
+    b_2 = t_0
+    %t5_0 = n_1-1
+    n_2 = %t5_0
+    (%t19,%t22,%t25) = (b_2,a_2,n_2)
+    goto  L2
+L4:
+    ret a_1
+    goto  L1
+L1:
+After removing phis from CSSA
+L0:
+    arg n_0
+    a_0 = 1
+    b_0 = 2
+    (%t18,%t21,%t24) = (b_0,a_0,n_0)
+    %t20 = %t18
+    %t23 = %t21
+    %t26 = %t24
+    goto  L2
+L2:
+    (b_1,a_1,n_1) = (%t20,%t23,%t26)
+    %t4_0 = n_1>0
+    if %t4_0 goto L3 else goto L4
+L3:
+    t_0 = a_1
+    a_2 = b_1
+    b_2 = t_0
+    %t5_0 = n_1-1
+    n_2 = %t5_0
+    (%t19,%t22,%t25) = (b_2,a_2,n_2)
+    %t20 = %t19
+    %t23 = %t22
+    %t26 = %t25
+    goto  L2
+L4:
+    ret a_1
+    goto  L1
+L1:
+After exiting SSA
+=================
+L0:
+    arg n_0
+    a_0 = 1
+    b_0 = 2
+    %t18 = b_0
+    %t21 = a_0
+    %t24 = n_0
+    %t20 = %t18
+    %t23 = %t21
+    %t26 = %t24
+    goto  L2
+L2:
+    b_1 = %t20
+    a_1 = %t23
+    n_1 = %t26
+    %t4_0 = n_1>0
+    if %t4_0 goto L3 else goto L4
+L3:
+    t_0 = a_1
+    a_2 = b_1
+    b_2 = t_0
+    %t5_0 = n_1-1
+    n_2 = %t5_0
+    %t19 = b_2
+    %t22 = a_2
+    %t25 = n_2
+    %t20 = %t19
+    %t23 = %t22
+    %t26 = %t25
+    goto  L2
+L4:
+    ret a_1
+    goto  L1
+L1:
+""", result);
+    }
+
+    @Test
+    public void testSSA1() {
+        String src = """
+                func foo()->Int {
+                    var a = 5
+                    var b = 10
+                    var c = a + b
+                    return c
+                }
+                """;
+        String result = compileSrc(src);
+        Assert.assertEquals("""
+func foo
+Before SSA
+==========
+L0:
+    a = 5
+    b = 10
+    %t3 = a+b
+    c = %t3
+    ret c
+    goto  L1
+L1:
+After SSA
+=========
+L0:
+    a_0 = 5
+    b_0 = 10
+    %t3_0 = a_0+b_0
+    c_0 = %t3_0
+    ret c_0
+    goto  L1
+L1:
+After converting from SSA to CSSA
+L0:
+    a_0 = 5
+    b_0 = 10
+    %t3_0 = a_0+b_0
+    c_0 = %t3_0
+    ret c_0
+    goto  L1
+L1:
+After removing phis from CSSA
+L0:
+    a_0 = 5
+    b_0 = 10
+    %t3_0 = a_0+b_0
+    c_0 = %t3_0
+    ret c_0
+    goto  L1
+L1:
+After exiting SSA
+=================
+L0:
+    a_0 = 5
+    b_0 = 10
+    %t3_0 = a_0+b_0
+    c_0 = %t3_0
+    ret c_0
+    goto  L1
+L1:
+""", result);
+    }
+
+    @Test
+    public void testSSA2() {
+        String src = """
+                func foo()->Int {
+                    var a = 5
+                    a = a + 1;
+                    return a
+                }
+                """;
+        String result = compileSrc(src);
+        Assert.assertEquals("""
+func foo
+Before SSA
+==========
+L0:
+    a = 5
+    %t1 = a+1
+    a = %t1
+    ret a
+    goto  L1
+L1:
+After SSA
+=========
+L0:
+    a_0 = 5
+    %t1_0 = a_0+1
+    a_1 = %t1_0
+    ret a_1
+    goto  L1
+L1:
+After converting from SSA to CSSA
+L0:
+    a_0 = 5
+    %t1_0 = a_0+1
+    a_1 = %t1_0
+    ret a_1
+    goto  L1
+L1:
+After removing phis from CSSA
+L0:
+    a_0 = 5
+    %t1_0 = a_0+1
+    a_1 = %t1_0
+    ret a_1
+    goto  L1
+L1:
+After exiting SSA
+=================
+L0:
+    a_0 = 5
+    %t1_0 = a_0+1
+    a_1 = %t1_0
+    ret a_1
+    goto  L1
+L1:
+""", result);
+    }
+
+    @Test
+    public void testSSA3() {
+        String src = """
+                func foo()->Int {
+                    var a = 5
+                    if (a > 3) {
+                        a = 10
+                    } else {
+                        a = 20
+                    }
+                    return a
+                }
+                """;
+        String result = compileSrc(src);
+        Assert.assertEquals("""
+func foo
+Before SSA
+==========
+L0:
+    a = 5
+    %t1 = a>3
+    if %t1 goto L2 else goto L3
+L2:
+    a = 10
+    goto  L4
+L4:
+    ret a
+    goto  L1
+L1:
+L3:
+    a = 20
+    goto  L4
+After SSA
+=========
+L0:
+    a_0 = 5
+    %t1_0 = a_0>3
+    if %t1_0 goto L2 else goto L3
+L2:
+    a_2 = 10
+    goto  L4
+L4:
+    a_3 = phi(a_2, a_1)
+    ret a_3
+    goto  L1
+L1:
+L3:
+    a_1 = 20
+    goto  L4
+After converting from SSA to CSSA
+L0:
+    a_0 = 5
+    %t1_0 = a_0>3
+    if %t1_0 goto L2 else goto L3
+L2:
+    a_2 = 10
+    (%t7) = (a_2)
+    goto  L4
+L4:
+    %t9 = phi(%t7, %t8)
+    (a_3) = (%t9)
+    ret a_3
+    goto  L1
+L1:
+L3:
+    a_1 = 20
+    (%t8) = (a_1)
+    goto  L4
+After removing phis from CSSA
+L0:
+    a_0 = 5
+    %t1_0 = a_0>3
+    if %t1_0 goto L2 else goto L3
+L2:
+    a_2 = 10
+    (%t7) = (a_2)
+    %t9 = %t7
+    goto  L4
+L4:
+    (a_3) = (%t9)
+    ret a_3
+    goto  L1
+L1:
+L3:
+    a_1 = 20
+    (%t8) = (a_1)
+    %t9 = %t8
+    goto  L4
+After exiting SSA
+=================
+L0:
+    a_0 = 5
+    %t1_0 = a_0>3
+    if %t1_0 goto L2 else goto L3
+L2:
+    a_2 = 10
+    %t7 = a_2
+    %t9 = %t7
+    goto  L4
+L4:
+    a_3 = %t9
+    ret a_3
+    goto  L1
+L1:
+L3:
+    a_1 = 20
+    %t8 = a_1
+    %t9 = %t8
+    goto  L4
+""", result);
+    }
+
+    @Test
+    public void testSSA4() {
+        String src = """
+                func foo()->Int {
+                    var a = 0
+                    while (a < 5) {
+                        a = a + 1;
+                    }
+                    return a
+                }
+                """;
+        String result = compileSrc(src);
+        Assert.assertEquals("""
+func foo
+Before SSA
+==========
+L0:
+    a = 0
+    goto  L2
+L2:
+    %t1 = a<5
+    if %t1 goto L3 else goto L4
+L3:
+    %t2 = a+1
+    a = %t2
+    goto  L2
+L4:
+    ret a
+    goto  L1
+L1:
+After SSA
+=========
+L0:
+    a_0 = 0
+    goto  L2
+L2:
+    a_1 = phi(a_0, a_2)
+    %t1_0 = a_1<5
+    if %t1_0 goto L3 else goto L4
+L3:
+    %t2_0 = a_1+1
+    a_2 = %t2_0
+    goto  L2
+L4:
+    ret a_1
+    goto  L1
+L1:
+After converting from SSA to CSSA
+L0:
+    a_0 = 0
+    (%t8) = (a_0)
+    goto  L2
+L2:
+    %t10 = phi(%t8, %t9)
+    (a_1) = (%t10)
+    %t1_0 = a_1<5
+    if %t1_0 goto L3 else goto L4
+L3:
+    %t2_0 = a_1+1
+    a_2 = %t2_0
+    (%t9) = (a_2)
+    goto  L2
+L4:
+    ret a_1
+    goto  L1
+L1:
+After removing phis from CSSA
+L0:
+    a_0 = 0
+    (%t8) = (a_0)
+    %t10 = %t8
+    goto  L2
+L2:
+    (a_1) = (%t10)
+    %t1_0 = a_1<5
+    if %t1_0 goto L3 else goto L4
+L3:
+    %t2_0 = a_1+1
+    a_2 = %t2_0
+    (%t9) = (a_2)
+    %t10 = %t9
+    goto  L2
+L4:
+    ret a_1
+    goto  L1
+L1:
+After exiting SSA
+=================
+L0:
+    a_0 = 0
+    %t8 = a_0
+    %t10 = %t8
+    goto  L2
+L2:
+    a_1 = %t10
+    %t1_0 = a_1<5
+    if %t1_0 goto L3 else goto L4
+L3:
+    %t2_0 = a_1+1
+    a_2 = %t2_0
+    %t9 = a_2
+    %t10 = %t9
+    goto  L2
+L4:
+    ret a_1
+    goto  L1
+L1:
+""", result);
+    }
+
+    @Test
+    public void testSSA5() {
+        String src = """
+                func foo()->Int {
+                    var a = 0
+                    var b = 10
+                    if (b > 5) {
+                        if (a < 5) {
+                            a = 5
+                        } else {
+                            a = 15
+                        }
+                    } else {
+                        a = 20
+                    }
+                    return a
+                }
+                """;
+        String result = compileSrc(src);
+        Assert.assertEquals("""
+func foo
+Before SSA
+==========
+L0:
+    a = 0
+    b = 10
+    %t2 = b>5
+    if %t2 goto L2 else goto L3
+L2:
+    %t3 = a<5
+    if %t3 goto L5 else goto L6
+L5:
+    a = 5
+    goto  L7
+L7:
+    goto  L4
+L4:
+    ret a
+    goto  L1
+L1:
+L6:
+    a = 15
+    goto  L7
+L3:
+    a = 20
+    goto  L4
+After SSA
+=========
+L0:
+    a_0 = 0
+    b_0 = 10
+    %t2_0 = b_0>5
+    if %t2_0 goto L2 else goto L3
+L2:
+    %t3_0 = a_0<5
+    if %t3_0 goto L5 else goto L6
+L5:
+    a_3 = 5
+    goto  L7
+L7:
+    a_4 = phi(a_3, a_2)
+    goto  L4
+L4:
+    a_5 = phi(a_4, a_1)
+    ret a_5
+    goto  L1
+L1:
+L6:
+    a_2 = 15
+    goto  L7
+L3:
+    a_1 = 20
+    goto  L4
+After converting from SSA to CSSA
+L0:
+    a_0 = 0
+    b_0 = 10
+    %t2_0 = b_0>5
+    if %t2_0 goto L2 else goto L3
+L2:
+    %t3_0 = a_0<5
+    if %t3_0 goto L5 else goto L6
+L5:
+    a_3 = 5
+    (%t16) = (a_3)
+    goto  L7
+L7:
+    %t18 = phi(%t16, %t17)
+    (a_4) = (%t18)
+    (%t13) = (a_4)
+    goto  L4
+L4:
+    %t15 = phi(%t13, %t14)
+    (a_5) = (%t15)
+    ret a_5
+    goto  L1
+L1:
+L6:
+    a_2 = 15
+    (%t17) = (a_2)
+    goto  L7
+L3:
+    a_1 = 20
+    (%t14) = (a_1)
+    goto  L4
+After removing phis from CSSA
+L0:
+    a_0 = 0
+    b_0 = 10
+    %t2_0 = b_0>5
+    if %t2_0 goto L2 else goto L3
+L2:
+    %t3_0 = a_0<5
+    if %t3_0 goto L5 else goto L6
+L5:
+    a_3 = 5
+    (%t16) = (a_3)
+    %t18 = %t16
+    goto  L7
+L7:
+    (a_4) = (%t18)
+    (%t13) = (a_4)
+    %t15 = %t13
+    goto  L4
+L4:
+    (a_5) = (%t15)
+    ret a_5
+    goto  L1
+L1:
+L6:
+    a_2 = 15
+    (%t17) = (a_2)
+    %t18 = %t17
+    goto  L7
+L3:
+    a_1 = 20
+    (%t14) = (a_1)
+    %t15 = %t14
+    goto  L4
+After exiting SSA
+=================
+L0:
+    a_0 = 0
+    b_0 = 10
+    %t2_0 = b_0>5
+    if %t2_0 goto L2 else goto L3
+L2:
+    %t3_0 = a_0<5
+    if %t3_0 goto L5 else goto L6
+L5:
+    a_3 = 5
+    %t16 = a_3
+    %t18 = %t16
+    goto  L7
+L7:
+    a_4 = %t18
+    %t13 = a_4
+    %t15 = %t13
+    goto  L4
+L4:
+    a_5 = %t15
+    ret a_5
+    goto  L1
+L1:
+L6:
+    a_2 = 15
+    %t17 = a_2
+    %t18 = %t17
+    goto  L7
+L3:
+    a_1 = 20
+    %t14 = a_1
+    %t15 = %t14
+    goto  L4
+""", result);
+    }
+
+    @Test
+    public void testSSA6() {
+        String src = """
+                func foo()->Int {
+                    var arr = new [Int] {1, 2};
+                    arr[0] = 10
+                    var x = arr[0]
+                    return x
+                }
+                """;
+        String result = compileSrc(src);
+        Assert.assertEquals("""
+func foo
+Before SSA
+==========
+L0:
+    %t2 = New([Int], len=2)
+    %t2[0] = 1
+    %t2[1] = 2
+    arr = %t2
+    arr[0] = 10
+    %t3 = arr[0]
+    x = %t3
+    ret x
+    goto  L1
+L1:
+After SSA
+=========
+L0:
+    %t2_0 = New([Int], len=2)
+    %t2_0[0] = 1
+    %t2_0[1] = 2
+    arr_0 = %t2_0
+    arr_0[0] = 10
+    %t3_0 = arr_0[0]
+    x_0 = %t3_0
+    ret x_0
+    goto  L1
+L1:
+After converting from SSA to CSSA
+L0:
+    %t2_0 = New([Int], len=2)
+    %t2_0[0] = 1
+    %t2_0[1] = 2
+    arr_0 = %t2_0
+    arr_0[0] = 10
+    %t3_0 = arr_0[0]
+    x_0 = %t3_0
+    ret x_0
+    goto  L1
+L1:
+After removing phis from CSSA
+L0:
+    %t2_0 = New([Int], len=2)
+    %t2_0[0] = 1
+    %t2_0[1] = 2
+    arr_0 = %t2_0
+    arr_0[0] = 10
+    %t3_0 = arr_0[0]
+    x_0 = %t3_0
+    ret x_0
+    goto  L1
+L1:
+After exiting SSA
+=================
+L0:
+    %t2_0 = New([Int], len=2)
+    %t2_0[0] = 1
+    %t2_0[1] = 2
+    arr_0 = %t2_0
+    arr_0[0] = 10
+    %t3_0 = arr_0[0]
+    x_0 = %t3_0
+    ret x_0
+    goto  L1
+L1:
+""", result);
+    }
+
+    @Test
+    public void testSSA7() {
+        String src = """
+        func add(x: Int, y : Int)->Int {
+            return x + y
+        }
+        func main()->Int {
+            var a = 5
+            var b = 10
+            var c = add(a, b)
+            return c
+        }
+""";
+        String result = compileSrc(src);
+        Assert.assertEquals("""
+func add
+Before SSA
+==========
+L0:
+    arg x
+    arg y
+    %t2 = x+y
+    ret %t2
+    goto  L1
+L1:
+After SSA
+=========
+L0:
+    arg x_0
+    arg y_0
+    %t2_0 = x_0+y_0
+    ret %t2_0
+    goto  L1
+L1:
+After converting from SSA to CSSA
+L0:
+    arg x_0
+    arg y_0
+    %t2_0 = x_0+y_0
+    ret %t2_0
+    goto  L1
+L1:
+After removing phis from CSSA
+L0:
+    arg x_0
+    arg y_0
+    %t2_0 = x_0+y_0
+    ret %t2_0
+    goto  L1
+L1:
+After exiting SSA
+=================
+L0:
+    arg x_0
+    arg y_0
+    %t2_0 = x_0+y_0
+    ret %t2_0
+    goto  L1
+L1:
+func main
+Before SSA
+==========
+L0:
+    a = 5
+    b = 10
+    %t3 = a
+    %t4 = b
+    %t5 = call add params %t3, %t4
+    c = %t5
+    ret c
+    goto  L1
+L1:
+After SSA
+=========
+L0:
+    a_0 = 5
+    b_0 = 10
+    %t3_0 = a_0
+    %t4_0 = b_0
+    %t5_0 = call add params %t3_0, %t4_0
+    c_0 = %t5_0
+    ret c_0
+    goto  L1
+L1:
+After converting from SSA to CSSA
+L0:
+    a_0 = 5
+    b_0 = 10
+    %t3_0 = a_0
+    %t4_0 = b_0
+    %t5_0 = call add params %t3_0, %t4_0
+    c_0 = %t5_0
+    ret c_0
+    goto  L1
+L1:
+After removing phis from CSSA
+L0:
+    a_0 = 5
+    b_0 = 10
+    %t3_0 = a_0
+    %t4_0 = b_0
+    %t5_0 = call add params %t3_0, %t4_0
+    c_0 = %t5_0
+    ret c_0
+    goto  L1
+L1:
+After exiting SSA
+=================
+L0:
+    a_0 = 5
+    b_0 = 10
+    %t3_0 = a_0
+    %t4_0 = b_0
+    %t5_0 = call add params %t3_0, %t4_0
+    c_0 = %t5_0
+    ret c_0
+    goto  L1
+L1:
+""", result);
+    }
+
+    @Test
+    public void testSSA8() {
+        String src = """
+        func main()->Int {
+            var a = 0
+            var b = 1
+            while (a < 10) {
+                a = a + 2
+            }
+            while (b < 20) {
+                b = b + 3
+            }
+            return a + b
+        }
+""";
+        String result = compileSrc(src);
+        Assert.assertEquals("""
+func main
+Before SSA
+==========
+L0:
+    a = 0
+    b = 1
+    goto  L2
+L2:
+    %t2 = a<10
+    if %t2 goto L3 else goto L4
+L3:
+    %t3 = a+2
+    a = %t3
+    goto  L2
+L4:
+    goto  L5
+L5:
+    %t4 = b<20
+    if %t4 goto L6 else goto L7
+L6:
+    %t5 = b+3
+    b = %t5
+    goto  L5
+L7:
+    %t6 = a+b
+    ret %t6
+    goto  L1
+L1:
+After SSA
+=========
+L0:
+    a_0 = 0
+    b_0 = 1
+    goto  L2
+L2:
+    a_1 = phi(a_0, a_2)
+    %t2_0 = a_1<10
+    if %t2_0 goto L3 else goto L4
+L3:
+    %t3_0 = a_1+2
+    a_2 = %t3_0
+    goto  L2
+L4:
+    goto  L5
+L5:
+    b_1 = phi(b_0, b_2)
+    %t4_0 = b_1<20
+    if %t4_0 goto L6 else goto L7
+L6:
+    %t5_0 = b_1+3
+    b_2 = %t5_0
+    goto  L5
+L7:
+    %t6_0 = a_1+b_1
+    ret %t6_0
+    goto  L1
+L1:
+After converting from SSA to CSSA
+L0:
+    a_0 = 0
+    b_0 = 1
+    (%t21) = (a_0)
+    goto  L2
+L2:
+    %t23 = phi(%t21, %t22)
+    (a_1) = (%t23)
+    %t2_0 = a_1<10
+    if %t2_0 goto L3 else goto L4
+L3:
+    %t3_0 = a_1+2
+    a_2 = %t3_0
+    (%t22) = (a_2)
+    goto  L2
+L4:
+    (%t18) = (b_0)
+    goto  L5
+L5:
+    %t20 = phi(%t18, %t19)
+    (b_1) = (%t20)
+    %t4_0 = b_1<20
+    if %t4_0 goto L6 else goto L7
+L6:
+    %t5_0 = b_1+3
+    b_2 = %t5_0
+    (%t19) = (b_2)
+    goto  L5
+L7:
+    %t6_0 = a_1+b_1
+    ret %t6_0
+    goto  L1
+L1:
+After removing phis from CSSA
+L0:
+    a_0 = 0
+    b_0 = 1
+    (%t21) = (a_0)
+    %t23 = %t21
+    goto  L2
+L2:
+    (a_1) = (%t23)
+    %t2_0 = a_1<10
+    if %t2_0 goto L3 else goto L4
+L3:
+    %t3_0 = a_1+2
+    a_2 = %t3_0
+    (%t22) = (a_2)
+    %t23 = %t22
+    goto  L2
+L4:
+    (%t18) = (b_0)
+    %t20 = %t18
+    goto  L5
+L5:
+    (b_1) = (%t20)
+    %t4_0 = b_1<20
+    if %t4_0 goto L6 else goto L7
+L6:
+    %t5_0 = b_1+3
+    b_2 = %t5_0
+    (%t19) = (b_2)
+    %t20 = %t19
+    goto  L5
+L7:
+    %t6_0 = a_1+b_1
+    ret %t6_0
+    goto  L1
+L1:
+After exiting SSA
+=================
+L0:
+    a_0 = 0
+    b_0 = 1
+    %t21 = a_0
+    %t23 = %t21
+    goto  L2
+L2:
+    a_1 = %t23
+    %t2_0 = a_1<10
+    if %t2_0 goto L3 else goto L4
+L3:
+    %t3_0 = a_1+2
+    a_2 = %t3_0
+    %t22 = a_2
+    %t23 = %t22
+    goto  L2
+L4:
+    %t18 = b_0
+    %t20 = %t18
+    goto  L5
+L5:
+    b_1 = %t20
+    %t4_0 = b_1<20
+    if %t4_0 goto L6 else goto L7
+L6:
+    %t5_0 = b_1+3
+    b_2 = %t5_0
+    %t19 = b_2
+    %t20 = %t19
+    goto  L5
+L7:
+    %t6_0 = a_1+b_1
+    ret %t6_0
+    goto  L1
+L1:
+""", result);
+    }
+
+    @Test
+    public void testSSA9() {
+        String src = """
+        func main()->Int {
+            var a = 0
+            var b = 0
+            var i = 0
+            var j = 0
+            while (i < 3) {
+                j = 0
+                while (j < 2) {
+                    a = a + 1
+                    i = j + 1
+                }
+                b = b + 1;
+                i = i + 1    
+            }
+            return a + b
+        }
+""";
+        String result = compileSrc(src);
+        Assert.assertEquals("""
+func main
+Before SSA
+==========
+L0:
+    a = 0
+    b = 0
+    i = 0
+    j = 0
+    goto  L2
+L2:
+    %t4 = i<3
+    if %t4 goto L3 else goto L4
+L3:
+    j = 0
+    goto  L5
+L5:
+    %t5 = j<2
+    if %t5 goto L6 else goto L7
+L6:
+    %t6 = a+1
+    a = %t6
+    %t7 = j+1
+    i = %t7
+    goto  L5
+L7:
+    %t8 = b+1
+    b = %t8
+    %t9 = i+1
+    i = %t9
+    goto  L2
+L4:
+    %t10 = a+b
+    ret %t10
+    goto  L1
+L1:
+After SSA
+=========
+L0:
+    a_0 = 0
+    b_0 = 0
+    i_0 = 0
+    j_0 = 0
+    goto  L2
+L2:
+    i_1 = phi(i_0, i_3)
+    b_1 = phi(b_0, b_2)
+    a_1 = phi(a_0, a_2)
+    %t4_0 = i_1<3
+    if %t4_0 goto L3 else goto L4
+L3:
+    j_1 = 0
+    goto  L5
+L5:
+    i_2 = phi(i_1, i_4)
+    a_2 = phi(a_1, a_3)
+    %t5_0 = j_1<2
+    if %t5_0 goto L6 else goto L7
+L6:
+    %t6_0 = a_2+1
+    a_3 = %t6_0
+    %t7_0 = j_1+1
+    i_4 = %t7_0
+    goto  L5
+L7:
+    %t8_0 = b_1+1
+    b_2 = %t8_0
+    %t9_0 = i_2+1
+    i_3 = %t9_0
+    goto  L2
+L4:
+    %t10_0 = a_1+b_1
+    ret %t10_0
+    goto  L1
+L1:
+After converting from SSA to CSSA
+L0:
+    a_0 = 0
+    b_0 = 0
+    i_0 = 0
+    j_0 = 0
+    (%t38,%t41,%t44) = (i_0,b_0,a_0)
+    goto  L2
+L2:
+    %t40 = phi(%t38, %t39)
+    %t43 = phi(%t41, %t42)
+    %t46 = phi(%t44, %t45)
+    (i_1,b_1,a_1) = (%t40,%t43,%t46)
+    %t4_0 = i_1<3
+    if %t4_0 goto L3 else goto L4
+L3:
+    j_1 = 0
+    (%t32,%t35) = (i_1,a_1)
+    goto  L5
+L5:
+    %t34 = phi(%t32, %t33)
+    %t37 = phi(%t35, %t36)
+    (i_2,a_2) = (%t34,%t37)
+    %t5_0 = j_1<2
+    if %t5_0 goto L6 else goto L7
+L6:
+    %t6_0 = a_2+1
+    a_3 = %t6_0
+    %t7_0 = j_1+1
+    i_4 = %t7_0
+    (%t33,%t36) = (i_4,a_3)
+    goto  L5
+L7:
+    %t8_0 = b_1+1
+    b_2 = %t8_0
+    %t9_0 = i_2+1
+    i_3 = %t9_0
+    (%t39,%t42,%t45) = (i_3,b_2,a_2)
+    goto  L2
+L4:
+    %t10_0 = a_1+b_1
+    ret %t10_0
+    goto  L1
+L1:
+After removing phis from CSSA
+L0:
+    a_0 = 0
+    b_0 = 0
+    i_0 = 0
+    j_0 = 0
+    (%t38,%t41,%t44) = (i_0,b_0,a_0)
+    %t40 = %t38
+    %t43 = %t41
+    %t46 = %t44
+    goto  L2
+L2:
+    (i_1,b_1,a_1) = (%t40,%t43,%t46)
+    %t4_0 = i_1<3
+    if %t4_0 goto L3 else goto L4
+L3:
+    j_1 = 0
+    (%t32,%t35) = (i_1,a_1)
+    %t34 = %t32
+    %t37 = %t35
+    goto  L5
+L5:
+    (i_2,a_2) = (%t34,%t37)
+    %t5_0 = j_1<2
+    if %t5_0 goto L6 else goto L7
+L6:
+    %t6_0 = a_2+1
+    a_3 = %t6_0
+    %t7_0 = j_1+1
+    i_4 = %t7_0
+    (%t33,%t36) = (i_4,a_3)
+    %t34 = %t33
+    %t37 = %t36
+    goto  L5
+L7:
+    %t8_0 = b_1+1
+    b_2 = %t8_0
+    %t9_0 = i_2+1
+    i_3 = %t9_0
+    (%t39,%t42,%t45) = (i_3,b_2,a_2)
+    %t40 = %t39
+    %t43 = %t42
+    %t46 = %t45
+    goto  L2
+L4:
+    %t10_0 = a_1+b_1
+    ret %t10_0
+    goto  L1
+L1:
+After exiting SSA
+=================
+L0:
+    a_0 = 0
+    b_0 = 0
+    i_0 = 0
+    j_0 = 0
+    %t38 = i_0
+    %t41 = b_0
+    %t44 = a_0
+    %t40 = %t38
+    %t43 = %t41
+    %t46 = %t44
+    goto  L2
+L2:
+    i_1 = %t40
+    b_1 = %t43
+    a_1 = %t46
+    %t4_0 = i_1<3
+    if %t4_0 goto L3 else goto L4
+L3:
+    j_1 = 0
+    %t32 = i_1
+    %t35 = a_1
+    %t34 = %t32
+    %t37 = %t35
+    goto  L5
+L5:
+    i_2 = %t34
+    a_2 = %t37
+    %t5_0 = j_1<2
+    if %t5_0 goto L6 else goto L7
+L6:
+    %t6_0 = a_2+1
+    a_3 = %t6_0
+    %t7_0 = j_1+1
+    i_4 = %t7_0
+    %t33 = i_4
+    %t36 = a_3
+    %t34 = %t33
+    %t37 = %t36
+    goto  L5
+L7:
+    %t8_0 = b_1+1
+    b_2 = %t8_0
+    %t9_0 = i_2+1
+    i_3 = %t9_0
+    %t39 = i_3
+    %t42 = b_2
+    %t45 = a_2
+    %t40 = %t39
+    %t43 = %t42
+    %t46 = %t45
+    goto  L2
+L4:
+    %t10_0 = a_1+b_1
+    ret %t10_0
+    goto  L1
+L1:
+""", result);
+    }
+
+    @Test
+    public void testSSA10() {
+        String src = """
+        func main()->Int {
+            var sum = 0
+            var i = 0
+            var j = 0
+            while (i < 5) {
+                j = 0
+                while (j < 5) {
+                    if (j % 2 == 0) 
+                        sum = sum + j
+                    j = j + 1    
+                }
+                i = i + 1    
+            }
+            return sum
+        }
+""";
+        String result = compileSrc(src);
+        Assert.assertEquals("""
+func main
+Before SSA
+==========
+L0:
+    sum = 0
+    i = 0
+    j = 0
+    goto  L2
+L2:
+    %t3 = i<5
+    if %t3 goto L3 else goto L4
+L3:
+    j = 0
+    goto  L5
+L5:
+    %t4 = j<5
+    if %t4 goto L6 else goto L7
+L6:
+    %t5 = j%2
+    %t6 = %t5==0
+    if %t6 goto L8 else goto L9
+L8:
+    %t7 = sum+j
+    sum = %t7
+    goto  L9
+L9:
+    %t8 = j+1
+    j = %t8
+    goto  L5
+L7:
+    %t9 = i+1
+    i = %t9
+    goto  L2
+L4:
+    ret sum
+    goto  L1
+L1:
+After SSA
+=========
+L0:
+    sum_0 = 0
+    i_0 = 0
+    j_0 = 0
+    goto  L2
+L2:
+    i_1 = phi(i_0, i_2)
+    sum_1 = phi(sum_0, sum_2)
+    %t3_0 = i_1<5
+    if %t3_0 goto L3 else goto L4
+L3:
+    j_1 = 0
+    goto  L5
+L5:
+    j_2 = phi(j_1, j_3)
+    sum_2 = phi(sum_1, sum_4)
+    %t4_0 = j_2<5
+    if %t4_0 goto L6 else goto L7
+L6:
+    %t5_0 = j_2%2
+    %t6_0 = %t5_0==0
+    if %t6_0 goto L8 else goto L9
+L8:
+    %t7_0 = sum_2+j_2
+    sum_3 = %t7_0
+    goto  L9
+L9:
+    sum_4 = phi(sum_2, sum_3)
+    %t8_0 = j_2+1
+    j_3 = %t8_0
+    goto  L5
+L7:
+    %t9_0 = i_1+1
+    i_2 = %t9_0
+    goto  L2
+L4:
+    ret sum_1
+    goto  L1
+L1:
+After converting from SSA to CSSA
+L0:
+    sum_0 = 0
+    i_0 = 0
+    j_0 = 0
+    (%t38,%t41) = (i_0,sum_0)
+    goto  L2
+L2:
+    %t40 = phi(%t38, %t39)
+    %t43 = phi(%t41, %t42)
+    (i_1,sum_1) = (%t40,%t43)
+    %t3_0 = i_1<5
+    if %t3_0 goto L3 else goto L4
+L3:
+    j_1 = 0
+    (%t32,%t35) = (j_1,sum_1)
+    goto  L5
+L5:
+    %t34 = phi(%t32, %t33)
+    %t37 = phi(%t35, %t36)
+    (j_2,sum_2) = (%t34,%t37)
+    %t4_0 = j_2<5
+    if %t4_0 goto L6 else goto L7
+L6:
+    %t5_0 = j_2%2
+    %t6_0 = %t5_0==0
+    (%t29) = (sum_2)
+    if %t6_0 goto L8 else goto L9
+L8:
+    %t7_0 = sum_2+j_2
+    sum_3 = %t7_0
+    (%t30) = (sum_3)
+    goto  L9
+L9:
+    %t31 = phi(%t29, %t30)
+    (sum_4) = (%t31)
+    %t8_0 = j_2+1
+    j_3 = %t8_0
+    (%t33,%t36) = (j_3,sum_4)
+    goto  L5
+L7:
+    %t9_0 = i_1+1
+    i_2 = %t9_0
+    (%t39,%t42) = (i_2,sum_2)
+    goto  L2
+L4:
+    ret sum_1
+    goto  L1
+L1:
+After removing phis from CSSA
+L0:
+    sum_0 = 0
+    i_0 = 0
+    j_0 = 0
+    (%t38,%t41) = (i_0,sum_0)
+    %t40 = %t38
+    %t43 = %t41
+    goto  L2
+L2:
+    (i_1,sum_1) = (%t40,%t43)
+    %t3_0 = i_1<5
+    if %t3_0 goto L3 else goto L4
+L3:
+    j_1 = 0
+    (%t32,%t35) = (j_1,sum_1)
+    %t34 = %t32
+    %t37 = %t35
+    goto  L5
+L5:
+    (j_2,sum_2) = (%t34,%t37)
+    %t4_0 = j_2<5
+    if %t4_0 goto L6 else goto L7
+L6:
+    %t5_0 = j_2%2
+    %t6_0 = %t5_0==0
+    (%t29) = (sum_2)
+    %t31 = %t29
+    if %t6_0 goto L8 else goto L9
+L8:
+    %t7_0 = sum_2+j_2
+    sum_3 = %t7_0
+    (%t30) = (sum_3)
+    %t31 = %t30
+    goto  L9
+L9:
+    (sum_4) = (%t31)
+    %t8_0 = j_2+1
+    j_3 = %t8_0
+    (%t33,%t36) = (j_3,sum_4)
+    %t34 = %t33
+    %t37 = %t36
+    goto  L5
+L7:
+    %t9_0 = i_1+1
+    i_2 = %t9_0
+    (%t39,%t42) = (i_2,sum_2)
+    %t40 = %t39
+    %t43 = %t42
+    goto  L2
+L4:
+    ret sum_1
+    goto  L1
+L1:
+After exiting SSA
+=================
+L0:
+    sum_0 = 0
+    i_0 = 0
+    j_0 = 0
+    %t38 = i_0
+    %t41 = sum_0
+    %t40 = %t38
+    %t43 = %t41
+    goto  L2
+L2:
+    i_1 = %t40
+    sum_1 = %t43
+    %t3_0 = i_1<5
+    if %t3_0 goto L3 else goto L4
+L3:
+    j_1 = 0
+    %t32 = j_1
+    %t35 = sum_1
+    %t34 = %t32
+    %t37 = %t35
+    goto  L5
+L5:
+    j_2 = %t34
+    sum_2 = %t37
+    %t4_0 = j_2<5
+    if %t4_0 goto L6 else goto L7
+L6:
+    %t5_0 = j_2%2
+    %t6_0 = %t5_0==0
+    %t29 = sum_2
+    %t31 = %t29
+    if %t6_0 goto L8 else goto L9
+L8:
+    %t7_0 = sum_2+j_2
+    sum_3 = %t7_0
+    %t30 = sum_3
+    %t31 = %t30
+    goto  L9
+L9:
+    sum_4 = %t31
+    %t8_0 = j_2+1
+    j_3 = %t8_0
+    %t33 = j_3
+    %t36 = sum_4
+    %t34 = %t33
+    %t37 = %t36
+    goto  L5
+L7:
+    %t9_0 = i_1+1
+    i_2 = %t9_0
+    %t39 = i_2
+    %t42 = sum_2
+    %t40 = %t39
+    %t43 = %t42
+    goto  L2
+L4:
+    ret sum_1
+    goto  L1
+L1:
+""", result);
+    }
+
+    @Test
+    public void testSSA11() {
+        String src = """
+        func main()->Int {
+            var a = 0
+            var i = 0
+            var j = 0
+            while (i < 3) {
+                j = 0
+                while (j < 3) {
+                    if (i == j) 
+                        a = a + i + j
+                    else if (i > j)
+                        a = a - 1
+                    j = j + 1        
+                }
+                i = i + 1    
+            }
+            return a
+        }
+""";
+        String result = compileSrc(src);
+        Assert.assertEquals("""
+func main
+Before SSA
+==========
+L0:
+    a = 0
+    i = 0
+    j = 0
+    goto  L2
+L2:
+    %t3 = i<3
+    if %t3 goto L3 else goto L4
+L3:
+    j = 0
+    goto  L5
+L5:
+    %t4 = j<3
+    if %t4 goto L6 else goto L7
+L6:
+    %t5 = i==j
+    if %t5 goto L8 else goto L9
+L8:
+    %t6 = a+i
+    %t7 = %t6+j
+    a = %t7
+    goto  L10
+L10:
+    %t10 = j+1
+    j = %t10
+    goto  L5
+L9:
+    %t8 = i>j
+    if %t8 goto L11 else goto L12
+L11:
+    %t9 = a-1
+    a = %t9
+    goto  L12
+L12:
+    goto  L10
+L7:
+    %t11 = i+1
+    i = %t11
+    goto  L2
+L4:
+    ret a
+    goto  L1
+L1:
+After SSA
+=========
+L0:
+    a_0 = 0
+    i_0 = 0
+    j_0 = 0
+    goto  L2
+L2:
+    i_1 = phi(i_0, i_2)
+    a_1 = phi(a_0, a_2)
+    %t3_0 = i_1<3
+    if %t3_0 goto L3 else goto L4
+L3:
+    j_1 = 0
+    goto  L5
+L5:
+    j_2 = phi(j_1, j_3)
+    a_2 = phi(a_1, a_6)
+    %t4_0 = j_2<3
+    if %t4_0 goto L6 else goto L7
+L6:
+    %t5_0 = i_1==j_2
+    if %t5_0 goto L8 else goto L9
+L8:
+    %t6_0 = a_2+i_1
+    %t7_0 = %t6_0+j_2
+    a_5 = %t7_0
+    goto  L10
+L10:
+    a_6 = phi(a_5, a_4)
+    %t10_0 = j_2+1
+    j_3 = %t10_0
+    goto  L5
+L9:
+    %t8_0 = i_1>j_2
+    if %t8_0 goto L11 else goto L12
+L11:
+    %t9_0 = a_2-1
+    a_3 = %t9_0
+    goto  L12
+L12:
+    a_4 = phi(a_2, a_3)
+    goto  L10
+L7:
+    %t11_0 = i_1+1
+    i_2 = %t11_0
+    goto  L2
+L4:
+    ret a_1
+    goto  L1
+L1:
+After converting from SSA to CSSA
+L0:
+    a_0 = 0
+    i_0 = 0
+    j_0 = 0
+    (%t47,%t50) = (i_0,a_0)
+    goto  L2
+L2:
+    %t49 = phi(%t47, %t48)
+    %t52 = phi(%t50, %t51)
+    (i_1,a_1) = (%t49,%t52)
+    %t3_0 = i_1<3
+    if %t3_0 goto L3 else goto L4
+L3:
+    j_1 = 0
+    (%t41,%t44) = (j_1,a_1)
+    goto  L5
+L5:
+    %t43 = phi(%t41, %t42)
+    %t46 = phi(%t44, %t45)
+    (j_2,a_2) = (%t43,%t46)
+    %t4_0 = j_2<3
+    if %t4_0 goto L6 else goto L7
+L6:
+    %t5_0 = i_1==j_2
+    if %t5_0 goto L8 else goto L9
+L8:
+    %t6_0 = a_2+i_1
+    %t7_0 = %t6_0+j_2
+    a_5 = %t7_0
+    (%t35) = (a_5)
+    goto  L10
+L10:
+    %t37 = phi(%t35, %t36)
+    (a_6) = (%t37)
+    %t10_0 = j_2+1
+    j_3 = %t10_0
+    (%t42,%t45) = (j_3,a_6)
+    goto  L5
+L9:
+    %t8_0 = i_1>j_2
+    (%t38) = (a_2)
+    if %t8_0 goto L11 else goto L12
+L11:
+    %t9_0 = a_2-1
+    a_3 = %t9_0
+    (%t39) = (a_3)
+    goto  L12
+L12:
+    %t40 = phi(%t38, %t39)
+    (a_4) = (%t40)
+    (%t36) = (a_4)
+    goto  L10
+L7:
+    %t11_0 = i_1+1
+    i_2 = %t11_0
+    (%t48,%t51) = (i_2,a_2)
+    goto  L2
+L4:
+    ret a_1
+    goto  L1
+L1:
+After removing phis from CSSA
+L0:
+    a_0 = 0
+    i_0 = 0
+    j_0 = 0
+    (%t47,%t50) = (i_0,a_0)
+    %t49 = %t47
+    %t52 = %t50
+    goto  L2
+L2:
+    (i_1,a_1) = (%t49,%t52)
+    %t3_0 = i_1<3
+    if %t3_0 goto L3 else goto L4
+L3:
+    j_1 = 0
+    (%t41,%t44) = (j_1,a_1)
+    %t43 = %t41
+    %t46 = %t44
+    goto  L5
+L5:
+    (j_2,a_2) = (%t43,%t46)
+    %t4_0 = j_2<3
+    if %t4_0 goto L6 else goto L7
+L6:
+    %t5_0 = i_1==j_2
+    if %t5_0 goto L8 else goto L9
+L8:
+    %t6_0 = a_2+i_1
+    %t7_0 = %t6_0+j_2
+    a_5 = %t7_0
+    (%t35) = (a_5)
+    %t37 = %t35
+    goto  L10
+L10:
+    (a_6) = (%t37)
+    %t10_0 = j_2+1
+    j_3 = %t10_0
+    (%t42,%t45) = (j_3,a_6)
+    %t43 = %t42
+    %t46 = %t45
+    goto  L5
+L9:
+    %t8_0 = i_1>j_2
+    (%t38) = (a_2)
+    %t40 = %t38
+    if %t8_0 goto L11 else goto L12
+L11:
+    %t9_0 = a_2-1
+    a_3 = %t9_0
+    (%t39) = (a_3)
+    %t40 = %t39
+    goto  L12
+L12:
+    (a_4) = (%t40)
+    (%t36) = (a_4)
+    %t37 = %t36
+    goto  L10
+L7:
+    %t11_0 = i_1+1
+    i_2 = %t11_0
+    (%t48,%t51) = (i_2,a_2)
+    %t49 = %t48
+    %t52 = %t51
+    goto  L2
+L4:
+    ret a_1
+    goto  L1
+L1:
+After exiting SSA
+=================
+L0:
+    a_0 = 0
+    i_0 = 0
+    j_0 = 0
+    %t47 = i_0
+    %t50 = a_0
+    %t49 = %t47
+    %t52 = %t50
+    goto  L2
+L2:
+    i_1 = %t49
+    a_1 = %t52
+    %t3_0 = i_1<3
+    if %t3_0 goto L3 else goto L4
+L3:
+    j_1 = 0
+    %t41 = j_1
+    %t44 = a_1
+    %t43 = %t41
+    %t46 = %t44
+    goto  L5
+L5:
+    j_2 = %t43
+    a_2 = %t46
+    %t4_0 = j_2<3
+    if %t4_0 goto L6 else goto L7
+L6:
+    %t5_0 = i_1==j_2
+    if %t5_0 goto L8 else goto L9
+L8:
+    %t6_0 = a_2+i_1
+    %t7_0 = %t6_0+j_2
+    a_5 = %t7_0
+    %t35 = a_5
+    %t37 = %t35
+    goto  L10
+L10:
+    a_6 = %t37
+    %t10_0 = j_2+1
+    j_3 = %t10_0
+    %t42 = j_3
+    %t45 = a_6
+    %t43 = %t42
+    %t46 = %t45
+    goto  L5
+L9:
+    %t8_0 = i_1>j_2
+    %t38 = a_2
+    %t40 = %t38
+    if %t8_0 goto L11 else goto L12
+L11:
+    %t9_0 = a_2-1
+    a_3 = %t9_0
+    %t39 = a_3
+    %t40 = %t39
+    goto  L12
+L12:
+    a_4 = %t40
+    %t36 = a_4
+    %t37 = %t36
+    goto  L10
+L7:
+    %t11_0 = i_1+1
+    i_2 = %t11_0
+    %t48 = i_2
+    %t51 = a_2
+    %t49 = %t48
+    %t52 = %t51
+    goto  L2
+L4:
+    ret a_1
+    goto  L1
+L1:
+""", result);
+    }
+
+    @Test
+    public void testSSA12() {
+        String src = """
+        func main()->Int {
+            var count = 0
+            var i = 0
+            var j = 0
+            while (i < 5) {
+                j = 0
+                while (j < 5) {
+                    if (i + j > 5)
+                        break
+                    if (i == j) {
+                        j = j + 1
+                        continue
+                    }
+                    count = count + 1
+                    j = j + 1
+                }
+                i = i + 1
+            }
+            return count
+        }
+""";
+        String result = compileSrc(src);
+        Assert.assertEquals("""
+func main
+Before SSA
+==========
+L0:
+    count = 0
+    i = 0
+    j = 0
+    goto  L2
+L2:
+    %t3 = i<5
+    if %t3 goto L3 else goto L4
+L3:
+    j = 0
+    goto  L5
+L5:
+    %t4 = j<5
+    if %t4 goto L6 else goto L7
+L6:
+    %t5 = i+j
+    %t6 = %t5>5
+    if %t6 goto L8 else goto L9
+L8:
+    goto  L7
+L7:
+    %t11 = i+1
+    i = %t11
+    goto  L2
+L9:
+    %t7 = i==j
+    if %t7 goto L10 else goto L11
+L10:
+    %t8 = j+1
+    j = %t8
+    goto  L5
+L11:
+    %t9 = count+1
+    count = %t9
+    %t10 = j+1
+    j = %t10
+    goto  L5
+L4:
+    ret count
+    goto  L1
+L1:
+After SSA
+=========
+L0:
+    count_0 = 0
+    i_0 = 0
+    j_0 = 0
+    goto  L2
+L2:
+    i_1 = phi(i_0, i_2)
+    count_1 = phi(count_0, count_2)
+    %t3_0 = i_1<5
+    if %t3_0 goto L3 else goto L4
+L3:
+    j_1 = 0
+    goto  L5
+L5:
+    j_2 = phi(j_1, j_4, j_3)
+    count_2 = phi(count_1, count_2, count_3)
+    %t4_0 = j_2<5
+    if %t4_0 goto L6 else goto L7
+L6:
+    %t5_0 = i_1+j_2
+    %t6_0 = %t5_0>5
+    if %t6_0 goto L8 else goto L9
+L8:
+    goto  L7
+L7:
+    %t11_0 = i_1+1
+    i_2 = %t11_0
+    goto  L2
+L9:
+    %t7_0 = i_1==j_2
+    if %t7_0 goto L10 else goto L11
+L10:
+    %t8_0 = j_2+1
+    j_4 = %t8_0
+    goto  L5
+L11:
+    %t9_0 = count_2+1
+    count_3 = %t9_0
+    %t10_0 = j_2+1
+    j_3 = %t10_0
+    goto  L5
+L4:
+    ret count_1
+    goto  L1
+L1:
+After converting from SSA to CSSA
+L0:
+    count_0 = 0
+    i_0 = 0
+    j_0 = 0
+    (%t41,%t44) = (i_0,count_0)
+    goto  L2
+L2:
+    %t43 = phi(%t41, %t42)
+    %t46 = phi(%t44, %t45)
+    (i_1,count_1) = (%t43,%t46)
+    %t3_0 = i_1<5
+    if %t3_0 goto L3 else goto L4
+L3:
+    j_1 = 0
+    (%t33,%t37) = (j_1,count_1)
+    goto  L5
+L5:
+    %t36 = phi(%t33, %t34, %t35)
+    %t40 = phi(%t37, %t38, %t39)
+    (j_2,count_2) = (%t36,%t40)
+    %t4_0 = j_2<5
+    if %t4_0 goto L6 else goto L7
+L6:
+    %t5_0 = i_1+j_2
+    %t6_0 = %t5_0>5
+    if %t6_0 goto L8 else goto L9
+L8:
+    goto  L7
+L7:
+    %t11_0 = i_1+1
+    i_2 = %t11_0
+    (%t42,%t45) = (i_2,count_2)
+    goto  L2
+L9:
+    %t7_0 = i_1==j_2
+    if %t7_0 goto L10 else goto L11
+L10:
+    %t8_0 = j_2+1
+    j_4 = %t8_0
+    (%t34,%t38) = (j_4,count_2)
+    goto  L5
+L11:
+    %t9_0 = count_2+1
+    count_3 = %t9_0
+    %t10_0 = j_2+1
+    j_3 = %t10_0
+    (%t35,%t39) = (j_3,count_3)
+    goto  L5
+L4:
+    ret count_1
+    goto  L1
+L1:
+After removing phis from CSSA
+L0:
+    count_0 = 0
+    i_0 = 0
+    j_0 = 0
+    (%t41,%t44) = (i_0,count_0)
+    %t43 = %t41
+    %t46 = %t44
+    goto  L2
+L2:
+    (i_1,count_1) = (%t43,%t46)
+    %t3_0 = i_1<5
+    if %t3_0 goto L3 else goto L4
+L3:
+    j_1 = 0
+    (%t33,%t37) = (j_1,count_1)
+    %t36 = %t33
+    %t40 = %t37
+    goto  L5
+L5:
+    (j_2,count_2) = (%t36,%t40)
+    %t4_0 = j_2<5
+    if %t4_0 goto L6 else goto L7
+L6:
+    %t5_0 = i_1+j_2
+    %t6_0 = %t5_0>5
+    if %t6_0 goto L8 else goto L9
+L8:
+    goto  L7
+L7:
+    %t11_0 = i_1+1
+    i_2 = %t11_0
+    (%t42,%t45) = (i_2,count_2)
+    %t43 = %t42
+    %t46 = %t45
+    goto  L2
+L9:
+    %t7_0 = i_1==j_2
+    if %t7_0 goto L10 else goto L11
+L10:
+    %t8_0 = j_2+1
+    j_4 = %t8_0
+    (%t34,%t38) = (j_4,count_2)
+    %t36 = %t34
+    %t40 = %t38
+    goto  L5
+L11:
+    %t9_0 = count_2+1
+    count_3 = %t9_0
+    %t10_0 = j_2+1
+    j_3 = %t10_0
+    (%t35,%t39) = (j_3,count_3)
+    %t36 = %t35
+    %t40 = %t39
+    goto  L5
+L4:
+    ret count_1
+    goto  L1
+L1:
+After exiting SSA
+=================
+L0:
+    count_0 = 0
+    i_0 = 0
+    j_0 = 0
+    %t41 = i_0
+    %t44 = count_0
+    %t43 = %t41
+    %t46 = %t44
+    goto  L2
+L2:
+    i_1 = %t43
+    count_1 = %t46
+    %t3_0 = i_1<5
+    if %t3_0 goto L3 else goto L4
+L3:
+    j_1 = 0
+    %t33 = j_1
+    %t37 = count_1
+    %t36 = %t33
+    %t40 = %t37
+    goto  L5
+L5:
+    j_2 = %t36
+    count_2 = %t40
+    %t4_0 = j_2<5
+    if %t4_0 goto L6 else goto L7
+L6:
+    %t5_0 = i_1+j_2
+    %t6_0 = %t5_0>5
+    if %t6_0 goto L8 else goto L9
+L8:
+    goto  L7
+L7:
+    %t11_0 = i_1+1
+    i_2 = %t11_0
+    %t42 = i_2
+    %t45 = count_2
+    %t43 = %t42
+    %t46 = %t45
+    goto  L2
+L9:
+    %t7_0 = i_1==j_2
+    if %t7_0 goto L10 else goto L11
+L10:
+    %t8_0 = j_2+1
+    j_4 = %t8_0
+    %t34 = j_4
+    %t38 = count_2
+    %t36 = %t34
+    %t40 = %t38
+    goto  L5
+L11:
+    %t9_0 = count_2+1
+    count_3 = %t9_0
+    %t10_0 = j_2+1
+    j_3 = %t10_0
+    %t35 = j_3
+    %t39 = count_3
+    %t36 = %t35
+    %t40 = %t39
+    goto  L5
+L4:
+    ret count_1
+    goto  L1
+L1:
+""", result);
+    }
+
+    @Test
+    public void testSSA13() {
+        String src = """
+        func main()->Int {
+            var outerSum = 0
+            var innerSum = 0
+            var i = 0
+            var j = 0
+            while (i < 4) {
+                j = 0
+                while (j < 4) {
+                    if ((i + j) % 2 == 0)
+                        innerSum = innerSum + j
+                    j = j + 1
+                }
+                outerSum = outerSum + innerSum
+                i = i + 1
+            }
+            return outerSum
+        }
+""";
+        String result = compileSrc(src);
+        Assert.assertEquals("""
+func main
+Before SSA
+==========
+L0:
+    outerSum = 0
+    innerSum = 0
+    i = 0
+    j = 0
+    goto  L2
+L2:
+    %t4 = i<4
+    if %t4 goto L3 else goto L4
+L3:
+    j = 0
+    goto  L5
+L5:
+    %t5 = j<4
+    if %t5 goto L6 else goto L7
+L6:
+    %t6 = i+j
+    %t7 = %t6%2
+    %t8 = %t7==0
+    if %t8 goto L8 else goto L9
+L8:
+    %t9 = innerSum+j
+    innerSum = %t9
+    goto  L9
+L9:
+    %t10 = j+1
+    j = %t10
+    goto  L5
+L7:
+    %t11 = outerSum+innerSum
+    outerSum = %t11
+    %t12 = i+1
+    i = %t12
+    goto  L2
+L4:
+    ret outerSum
+    goto  L1
+L1:
+After SSA
+=========
+L0:
+    outerSum_0 = 0
+    innerSum_0 = 0
+    i_0 = 0
+    j_0 = 0
+    goto  L2
+L2:
+    i_1 = phi(i_0, i_2)
+    innerSum_1 = phi(innerSum_0, innerSum_2)
+    outerSum_1 = phi(outerSum_0, outerSum_2)
+    %t4_0 = i_1<4
+    if %t4_0 goto L3 else goto L4
+L3:
+    j_1 = 0
+    goto  L5
+L5:
+    j_2 = phi(j_1, j_3)
+    innerSum_2 = phi(innerSum_1, innerSum_4)
+    %t5_0 = j_2<4
+    if %t5_0 goto L6 else goto L7
+L6:
+    %t6_0 = i_1+j_2
+    %t7_0 = %t6_0%2
+    %t8_0 = %t7_0==0
+    if %t8_0 goto L8 else goto L9
+L8:
+    %t9_0 = innerSum_2+j_2
+    innerSum_3 = %t9_0
+    goto  L9
+L9:
+    innerSum_4 = phi(innerSum_2, innerSum_3)
+    %t10_0 = j_2+1
+    j_3 = %t10_0
+    goto  L5
+L7:
+    %t11_0 = outerSum_1+innerSum_2
+    outerSum_2 = %t11_0
+    %t12_0 = i_1+1
+    i_2 = %t12_0
+    goto  L2
+L4:
+    ret outerSum_1
+    goto  L1
+L1:
+After converting from SSA to CSSA
+L0:
+    outerSum_0 = 0
+    innerSum_0 = 0
+    i_0 = 0
+    j_0 = 0
+    (%t46,%t49,%t52) = (i_0,innerSum_0,outerSum_0)
+    goto  L2
+L2:
+    %t48 = phi(%t46, %t47)
+    %t51 = phi(%t49, %t50)
+    %t54 = phi(%t52, %t53)
+    (i_1,innerSum_1,outerSum_1) = (%t48,%t51,%t54)
+    %t4_0 = i_1<4
+    if %t4_0 goto L3 else goto L4
+L3:
+    j_1 = 0
+    (%t40,%t43) = (j_1,innerSum_1)
+    goto  L5
+L5:
+    %t42 = phi(%t40, %t41)
+    %t45 = phi(%t43, %t44)
+    (j_2,innerSum_2) = (%t42,%t45)
+    %t5_0 = j_2<4
+    if %t5_0 goto L6 else goto L7
+L6:
+    %t6_0 = i_1+j_2
+    %t7_0 = %t6_0%2
+    %t8_0 = %t7_0==0
+    (%t37) = (innerSum_2)
+    if %t8_0 goto L8 else goto L9
+L8:
+    %t9_0 = innerSum_2+j_2
+    innerSum_3 = %t9_0
+    (%t38) = (innerSum_3)
+    goto  L9
+L9:
+    %t39 = phi(%t37, %t38)
+    (innerSum_4) = (%t39)
+    %t10_0 = j_2+1
+    j_3 = %t10_0
+    (%t41,%t44) = (j_3,innerSum_4)
+    goto  L5
+L7:
+    %t11_0 = outerSum_1+innerSum_2
+    outerSum_2 = %t11_0
+    %t12_0 = i_1+1
+    i_2 = %t12_0
+    (%t47,%t50,%t53) = (i_2,innerSum_2,outerSum_2)
+    goto  L2
+L4:
+    ret outerSum_1
+    goto  L1
+L1:
+After removing phis from CSSA
+L0:
+    outerSum_0 = 0
+    innerSum_0 = 0
+    i_0 = 0
+    j_0 = 0
+    (%t46,%t49,%t52) = (i_0,innerSum_0,outerSum_0)
+    %t48 = %t46
+    %t51 = %t49
+    %t54 = %t52
+    goto  L2
+L2:
+    (i_1,innerSum_1,outerSum_1) = (%t48,%t51,%t54)
+    %t4_0 = i_1<4
+    if %t4_0 goto L3 else goto L4
+L3:
+    j_1 = 0
+    (%t40,%t43) = (j_1,innerSum_1)
+    %t42 = %t40
+    %t45 = %t43
+    goto  L5
+L5:
+    (j_2,innerSum_2) = (%t42,%t45)
+    %t5_0 = j_2<4
+    if %t5_0 goto L6 else goto L7
+L6:
+    %t6_0 = i_1+j_2
+    %t7_0 = %t6_0%2
+    %t8_0 = %t7_0==0
+    (%t37) = (innerSum_2)
+    %t39 = %t37
+    if %t8_0 goto L8 else goto L9
+L8:
+    %t9_0 = innerSum_2+j_2
+    innerSum_3 = %t9_0
+    (%t38) = (innerSum_3)
+    %t39 = %t38
+    goto  L9
+L9:
+    (innerSum_4) = (%t39)
+    %t10_0 = j_2+1
+    j_3 = %t10_0
+    (%t41,%t44) = (j_3,innerSum_4)
+    %t42 = %t41
+    %t45 = %t44
+    goto  L5
+L7:
+    %t11_0 = outerSum_1+innerSum_2
+    outerSum_2 = %t11_0
+    %t12_0 = i_1+1
+    i_2 = %t12_0
+    (%t47,%t50,%t53) = (i_2,innerSum_2,outerSum_2)
+    %t48 = %t47
+    %t51 = %t50
+    %t54 = %t53
+    goto  L2
+L4:
+    ret outerSum_1
+    goto  L1
+L1:
+After exiting SSA
+=================
+L0:
+    outerSum_0 = 0
+    innerSum_0 = 0
+    i_0 = 0
+    j_0 = 0
+    %t46 = i_0
+    %t49 = innerSum_0
+    %t52 = outerSum_0
+    %t48 = %t46
+    %t51 = %t49
+    %t54 = %t52
+    goto  L2
+L2:
+    i_1 = %t48
+    innerSum_1 = %t51
+    outerSum_1 = %t54
+    %t4_0 = i_1<4
+    if %t4_0 goto L3 else goto L4
+L3:
+    j_1 = 0
+    %t40 = j_1
+    %t43 = innerSum_1
+    %t42 = %t40
+    %t45 = %t43
+    goto  L5
+L5:
+    j_2 = %t42
+    innerSum_2 = %t45
+    %t5_0 = j_2<4
+    if %t5_0 goto L6 else goto L7
+L6:
+    %t6_0 = i_1+j_2
+    %t7_0 = %t6_0%2
+    %t8_0 = %t7_0==0
+    %t37 = innerSum_2
+    %t39 = %t37
+    if %t8_0 goto L8 else goto L9
+L8:
+    %t9_0 = innerSum_2+j_2
+    innerSum_3 = %t9_0
+    %t38 = innerSum_3
+    %t39 = %t38
+    goto  L9
+L9:
+    innerSum_4 = %t39
+    %t10_0 = j_2+1
+    j_3 = %t10_0
+    %t41 = j_3
+    %t44 = innerSum_4
+    %t42 = %t41
+    %t45 = %t44
+    goto  L5
+L7:
+    %t11_0 = outerSum_1+innerSum_2
+    outerSum_2 = %t11_0
+    %t12_0 = i_1+1
+    i_2 = %t12_0
+    %t47 = i_2
+    %t50 = innerSum_2
+    %t53 = outerSum_2
+    %t48 = %t47
+    %t51 = %t50
+    %t54 = %t53
+    goto  L2
+L4:
+    ret outerSum_1
+    goto  L1
+L1:
+""", result);
+    }
+
+    @Test
+    public void testSSA14() {
+        String src = """
+                func foo()->Int 
+                {
+                    return 1 && 2
+                }
+""";
+        String result = compileSrc(src);
+        Assert.assertEquals("""
+func foo
+Before SSA
+==========
+L0:
+    if 1 goto L2 else goto L3
+L2:
+    %t0 = 2
+    goto  L4
+L4:
+    ret %t0
+    goto  L1
+L1:
+L3:
+    %t0 = 0
+    goto  L4
+After SSA
+=========
+L0:
+    if 1 goto L2 else goto L3
+L2:
+    %t0_1 = 2
+    goto  L4
+L4:
+    %t0_2 = phi(%t0_1, %t0_0)
+    ret %t0_2
+    goto  L1
+L1:
+L3:
+    %t0_0 = 0
+    goto  L4
+After converting from SSA to CSSA
+L0:
+    if 1 goto L2 else goto L3
+L2:
+    %t0_1 = 2
+    (%t4) = (%t0_1)
+    goto  L4
+L4:
+    %t6 = phi(%t4, %t5)
+    (%t0_2) = (%t6)
+    ret %t0_2
+    goto  L1
+L1:
+L3:
+    %t0_0 = 0
+    (%t5) = (%t0_0)
+    goto  L4
+After removing phis from CSSA
+L0:
+    if 1 goto L2 else goto L3
+L2:
+    %t0_1 = 2
+    (%t4) = (%t0_1)
+    %t6 = %t4
+    goto  L4
+L4:
+    (%t0_2) = (%t6)
+    ret %t0_2
+    goto  L1
+L1:
+L3:
+    %t0_0 = 0
+    (%t5) = (%t0_0)
+    %t6 = %t5
+    goto  L4
+After exiting SSA
+=================
+L0:
+    if 1 goto L2 else goto L3
+L2:
+    %t0_1 = 2
+    %t4 = %t0_1
+    %t6 = %t4
+    goto  L4
+L4:
+    %t0_2 = %t6
+    ret %t0_2
+    goto  L1
+L1:
+L3:
+    %t0_0 = 0
+    %t5 = %t0_0
+    %t6 = %t5
+    goto  L4
+""", result);
+    }
+
+    @Test
+    public void testSSA15() {
+        String src = """
+                struct Foo
+                {
+                    var i: Int
+                }
+                func foo()->Int
+                {
+                    var f = new [Foo?] { new Foo{i = 1}, null }
+                    return null == f[1] && 1 == f[0].i
+                }
+""";
+        String result = compileSrc(src);
+        Assert.assertEquals("""
+func foo
+Before SSA
+==========
+L0:
+    %t1 = New([Foo?], len=2)
+    %t2 = New(Foo)
+    %t2.i = 1
+    %t1[0] = %t2
+    %t1[1] = null
+    f = %t1
+    %t3 = f[1]
+    %t4 = null==%t3
+    if %t4 goto L2 else goto L3
+L2:
+    %t5 = f[0]
+    %t6 = %t5.i
+    %t7 = 1==%t6
+    goto  L4
+L4:
+    ret %t7
+    goto  L1
+L1:
+L3:
+    %t7 = 0
+    goto  L4
+After SSA
+=========
+L0:
+    %t1_0 = New([Foo?], len=2)
+    %t2_0 = New(Foo)
+    %t2_0.i = 1
+    %t1_0[0] = %t2_0
+    %t1_0[1] = null
+    f_0 = %t1_0
+    %t3_0 = f_0[1]
+    %t4_0 = null==%t3_0
+    if %t4_0 goto L2 else goto L3
+L2:
+    %t5_0 = f_0[0]
+    %t6_0 = %t5_0.i
+    %t7_1 = 1==%t6_0
+    goto  L4
+L4:
+    %t7_2 = phi(%t7_1, %t7_0)
+    ret %t7_2
+    goto  L1
+L1:
+L3:
+    %t7_0 = 0
+    goto  L4
+After converting from SSA to CSSA
+L0:
+    %t1_0 = New([Foo?], len=2)
+    %t2_0 = New(Foo)
+    %t2_0.i = 1
+    %t1_0[0] = %t2_0
+    %t1_0[1] = null
+    f_0 = %t1_0
+    %t3_0 = f_0[1]
+    %t4_0 = null==%t3_0
+    if %t4_0 goto L2 else goto L3
+L2:
+    %t5_0 = f_0[0]
+    %t6_0 = %t5_0.i
+    %t7_1 = 1==%t6_0
+    (%t18) = (%t7_1)
+    goto  L4
+L4:
+    %t20 = phi(%t18, %t19)
+    (%t7_2) = (%t20)
+    ret %t7_2
+    goto  L1
+L1:
+L3:
+    %t7_0 = 0
+    (%t19) = (%t7_0)
+    goto  L4
+After removing phis from CSSA
+L0:
+    %t1_0 = New([Foo?], len=2)
+    %t2_0 = New(Foo)
+    %t2_0.i = 1
+    %t1_0[0] = %t2_0
+    %t1_0[1] = null
+    f_0 = %t1_0
+    %t3_0 = f_0[1]
+    %t4_0 = null==%t3_0
+    if %t4_0 goto L2 else goto L3
+L2:
+    %t5_0 = f_0[0]
+    %t6_0 = %t5_0.i
+    %t7_1 = 1==%t6_0
+    (%t18) = (%t7_1)
+    %t20 = %t18
+    goto  L4
+L4:
+    (%t7_2) = (%t20)
+    ret %t7_2
+    goto  L1
+L1:
+L3:
+    %t7_0 = 0
+    (%t19) = (%t7_0)
+    %t20 = %t19
+    goto  L4
+After exiting SSA
+=================
+L0:
+    %t1_0 = New([Foo?], len=2)
+    %t2_0 = New(Foo)
+    %t2_0.i = 1
+    %t1_0[0] = %t2_0
+    %t1_0[1] = null
+    f_0 = %t1_0
+    %t3_0 = f_0[1]
+    %t4_0 = null==%t3_0
+    if %t4_0 goto L2 else goto L3
+L2:
+    %t5_0 = f_0[0]
+    %t6_0 = %t5_0.i
+    %t7_1 = 1==%t6_0
+    %t18 = %t7_1
+    %t20 = %t18
+    goto  L4
+L4:
+    %t7_2 = %t20
+    ret %t7_2
+    goto  L1
+L1:
+L3:
+    %t7_0 = 0
+    %t19 = %t7_0
+    %t20 = %t19
+    goto  L4
+""", result);
+    }
+
+
+    @Test
+    public void testSSA17() {
+        String src = """
+func merge(begin: Int, middle: Int, end: Int)
+{
+    if (begin < end) {
+        var cond = 0
+        if (begin < middle) {
+            if (begin >= end)          cond = 1;
+        }
+        if (cond)
+        {
+            cond = 0
+        }
+    }
+}
+""";
+        String result = compileSrc(src);
+        Assert.assertEquals("""
+func merge
+Before SSA
+==========
+L0:
+    arg begin
+    arg middle
+    arg end
+    %t4 = begin<end
+    if %t4 goto L2 else goto L3
+L2:
+    cond = 0
+    %t5 = begin<middle
+    if %t5 goto L4 else goto L5
+L4:
+    %t6 = begin>=end
+    if %t6 goto L6 else goto L7
+L6:
+    cond = 1
+    goto  L7
+L7:
+    goto  L5
+L5:
+    if cond goto L8 else goto L9
+L8:
+    cond = 0
+    goto  L9
+L9:
+    goto  L3
+L3:
+    goto  L1
+L1:
+After SSA
+=========
+L0:
+    arg begin_0
+    arg middle_0
+    arg end_0
+    %t4_0 = begin_0<end_0
+    if %t4_0 goto L2 else goto L3
+L2:
+    cond_0 = 0
+    %t5_0 = begin_0<middle_0
+    if %t5_0 goto L4 else goto L5
+L4:
+    %t6_0 = begin_0>=end_0
+    if %t6_0 goto L6 else goto L7
+L6:
+    cond_1 = 1
+    goto  L7
+L7:
+    cond_2 = phi(cond_0, cond_1)
+    goto  L5
+L5:
+    cond_3 = phi(cond_0, cond_2)
+    if cond_3 goto L8 else goto L9
+L8:
+    cond_4 = 0
+    goto  L9
+L9:
+    goto  L3
+L3:
+    goto  L1
+L1:
+After converting from SSA to CSSA
+L0:
+    arg begin_0
+    arg middle_0
+    arg end_0
+    %t4_0 = begin_0<end_0
+    if %t4_0 goto L2 else goto L3
+L2:
+    cond_0 = 0
+    %t5_0 = begin_0<middle_0
+    (%t18) = (cond_0)
+    if %t5_0 goto L4 else goto L5
+L4:
+    %t6_0 = begin_0>=end_0
+    (%t21) = (cond_0)
+    if %t6_0 goto L6 else goto L7
+L6:
+    cond_1 = 1
+    (%t22) = (cond_1)
+    goto  L7
+L7:
+    %t23 = phi(%t21, %t22)
+    (cond_2) = (%t23)
+    (%t19) = (cond_2)
+    goto  L5
+L5:
+    %t20 = phi(%t18, %t19)
+    (cond_3) = (%t20)
+    if cond_3 goto L8 else goto L9
+L8:
+    cond_4 = 0
+    goto  L9
+L9:
+    goto  L3
+L3:
+    goto  L1
+L1:
+After removing phis from CSSA
+L0:
+    arg begin_0
+    arg middle_0
+    arg end_0
+    %t4_0 = begin_0<end_0
+    if %t4_0 goto L2 else goto L3
+L2:
+    cond_0 = 0
+    %t5_0 = begin_0<middle_0
+    (%t18) = (cond_0)
+    %t20 = %t18
+    if %t5_0 goto L4 else goto L5
+L4:
+    %t6_0 = begin_0>=end_0
+    (%t21) = (cond_0)
+    %t23 = %t21
+    if %t6_0 goto L6 else goto L7
+L6:
+    cond_1 = 1
+    (%t22) = (cond_1)
+    %t23 = %t22
+    goto  L7
+L7:
+    (cond_2) = (%t23)
+    (%t19) = (cond_2)
+    %t20 = %t19
+    goto  L5
+L5:
+    (cond_3) = (%t20)
+    if cond_3 goto L8 else goto L9
+L8:
+    cond_4 = 0
+    goto  L9
+L9:
+    goto  L3
+L3:
+    goto  L1
+L1:
+After exiting SSA
+=================
+L0:
+    arg begin_0
+    arg middle_0
+    arg end_0
+    %t4_0 = begin_0<end_0
+    if %t4_0 goto L2 else goto L3
+L2:
+    cond_0 = 0
+    %t5_0 = begin_0<middle_0
+    %t18 = cond_0
+    %t20 = %t18
+    if %t5_0 goto L4 else goto L5
+L4:
+    %t6_0 = begin_0>=end_0
+    %t21 = cond_0
+    %t23 = %t21
+    if %t6_0 goto L6 else goto L7
+L6:
+    cond_1 = 1
+    %t22 = cond_1
+    %t23 = %t22
+    goto  L7
+L7:
+    cond_2 = %t23
+    %t19 = cond_2
+    %t20 = %t19
+    goto  L5
+L5:
+    cond_3 = %t20
+    if cond_3 goto L8 else goto L9
+L8:
+    cond_4 = 0
+    goto  L9
+L9:
+    goto  L3
+L3:
+    goto  L1
+L1:
+""", result);
+    }
+
+    @Test
+    public void testSSA18()
+    {
+        String src = """
+                func foo(len: Int, val: Int, x: Int, y: Int)->[Int] {
+                    if (x > y) {
+                        len=len+x
+                        val=val+x
+                    }
+                    return new [Int]{len=len,value=val} 
+                }
+                """;
+        String result = compileSrc(src);
+        Assert.assertEquals("""
+func foo
+Before SSA
+==========
+L0:
+    arg len
+    arg val
+    arg x
+    arg y
+    %t4 = x>y
+    if %t4 goto L2 else goto L3
+L2:
+    %t5 = len+x
+    len = %t5
+    %t6 = val+x
+    val = %t6
+    goto  L3
+L3:
+    %t7 = New([Int], len=len, initValue=val)
+    ret %t7
+    goto  L1
+L1:
+After SSA
+=========
+L0:
+    arg len_0
+    arg val_0
+    arg x_0
+    arg y_0
+    %t4_0 = x_0>y_0
+    if %t4_0 goto L2 else goto L3
+L2:
+    %t5_0 = len_0+x_0
+    len_1 = %t5_0
+    %t6_0 = val_0+x_0
+    val_1 = %t6_0
+    goto  L3
+L3:
+    val_2 = phi(val_0, val_1)
+    len_2 = phi(len_0, len_1)
+    %t7_0 = New([Int], len=len_2, initValue=val_2)
+    ret %t7_0
+    goto  L1
+L1:
+After converting from SSA to CSSA
+L0:
+    arg len_0
+    arg val_0
+    arg x_0
+    arg y_0
+    %t4_0 = x_0>y_0
+    (%t20,%t23) = (val_0,len_0)
+    if %t4_0 goto L2 else goto L3
+L2:
+    %t5_0 = len_0+x_0
+    len_1 = %t5_0
+    %t6_0 = val_0+x_0
+    val_1 = %t6_0
+    (%t21,%t24) = (val_1,len_1)
+    goto  L3
+L3:
+    %t22 = phi(%t20, %t21)
+    %t25 = phi(%t23, %t24)
+    (val_2,len_2) = (%t22,%t25)
+    %t7_0 = New([Int], len=len_2, initValue=val_2)
+    ret %t7_0
+    goto  L1
+L1:
+After removing phis from CSSA
+L0:
+    arg len_0
+    arg val_0
+    arg x_0
+    arg y_0
+    %t4_0 = x_0>y_0
+    (%t20,%t23) = (val_0,len_0)
+    %t22 = %t20
+    %t25 = %t23
+    if %t4_0 goto L2 else goto L3
+L2:
+    %t5_0 = len_0+x_0
+    len_1 = %t5_0
+    %t6_0 = val_0+x_0
+    val_1 = %t6_0
+    (%t21,%t24) = (val_1,len_1)
+    %t22 = %t21
+    %t25 = %t24
+    goto  L3
+L3:
+    (val_2,len_2) = (%t22,%t25)
+    %t7_0 = New([Int], len=len_2, initValue=val_2)
+    ret %t7_0
+    goto  L1
+L1:
+After exiting SSA
+=================
+L0:
+    arg len_0
+    arg val_0
+    arg x_0
+    arg y_0
+    %t4_0 = x_0>y_0
+    %t20 = val_0
+    %t23 = len_0
+    %t22 = %t20
+    %t25 = %t23
+    if %t4_0 goto L2 else goto L3
+L2:
+    %t5_0 = len_0+x_0
+    len_1 = %t5_0
+    %t6_0 = val_0+x_0
+    val_1 = %t6_0
+    %t21 = val_1
+    %t24 = len_1
+    %t22 = %t21
+    %t25 = %t24
+    goto  L3
+L3:
+    val_2 = %t22
+    len_2 = %t25
+    %t7_0 = New([Int], len=len_2, initValue=val_2)
+    ret %t7_0
+    goto  L1
+L1:
+""", result);
+    }
+
+    @Test
+    public void testSSA19()
+    {
+        String src = """
+func bug(N: Int)
+{
+    var p=2
+    while( p < N ) {
+        if (p) {
+            p = p + 1
+        }
+    }
+    while ( p < N ) {
+        p = p + 1
+    }
+}
+                """;
+        String result = compileSrc(src);
+        Assert.assertEquals("""
+func bug
+Before SSA
+==========
+L0:
+    arg N
+    p = 2
+    goto  L2
+L2:
+    %t2 = p<N
+    if %t2 goto L3 else goto L4
+L3:
+    if p goto L5 else goto L6
+L5:
+    %t3 = p+1
+    p = %t3
+    goto  L6
+L6:
+    goto  L2
+L4:
+    goto  L7
+L7:
+    %t4 = p<N
+    if %t4 goto L8 else goto L9
+L8:
+    %t5 = p+1
+    p = %t5
+    goto  L7
+L9:
+    goto  L1
+L1:
+After SSA
+=========
+L0:
+    arg N_0
+    p_0 = 2
+    goto  L2
+L2:
+    p_1 = phi(p_0, p_5)
+    %t2_0 = p_1<N_0
+    if %t2_0 goto L3 else goto L4
+L3:
+    if p_1 goto L5 else goto L6
+L5:
+    %t3_0 = p_1+1
+    p_4 = %t3_0
+    goto  L6
+L6:
+    p_5 = phi(p_1, p_4)
+    goto  L2
+L4:
+    goto  L7
+L7:
+    p_2 = phi(p_1, p_3)
+    %t4_0 = p_2<N_0
+    if %t4_0 goto L8 else goto L9
+L8:
+    %t5_0 = p_2+1
+    p_3 = %t5_0
+    goto  L7
+L9:
+    goto  L1
+L1:
+After converting from SSA to CSSA
+L0:
+    arg N_0
+    p_0 = 2
+    (%t23) = (p_0)
+    goto  L2
+L2:
+    %t25 = phi(%t23, %t24)
+    (p_1) = (%t25)
+    %t2_0 = p_1<N_0
+    if %t2_0 goto L3 else goto L4
+L3:
+    (%t17) = (p_1)
+    if p_1 goto L5 else goto L6
+L5:
+    %t3_0 = p_1+1
+    p_4 = %t3_0
+    (%t18) = (p_4)
+    goto  L6
+L6:
+    %t19 = phi(%t17, %t18)
+    (p_5) = (%t19)
+    (%t24) = (p_5)
+    goto  L2
+L4:
+    (%t20) = (p_1)
+    goto  L7
+L7:
+    %t22 = phi(%t20, %t21)
+    (p_2) = (%t22)
+    %t4_0 = p_2<N_0
+    if %t4_0 goto L8 else goto L9
+L8:
+    %t5_0 = p_2+1
+    p_3 = %t5_0
+    (%t21) = (p_3)
+    goto  L7
+L9:
+    goto  L1
+L1:
+After removing phis from CSSA
+L0:
+    arg N_0
+    p_0 = 2
+    (%t23) = (p_0)
+    %t25 = %t23
+    goto  L2
+L2:
+    (p_1) = (%t25)
+    %t2_0 = p_1<N_0
+    if %t2_0 goto L3 else goto L4
+L3:
+    (%t17) = (p_1)
+    %t19 = %t17
+    if p_1 goto L5 else goto L6
+L5:
+    %t3_0 = p_1+1
+    p_4 = %t3_0
+    (%t18) = (p_4)
+    %t19 = %t18
+    goto  L6
+L6:
+    (p_5) = (%t19)
+    (%t24) = (p_5)
+    %t25 = %t24
+    goto  L2
+L4:
+    (%t20) = (p_1)
+    %t22 = %t20
+    goto  L7
+L7:
+    (p_2) = (%t22)
+    %t4_0 = p_2<N_0
+    if %t4_0 goto L8 else goto L9
+L8:
+    %t5_0 = p_2+1
+    p_3 = %t5_0
+    (%t21) = (p_3)
+    %t22 = %t21
+    goto  L7
+L9:
+    goto  L1
+L1:
+After exiting SSA
+=================
+L0:
+    arg N_0
+    p_0 = 2
+    %t23 = p_0
+    %t25 = %t23
+    goto  L2
+L2:
+    p_1 = %t25
+    %t2_0 = p_1<N_0
+    if %t2_0 goto L3 else goto L4
+L3:
+    %t17 = p_1
+    %t19 = %t17
+    if p_1 goto L5 else goto L6
+L5:
+    %t3_0 = p_1+1
+    p_4 = %t3_0
+    %t18 = p_4
+    %t19 = %t18
+    goto  L6
+L6:
+    p_5 = %t19
+    %t24 = p_5
+    %t25 = %t24
+    goto  L2
+L4:
+    %t20 = p_1
+    %t22 = %t20
+    goto  L7
+L7:
+    p_2 = %t22
+    %t4_0 = p_2<N_0
+    if %t4_0 goto L8 else goto L9
+L8:
+    %t5_0 = p_2+1
+    p_3 = %t5_0
+    %t21 = p_3
+    %t22 = %t21
+    goto  L7
+L9:
+    goto  L1
+L1:
+""", result);
+    }
+
+    @Test
+    public void testSSA20()
+    {
+        String src = """
+func sieve(N: Int)->[Int]
+{
+    // The main Sieve array
+    var ary = new [Int]{len=N,value=0}
+    // The primes less than N
+    var primes = new [Int]{len=N/2,value=0}
+    // Number of primes so far, searching at index p
+    var nprimes = 0
+    var p=2
+    // Find primes while p^2 < N
+    while( p*p < N ) {
+        // skip marked non-primes
+        while( ary[p] ) {
+            p = p + 1
+        }
+        // p is now a prime
+        primes[nprimes] = p
+        nprimes = nprimes+1
+        // Mark out the rest non-primes
+        var i = p + p
+        while( i < N ) {
+            ary[i] = 1
+            i = i + p
+        }
+        p = p + 1
+    }
+
+    // Now just collect the remaining primes, no more marking
+    while ( p < N ) {
+        if( !ary[p] ) {
+            primes[nprimes] = p
+            nprimes = nprimes + 1
+        }
+        p = p + 1
+    }
+
+    // Copy/shrink the result array
+    var rez = new [Int]{len=nprimes,value=0}
+    var j = 0
+    while( j < nprimes ) {
+        rez[j] = primes[j]
+        j = j + 1
+    }
+    return rez
+}
+func eq(a: [Int], b: [Int], n: Int)->Int
+{
+    var result = 1
+    var i = 0
+    while (i < n)
+    {
+        if (a[i] != b[i])
+        {
+            result = 0
+            break
+        }
+        i = i + 1
+    }
+    return result
+}
+
+func main()->Int
+{
+    var rez = sieve(20)
+    var expected = new [Int]{2,3,5,7,11,13,17,19}
+    return eq(rez,expected,8)
+}
+""";
+        String result = compileSrc(src);
+        Assert.assertEquals("""
+func sieve
+Before SSA
+==========
+L0:
+    arg N
+    %t8 = New([Int], len=N, initValue=0)
+    ary = %t8
+    %t10 = N/2
+    %t9 = New([Int], len=%t10, initValue=0)
+    primes = %t9
+    nprimes = 0
+    p = 2
+    goto  L2
+L2:
+    %t11 = p*p
+    %t12 = %t11<N
+    if %t12 goto L3 else goto L4
+L3:
+    goto  L5
+L5:
+    %t13 = ary[p]
+    if %t13 goto L6 else goto L7
+L6:
+    %t14 = p+1
+    p = %t14
+    goto  L5
+L7:
+    primes[nprimes] = p
+    %t15 = nprimes+1
+    nprimes = %t15
+    %t16 = p+p
+    i = %t16
+    goto  L8
+L8:
+    %t17 = i<N
+    if %t17 goto L9 else goto L10
+L9:
+    ary[i] = 1
+    %t18 = i+p
+    i = %t18
+    goto  L8
+L10:
+    %t19 = p+1
+    p = %t19
+    goto  L2
+L4:
+    goto  L11
+L11:
+    %t20 = p<N
+    if %t20 goto L12 else goto L13
+L12:
+    %t21 = ary[p]
+    %t22 = !%t21
+    if %t22 goto L14 else goto L15
+L14:
+    primes[nprimes] = p
+    %t23 = nprimes+1
+    nprimes = %t23
+    goto  L15
+L15:
+    %t24 = p+1
+    p = %t24
+    goto  L11
+L13:
+    %t25 = New([Int], len=nprimes, initValue=0)
+    rez = %t25
+    j = 0
+    goto  L16
+L16:
+    %t26 = j<nprimes
+    if %t26 goto L17 else goto L18
+L17:
+    %t27 = primes[j]
+    rez[j] = %t27
+    %t28 = j+1
+    j = %t28
+    goto  L16
+L18:
+    ret rez
+    goto  L1
+L1:
+After SSA
+=========
+L0:
+    arg N_0
+    %t8_0 = New([Int], len=N_0, initValue=0)
+    ary_0 = %t8_0
+    %t10_0 = N_0/2
+    %t9_0 = New([Int], len=%t10_0, initValue=0)
+    primes_0 = %t9_0
+    nprimes_0 = 0
+    p_0 = 2
+    goto  L2
+L2:
+    p_1 = phi(p_0, p_5)
+    nprimes_1 = phi(nprimes_0, nprimes_5)
+    %t11_0 = p_1*p_1
+    %t12_0 = %t11_0<N_0
+    if %t12_0 goto L3 else goto L4
+L3:
+    goto  L5
+L5:
+    p_4 = phi(p_1, p_6)
+    %t13_0 = ary_0[p_4]
+    if %t13_0 goto L6 else goto L7
+L6:
+    %t14_0 = p_4+1
+    p_6 = %t14_0
+    goto  L5
+L7:
+    primes_0[nprimes_1] = p_4
+    %t15_0 = nprimes_1+1
+    nprimes_5 = %t15_0
+    %t16_0 = p_4+p_4
+    i_0 = %t16_0
+    goto  L8
+L8:
+    i_1 = phi(i_0, i_2)
+    %t17_0 = i_1<N_0
+    if %t17_0 goto L9 else goto L10
+L9:
+    ary_0[i_1] = 1
+    %t18_0 = i_1+p_4
+    i_2 = %t18_0
+    goto  L8
+L10:
+    %t19_0 = p_4+1
+    p_5 = %t19_0
+    goto  L2
+L4:
+    goto  L11
+L11:
+    p_2 = phi(p_1, p_3)
+    nprimes_2 = phi(nprimes_1, nprimes_4)
+    %t20_0 = p_2<N_0
+    if %t20_0 goto L12 else goto L13
+L12:
+    %t21_0 = ary_0[p_2]
+    %t22_0 = !%t21_0
+    if %t22_0 goto L14 else goto L15
+L14:
+    primes_0[nprimes_2] = p_2
+    %t23_0 = nprimes_2+1
+    nprimes_3 = %t23_0
+    goto  L15
+L15:
+    nprimes_4 = phi(nprimes_2, nprimes_3)
+    %t24_0 = p_2+1
+    p_3 = %t24_0
+    goto  L11
+L13:
+    %t25_0 = New([Int], len=nprimes_2, initValue=0)
+    rez_0 = %t25_0
+    j_0 = 0
+    goto  L16
+L16:
+    j_1 = phi(j_0, j_2)
+    %t26_0 = j_1<nprimes_2
+    if %t26_0 goto L17 else goto L18
+L17:
+    %t27_0 = primes_0[j_1]
+    rez_0[j_1] = %t27_0
+    %t28_0 = j_1+1
+    j_2 = %t28_0
+    goto  L16
+L18:
+    ret rez_0
+    goto  L1
+L1:
+After converting from SSA to CSSA
+L0:
+    arg N_0
+    %t8_0 = New([Int], len=N_0, initValue=0)
+    ary_0 = %t8_0
+    %t10_0 = N_0/2
+    %t9_0 = New([Int], len=%t10_0, initValue=0)
+    primes_0 = %t9_0
+    nprimes_0 = 0
+    p_0 = 2
+    (%t91,%t94) = (p_0,nprimes_0)
+    goto  L2
+L2:
+    %t93 = phi(%t91, %t92)
+    %t96 = phi(%t94, %t95)
+    (p_1,nprimes_1) = (%t93,%t96)
+    %t11_0 = p_1*p_1
+    %t12_0 = %t11_0<N_0
+    if %t12_0 goto L3 else goto L4
+L3:
+    (%t76) = (p_1)
+    goto  L5
+L5:
+    %t78 = phi(%t76, %t77)
+    (p_4) = (%t78)
+    %t13_0 = ary_0[p_4]
+    if %t13_0 goto L6 else goto L7
+L6:
+    %t14_0 = p_4+1
+    p_6 = %t14_0
+    (%t77) = (p_6)
+    goto  L5
+L7:
+    primes_0[nprimes_1] = p_4
+    %t15_0 = nprimes_1+1
+    nprimes_5 = %t15_0
+    %t16_0 = p_4+p_4
+    i_0 = %t16_0
+    (%t73) = (i_0)
+    goto  L8
+L8:
+    %t75 = phi(%t73, %t74)
+    (i_1) = (%t75)
+    %t17_0 = i_1<N_0
+    if %t17_0 goto L9 else goto L10
+L9:
+    ary_0[i_1] = 1
+    %t18_0 = i_1+p_4
+    i_2 = %t18_0
+    (%t74) = (i_2)
+    goto  L8
+L10:
+    %t19_0 = p_4+1
+    p_5 = %t19_0
+    (%t92,%t95) = (p_5,nprimes_5)
+    goto  L2
+L4:
+    (%t85,%t88) = (p_1,nprimes_1)
+    goto  L11
+L11:
+    %t87 = phi(%t85, %t86)
+    %t90 = phi(%t88, %t89)
+    (p_2,nprimes_2) = (%t87,%t90)
+    %t20_0 = p_2<N_0
+    if %t20_0 goto L12 else goto L13
+L12:
+    %t21_0 = ary_0[p_2]
+    %t22_0 = !%t21_0
+    (%t79) = (nprimes_2)
+    if %t22_0 goto L14 else goto L15
+L14:
+    primes_0[nprimes_2] = p_2
+    %t23_0 = nprimes_2+1
+    nprimes_3 = %t23_0
+    (%t80) = (nprimes_3)
+    goto  L15
+L15:
+    %t81 = phi(%t79, %t80)
+    (nprimes_4) = (%t81)
+    %t24_0 = p_2+1
+    p_3 = %t24_0
+    (%t86,%t89) = (p_3,nprimes_4)
+    goto  L11
+L13:
+    %t25_0 = New([Int], len=nprimes_2, initValue=0)
+    rez_0 = %t25_0
+    j_0 = 0
+    (%t82) = (j_0)
+    goto  L16
+L16:
+    %t84 = phi(%t82, %t83)
+    (j_1) = (%t84)
+    %t26_0 = j_1<nprimes_2
+    if %t26_0 goto L17 else goto L18
+L17:
+    %t27_0 = primes_0[j_1]
+    rez_0[j_1] = %t27_0
+    %t28_0 = j_1+1
+    j_2 = %t28_0
+    (%t83) = (j_2)
+    goto  L16
+L18:
+    ret rez_0
+    goto  L1
+L1:
+After removing phis from CSSA
+L0:
+    arg N_0
+    %t8_0 = New([Int], len=N_0, initValue=0)
+    ary_0 = %t8_0
+    %t10_0 = N_0/2
+    %t9_0 = New([Int], len=%t10_0, initValue=0)
+    primes_0 = %t9_0
+    nprimes_0 = 0
+    p_0 = 2
+    (%t91,%t94) = (p_0,nprimes_0)
+    %t93 = %t91
+    %t96 = %t94
+    goto  L2
+L2:
+    (p_1,nprimes_1) = (%t93,%t96)
+    %t11_0 = p_1*p_1
+    %t12_0 = %t11_0<N_0
+    if %t12_0 goto L3 else goto L4
+L3:
+    (%t76) = (p_1)
+    %t78 = %t76
+    goto  L5
+L5:
+    (p_4) = (%t78)
+    %t13_0 = ary_0[p_4]
+    if %t13_0 goto L6 else goto L7
+L6:
+    %t14_0 = p_4+1
+    p_6 = %t14_0
+    (%t77) = (p_6)
+    %t78 = %t77
+    goto  L5
+L7:
+    primes_0[nprimes_1] = p_4
+    %t15_0 = nprimes_1+1
+    nprimes_5 = %t15_0
+    %t16_0 = p_4+p_4
+    i_0 = %t16_0
+    (%t73) = (i_0)
+    %t75 = %t73
+    goto  L8
+L8:
+    (i_1) = (%t75)
+    %t17_0 = i_1<N_0
+    if %t17_0 goto L9 else goto L10
+L9:
+    ary_0[i_1] = 1
+    %t18_0 = i_1+p_4
+    i_2 = %t18_0
+    (%t74) = (i_2)
+    %t75 = %t74
+    goto  L8
+L10:
+    %t19_0 = p_4+1
+    p_5 = %t19_0
+    (%t92,%t95) = (p_5,nprimes_5)
+    %t93 = %t92
+    %t96 = %t95
+    goto  L2
+L4:
+    (%t85,%t88) = (p_1,nprimes_1)
+    %t87 = %t85
+    %t90 = %t88
+    goto  L11
+L11:
+    (p_2,nprimes_2) = (%t87,%t90)
+    %t20_0 = p_2<N_0
+    if %t20_0 goto L12 else goto L13
+L12:
+    %t21_0 = ary_0[p_2]
+    %t22_0 = !%t21_0
+    (%t79) = (nprimes_2)
+    %t81 = %t79
+    if %t22_0 goto L14 else goto L15
+L14:
+    primes_0[nprimes_2] = p_2
+    %t23_0 = nprimes_2+1
+    nprimes_3 = %t23_0
+    (%t80) = (nprimes_3)
+    %t81 = %t80
+    goto  L15
+L15:
+    (nprimes_4) = (%t81)
+    %t24_0 = p_2+1
+    p_3 = %t24_0
+    (%t86,%t89) = (p_3,nprimes_4)
+    %t87 = %t86
+    %t90 = %t89
+    goto  L11
+L13:
+    %t25_0 = New([Int], len=nprimes_2, initValue=0)
+    rez_0 = %t25_0
+    j_0 = 0
+    (%t82) = (j_0)
+    %t84 = %t82
+    goto  L16
+L16:
+    (j_1) = (%t84)
+    %t26_0 = j_1<nprimes_2
+    if %t26_0 goto L17 else goto L18
+L17:
+    %t27_0 = primes_0[j_1]
+    rez_0[j_1] = %t27_0
+    %t28_0 = j_1+1
+    j_2 = %t28_0
+    (%t83) = (j_2)
+    %t84 = %t83
+    goto  L16
+L18:
+    ret rez_0
+    goto  L1
+L1:
+After exiting SSA
+=================
+L0:
+    arg N_0
+    %t8_0 = New([Int], len=N_0, initValue=0)
+    ary_0 = %t8_0
+    %t10_0 = N_0/2
+    %t9_0 = New([Int], len=%t10_0, initValue=0)
+    primes_0 = %t9_0
+    nprimes_0 = 0
+    p_0 = 2
+    %t91 = p_0
+    %t94 = nprimes_0
+    %t93 = %t91
+    %t96 = %t94
+    goto  L2
+L2:
+    p_1 = %t93
+    nprimes_1 = %t96
+    %t11_0 = p_1*p_1
+    %t12_0 = %t11_0<N_0
+    if %t12_0 goto L3 else goto L4
+L3:
+    %t76 = p_1
+    %t78 = %t76
+    goto  L5
+L5:
+    p_4 = %t78
+    %t13_0 = ary_0[p_4]
+    if %t13_0 goto L6 else goto L7
+L6:
+    %t14_0 = p_4+1
+    p_6 = %t14_0
+    %t77 = p_6
+    %t78 = %t77
+    goto  L5
+L7:
+    primes_0[nprimes_1] = p_4
+    %t15_0 = nprimes_1+1
+    nprimes_5 = %t15_0
+    %t16_0 = p_4+p_4
+    i_0 = %t16_0
+    %t73 = i_0
+    %t75 = %t73
+    goto  L8
+L8:
+    i_1 = %t75
+    %t17_0 = i_1<N_0
+    if %t17_0 goto L9 else goto L10
+L9:
+    ary_0[i_1] = 1
+    %t18_0 = i_1+p_4
+    i_2 = %t18_0
+    %t74 = i_2
+    %t75 = %t74
+    goto  L8
+L10:
+    %t19_0 = p_4+1
+    p_5 = %t19_0
+    %t92 = p_5
+    %t95 = nprimes_5
+    %t93 = %t92
+    %t96 = %t95
+    goto  L2
+L4:
+    %t85 = p_1
+    %t88 = nprimes_1
+    %t87 = %t85
+    %t90 = %t88
+    goto  L11
+L11:
+    p_2 = %t87
+    nprimes_2 = %t90
+    %t20_0 = p_2<N_0
+    if %t20_0 goto L12 else goto L13
+L12:
+    %t21_0 = ary_0[p_2]
+    %t22_0 = !%t21_0
+    %t79 = nprimes_2
+    %t81 = %t79
+    if %t22_0 goto L14 else goto L15
+L14:
+    primes_0[nprimes_2] = p_2
+    %t23_0 = nprimes_2+1
+    nprimes_3 = %t23_0
+    %t80 = nprimes_3
+    %t81 = %t80
+    goto  L15
+L15:
+    nprimes_4 = %t81
+    %t24_0 = p_2+1
+    p_3 = %t24_0
+    %t86 = p_3
+    %t89 = nprimes_4
+    %t87 = %t86
+    %t90 = %t89
+    goto  L11
+L13:
+    %t25_0 = New([Int], len=nprimes_2, initValue=0)
+    rez_0 = %t25_0
+    j_0 = 0
+    %t82 = j_0
+    %t84 = %t82
+    goto  L16
+L16:
+    j_1 = %t84
+    %t26_0 = j_1<nprimes_2
+    if %t26_0 goto L17 else goto L18
+L17:
+    %t27_0 = primes_0[j_1]
+    rez_0[j_1] = %t27_0
+    %t28_0 = j_1+1
+    j_2 = %t28_0
+    %t83 = j_2
+    %t84 = %t83
+    goto  L16
+L18:
+    ret rez_0
+    goto  L1
+L1:
+func eq
+Before SSA
+==========
+L0:
+    arg a
+    arg b
+    arg n
+    result = 1
+    i = 0
+    goto  L2
+L2:
+    %t5 = i<n
+    if %t5 goto L3 else goto L4
+L3:
+    %t6 = a[i]
+    %t7 = b[i]
+    %t8 = %t6!=%t7
+    if %t8 goto L5 else goto L6
+L5:
+    result = 0
+    goto  L4
+L4:
+    ret result
+    goto  L1
+L1:
+L6:
+    %t9 = i+1
+    i = %t9
+    goto  L2
+After SSA
+=========
+L0:
+    arg a_0
+    arg b_0
+    arg n_0
+    result_0 = 1
+    i_0 = 0
+    goto  L2
+L2:
+    i_1 = phi(i_0, i_2)
+    %t5_0 = i_1<n_0
+    if %t5_0 goto L3 else goto L4
+L3:
+    %t6_0 = a_0[i_1]
+    %t7_0 = b_0[i_1]
+    %t8_0 = %t6_0!=%t7_0
+    if %t8_0 goto L5 else goto L6
+L5:
+    result_1 = 0
+    goto  L4
+L4:
+    result_2 = phi(result_0, result_1)
+    ret result_2
+    goto  L1
+L1:
+L6:
+    %t9_0 = i_1+1
+    i_2 = %t9_0
+    goto  L2
+After converting from SSA to CSSA
+L0:
+    arg a_0
+    arg b_0
+    arg n_0
+    result_0 = 1
+    i_0 = 0
+    (%t27) = (i_0)
+    goto  L2
+L2:
+    %t29 = phi(%t27, %t28)
+    (i_1) = (%t29)
+    %t5_0 = i_1<n_0
+    (%t24) = (result_0)
+    if %t5_0 goto L3 else goto L4
+L3:
+    %t6_0 = a_0[i_1]
+    %t7_0 = b_0[i_1]
+    %t8_0 = %t6_0!=%t7_0
+    if %t8_0 goto L5 else goto L6
+L5:
+    result_1 = 0
+    (%t25) = (result_1)
+    goto  L4
+L4:
+    %t26 = phi(%t24, %t25)
+    (result_2) = (%t26)
+    ret result_2
+    goto  L1
+L1:
+L6:
+    %t9_0 = i_1+1
+    i_2 = %t9_0
+    (%t28) = (i_2)
+    goto  L2
+After removing phis from CSSA
+L0:
+    arg a_0
+    arg b_0
+    arg n_0
+    result_0 = 1
+    i_0 = 0
+    (%t27) = (i_0)
+    %t29 = %t27
+    goto  L2
+L2:
+    (i_1) = (%t29)
+    %t5_0 = i_1<n_0
+    (%t24) = (result_0)
+    %t26 = %t24
+    if %t5_0 goto L3 else goto L4
+L3:
+    %t6_0 = a_0[i_1]
+    %t7_0 = b_0[i_1]
+    %t8_0 = %t6_0!=%t7_0
+    if %t8_0 goto L5 else goto L6
+L5:
+    result_1 = 0
+    (%t25) = (result_1)
+    %t26 = %t25
+    goto  L4
+L4:
+    (result_2) = (%t26)
+    ret result_2
+    goto  L1
+L1:
+L6:
+    %t9_0 = i_1+1
+    i_2 = %t9_0
+    (%t28) = (i_2)
+    %t29 = %t28
+    goto  L2
+After exiting SSA
+=================
+L0:
+    arg a_0
+    arg b_0
+    arg n_0
+    result_0 = 1
+    i_0 = 0
+    %t27 = i_0
+    %t29 = %t27
+    goto  L2
+L2:
+    i_1 = %t29
+    %t5_0 = i_1<n_0
+    %t24 = result_0
+    %t26 = %t24
+    if %t5_0 goto L3 else goto L4
+L3:
+    %t6_0 = a_0[i_1]
+    %t7_0 = b_0[i_1]
+    %t8_0 = %t6_0!=%t7_0
+    if %t8_0 goto L5 else goto L6
+L5:
+    result_1 = 0
+    %t25 = result_1
+    %t26 = %t25
+    goto  L4
+L4:
+    result_2 = %t26
+    ret result_2
+    goto  L1
+L1:
+L6:
+    %t9_0 = i_1+1
+    i_2 = %t9_0
+    %t28 = i_2
+    %t29 = %t28
+    goto  L2
+func main
+Before SSA
+==========
+L0:
+    %t2 = 20
+    %t3 = call sieve params %t2
+    rez = %t3
+    %t4 = New([Int], len=8)
+    %t4[0] = 2
+    %t4[1] = 3
+    %t4[2] = 5
+    %t4[3] = 7
+    %t4[4] = 11
+    %t4[5] = 13
+    %t4[6] = 17
+    %t4[7] = 19
+    expected = %t4
+    %t5 = rez
+    %t6 = expected
+    %t7 = 8
+    %t8 = call eq params %t5, %t6, %t7
+    ret %t8
+    goto  L1
+L1:
+After SSA
+=========
+L0:
+    %t2_0 = 20
+    %t3_0 = call sieve params %t2_0
+    rez_0 = %t3_0
+    %t4_0 = New([Int], len=8)
+    %t4_0[0] = 2
+    %t4_0[1] = 3
+    %t4_0[2] = 5
+    %t4_0[3] = 7
+    %t4_0[4] = 11
+    %t4_0[5] = 13
+    %t4_0[6] = 17
+    %t4_0[7] = 19
+    expected_0 = %t4_0
+    %t5_0 = rez_0
+    %t6_0 = expected_0
+    %t7_0 = 8
+    %t8_0 = call eq params %t5_0, %t6_0, %t7_0
+    ret %t8_0
+    goto  L1
+L1:
+After converting from SSA to CSSA
+L0:
+    %t2_0 = 20
+    %t3_0 = call sieve params %t2_0
+    rez_0 = %t3_0
+    %t4_0 = New([Int], len=8)
+    %t4_0[0] = 2
+    %t4_0[1] = 3
+    %t4_0[2] = 5
+    %t4_0[3] = 7
+    %t4_0[4] = 11
+    %t4_0[5] = 13
+    %t4_0[6] = 17
+    %t4_0[7] = 19
+    expected_0 = %t4_0
+    %t5_0 = rez_0
+    %t6_0 = expected_0
+    %t7_0 = 8
+    %t8_0 = call eq params %t5_0, %t6_0, %t7_0
+    ret %t8_0
+    goto  L1
+L1:
+After removing phis from CSSA
+L0:
+    %t2_0 = 20
+    %t3_0 = call sieve params %t2_0
+    rez_0 = %t3_0
+    %t4_0 = New([Int], len=8)
+    %t4_0[0] = 2
+    %t4_0[1] = 3
+    %t4_0[2] = 5
+    %t4_0[3] = 7
+    %t4_0[4] = 11
+    %t4_0[5] = 13
+    %t4_0[6] = 17
+    %t4_0[7] = 19
+    expected_0 = %t4_0
+    %t5_0 = rez_0
+    %t6_0 = expected_0
+    %t7_0 = 8
+    %t8_0 = call eq params %t5_0, %t6_0, %t7_0
+    ret %t8_0
+    goto  L1
+L1:
+After exiting SSA
+=================
+L0:
+    %t2_0 = 20
+    %t3_0 = call sieve params %t2_0
+    rez_0 = %t3_0
+    %t4_0 = New([Int], len=8)
+    %t4_0[0] = 2
+    %t4_0[1] = 3
+    %t4_0[2] = 5
+    %t4_0[3] = 7
+    %t4_0[4] = 11
+    %t4_0[5] = 13
+    %t4_0[6] = 17
+    %t4_0[7] = 19
+    expected_0 = %t4_0
+    %t5_0 = rez_0
+    %t6_0 = expected_0
+    %t7_0 = 8
+    %t8_0 = call eq params %t5_0, %t6_0, %t7_0
+    ret %t8_0
+    goto  L1
+L1:
+""", result);
+    }
+
+    @Test
+    public void testSSA21()
+    {
+        String src = """
+func eq(a: [Int], b: [Int], n: Int)->Int
+{
+    var result = 1
+    var i = 0
+    while (i < n)
+    {
+        if (a[i] != b[i])
+        {
+            result = 0
+            break
+        }
+        i = i + 1
+    } 
+    return result
+}
+                """;
+        String result = compileSrc(src);
+        Assert.assertEquals("""
+func eq
+Before SSA
+==========
+L0:
+    arg a
+    arg b
+    arg n
+    result = 1
+    i = 0
+    goto  L2
+L2:
+    %t5 = i<n
+    if %t5 goto L3 else goto L4
+L3:
+    %t6 = a[i]
+    %t7 = b[i]
+    %t8 = %t6!=%t7
+    if %t8 goto L5 else goto L6
+L5:
+    result = 0
+    goto  L4
+L4:
+    ret result
+    goto  L1
+L1:
+L6:
+    %t9 = i+1
+    i = %t9
+    goto  L2
+After SSA
+=========
+L0:
+    arg a_0
+    arg b_0
+    arg n_0
+    result_0 = 1
+    i_0 = 0
+    goto  L2
+L2:
+    i_1 = phi(i_0, i_2)
+    %t5_0 = i_1<n_0
+    if %t5_0 goto L3 else goto L4
+L3:
+    %t6_0 = a_0[i_1]
+    %t7_0 = b_0[i_1]
+    %t8_0 = %t6_0!=%t7_0
+    if %t8_0 goto L5 else goto L6
+L5:
+    result_1 = 0
+    goto  L4
+L4:
+    result_2 = phi(result_0, result_1)
+    ret result_2
+    goto  L1
+L1:
+L6:
+    %t9_0 = i_1+1
+    i_2 = %t9_0
+    goto  L2
+After converting from SSA to CSSA
+L0:
+    arg a_0
+    arg b_0
+    arg n_0
+    result_0 = 1
+    i_0 = 0
+    (%t27) = (i_0)
+    goto  L2
+L2:
+    %t29 = phi(%t27, %t28)
+    (i_1) = (%t29)
+    %t5_0 = i_1<n_0
+    (%t24) = (result_0)
+    if %t5_0 goto L3 else goto L4
+L3:
+    %t6_0 = a_0[i_1]
+    %t7_0 = b_0[i_1]
+    %t8_0 = %t6_0!=%t7_0
+    if %t8_0 goto L5 else goto L6
+L5:
+    result_1 = 0
+    (%t25) = (result_1)
+    goto  L4
+L4:
+    %t26 = phi(%t24, %t25)
+    (result_2) = (%t26)
+    ret result_2
+    goto  L1
+L1:
+L6:
+    %t9_0 = i_1+1
+    i_2 = %t9_0
+    (%t28) = (i_2)
+    goto  L2
+After removing phis from CSSA
+L0:
+    arg a_0
+    arg b_0
+    arg n_0
+    result_0 = 1
+    i_0 = 0
+    (%t27) = (i_0)
+    %t29 = %t27
+    goto  L2
+L2:
+    (i_1) = (%t29)
+    %t5_0 = i_1<n_0
+    (%t24) = (result_0)
+    %t26 = %t24
+    if %t5_0 goto L3 else goto L4
+L3:
+    %t6_0 = a_0[i_1]
+    %t7_0 = b_0[i_1]
+    %t8_0 = %t6_0!=%t7_0
+    if %t8_0 goto L5 else goto L6
+L5:
+    result_1 = 0
+    (%t25) = (result_1)
+    %t26 = %t25
+    goto  L4
+L4:
+    (result_2) = (%t26)
+    ret result_2
+    goto  L1
+L1:
+L6:
+    %t9_0 = i_1+1
+    i_2 = %t9_0
+    (%t28) = (i_2)
+    %t29 = %t28
+    goto  L2
+After exiting SSA
+=================
+L0:
+    arg a_0
+    arg b_0
+    arg n_0
+    result_0 = 1
+    i_0 = 0
+    %t27 = i_0
+    %t29 = %t27
+    goto  L2
+L2:
+    i_1 = %t29
+    %t5_0 = i_1<n_0
+    %t24 = result_0
+    %t26 = %t24
+    if %t5_0 goto L3 else goto L4
+L3:
+    %t6_0 = a_0[i_1]
+    %t7_0 = b_0[i_1]
+    %t8_0 = %t6_0!=%t7_0
+    if %t8_0 goto L5 else goto L6
+L5:
+    result_1 = 0
+    %t25 = result_1
+    %t26 = %t25
+    goto  L4
+L4:
+    result_2 = %t26
+    ret result_2
+    goto  L1
+L1:
+L6:
+    %t9_0 = i_1+1
+    i_2 = %t9_0
+    %t28 = i_2
+    %t29 = %t28
+    goto  L2
+""", result);
+    }
+
+}
\ No newline at end of file

From a1b0af01fc154d6798df0d415eb2ee031ce62983 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Tue, 12 Aug 2025 11:21:12 +0100
Subject: [PATCH 116/119] #57 Update README

---
 optvm/README.md | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/optvm/README.md b/optvm/README.md
index dc96951..ed134e8 100644
--- a/optvm/README.md
+++ b/optvm/README.md
@@ -44,11 +44,14 @@ A VM / Interpreter is provided that can run the generated code.
   SSA Form using Dominator Trees. The input to this transformation is regular IR, output is SSA IR.
 * Incremental SSA - This method generate SSA IR directly from the AST, using [Braun's algorithm](https://dl.acm.org/doi/10.1007/978-3-642-37051-9_6) - this is integrated into the
   [compiler](src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java) itself and can be enabled using an option.
-* [ExitSSA](src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA.java) - Exits SSA form, using algorithm by Preston Briggs.
+* [ExitSSA](src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA.java) - Exits SSA form - i.e. implements SSA destruction. Two methods are implemented:
+  * [ExitSSABriggs](src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSABriggs.java) - implements method described by [Preston Briggs](https://dl.acm.org/doi/10.5555/295545.295551).
+  * [ExitSSABoissinotNoCoalesce](src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSABoissinotNoCoalesce.java) - implements method described by [Benoit Boissinot](https://inria.hal.science/inria-00349925v1/document) - 
+    without the coalescing step. Thus, it is the "naive" approach that resembles Sreedhar's method I. 
 
 ## Optimizations on SSA Form
 
-* [SparseConditionalConstantPropagation](src/main/java/com/compilerprogramming/ezlang/compiler/SparseConditionalConstantPropagation.java) - Conditional Constant Propagation on SSA form (SCCP)
+* [SparseConditionalConstantPropagation](src/main/java/com/compilerprogramming/ezlang/compiler/SparseConditionalConstantPropagation.java) - Conditional Constant Propagation on SSA form (SCCP). This is an implementation of the paper [Constant propagation with conditional branches](https://dl.acm.org/doi/10.1145/103135.103136).
 * [ConstantComparisonPropagation](src/main/java/com/compilerprogramming/ezlang/compiler/ConstantComparisonPropagation.java) - Detects equals and not equals against constants within conditionals,
    and inserts scoped variables with appropriately specialized type within the dominated blocks, so that a second pass of SCCP can further optimize code.
 * [SSAEdges](src/main/java/com/compilerprogramming/ezlang/compiler/SSAEdges.java) - SSAEdges are def-use chains used by SCCP algorithm, and also generated during incremental SSA construction using Braun's method.
@@ -70,7 +73,7 @@ unlimited amount of those.
 * [InterferenceGraphBuilder](src/main/java/com/compilerprogramming/ezlang/compiler/InterferenceGraphBuilder.java) - Constructs an InterferenceGraph for a set
   of basic bocks, using basic block level liveness information as a starting point for calculating instruction level liveness.
 * [ChaitinGraphColoringRegisterAllocator](src/main/java/com/compilerprogramming/ezlang/compiler/ChaitinGraphColoringRegisterAllocator.java) - basic
-  Chaitin Graph Coloring Register Allocator. Since our target machine here is an abstract machine, we do not really needing spilling support
+  [Chaitin Graph Coloring Register Allocator](https://web.eecs.umich.edu/~mahlke/courses/583f12/reading/chaitin82.pdf) without spilling. Since our target machine here is an abstract machine, we do not really need spilling support
   as we can size each function's stack frame to accommodate the number of registers needed such that each register is really a slot in the stack
   frame. But we will eventually simulate an abstract machine with a limited set of registers and a separate stack frame.
 

From 3b12690384f79118737e5cc561a03e97e9e3ecbc Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Sat, 16 Aug 2025 18:32:30 +0100
Subject: [PATCH 117/119] #57 Update test

---
 .../ezlang/compiler/TestSSADestructB.java     | 71 ++++++++++++++++++-
 1 file changed, 69 insertions(+), 2 deletions(-)

diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSADestructB.java b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSADestructB.java
index d686f79..b3f7144 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSADestructB.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSADestructB.java
@@ -1077,8 +1077,40 @@ public void testLostCopyProblem() {
     ret x2
 """;
         Assert.assertEquals(expected, function.toStr(new StringBuilder(), false).toString());
+        var sb = new StringBuilder();
+        function.setDumpTarget(sb);
         new ExitSSA(function, ssaExitOptions);
         expected = """
+After converting from SSA to CSSA
+L0:
+    arg p
+    x1 = 1
+    (%t4) = (x1)
+    goto  L2
+L2:
+    %t6 = phi(%t4, %t5)
+    (x2) = (%t6)
+    x3 = x2+1
+    (%t5) = (x3)
+    if p goto L2 else goto L1
+L1:
+    ret x2
+After removing phis from CSSA
+L0:
+    arg p
+    x1 = 1
+    (%t4) = (x1)
+    %t6 = %t4
+    goto  L2
+L2:
+    (x2) = (%t6)
+    x3 = x2+1
+    (%t5) = (x3)
+    %t6 = %t5
+    if p goto L2 else goto L1
+L1:
+    ret x2
+After sequencing parallel copies
 L0:
     arg p
     x1 = 1
@@ -1094,7 +1126,8 @@ public void testLostCopyProblem() {
 L1:
     ret x2
 """;
-        Assert.assertEquals(expected, function.toStr(new StringBuilder(), false).toString());
+        sb.append("After sequencing parallel copies\n");
+        Assert.assertEquals(expected, function.toStr(sb, false).toString());
     }
 
     /**
@@ -1150,8 +1183,41 @@ public void testSwapProblem() {
 L1:
 """;
         Assert.assertEquals(expected, function.toStr(new StringBuilder(), false).toString());
+        var sb = new StringBuilder();
+        function.setDumpTarget(sb);
         new ExitSSA(function, ssaExitOptions);
         expected = """
+After converting from SSA to CSSA
+L0:
+    arg p
+    a1 = 42
+    b1 = 24
+    (%t5,%t8) = (a1,b1)
+    goto  L2
+L2:
+    %t7 = phi(%t5, %t6)
+    %t10 = phi(%t8, %t9)
+    (a2,b2) = (%t7,%t10)
+    (%t6,%t9) = (b2,a2)
+    if p goto L2 else goto L1
+L1:
+After removing phis from CSSA
+L0:
+    arg p
+    a1 = 42
+    b1 = 24
+    (%t5,%t8) = (a1,b1)
+    %t7 = %t5
+    %t10 = %t8
+    goto  L2
+L2:
+    (a2,b2) = (%t7,%t10)
+    (%t6,%t9) = (b2,a2)
+    %t7 = %t6
+    %t10 = %t9
+    if p goto L2 else goto L1
+L1:
+After sequencing parallel copies
 L0:
     arg p
     a1 = 42
@@ -1171,7 +1237,8 @@ public void testSwapProblem() {
     if p goto L2 else goto L1
 L1:
 """;
-        Assert.assertEquals(expected, function.toStr(new StringBuilder(), false).toString());
+        sb.append("After sequencing parallel copies\n");
+        Assert.assertEquals(expected, function.toStr(sb, false).toString());
     }
 
     @Test

From 42e6626eddcd2302099978a3af5904c0bf794db0 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Sat, 16 Aug 2025 19:21:14 +0100
Subject: [PATCH 118/119] #57 Use better naming of temps created as a result of
 SSA destruction - using original names as prefix if possible. Update tests.

---
 .../compiler/ExitSSABoissinotNoCoalesce.java  |   18 +-
 .../ezlang/compiler/TestSSADestructB.java     | 1956 ++++++++---------
 2 files changed, 989 insertions(+), 985 deletions(-)

diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSABoissinotNoCoalesce.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSABoissinotNoCoalesce.java
index 8b1eefe..0a43da2 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSABoissinotNoCoalesce.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSABoissinotNoCoalesce.java
@@ -4,9 +4,11 @@
 
 /**
  * Implement method to exit SSA by converting to conventional SSA,
- * without coalescing. This is the basic form.
+ * without coalescing. This is the basic form - it creates extra copies but
+ * the transformation is simple and correct, and does not suffer from the lost copy or
+ * swap problem.
  *
- * This is essentially Method 1 described by
+ * The simple conversion is essentially Method 1 described by
  * Translating Out of Static Single Assignment Form
  * Vugranam C. Sreedhar, Roy Dz-Ching Ju, David M. Gillies, and Vatsa Santhanam
  *
@@ -17,11 +19,11 @@
  * Benoit Boissinot, Alain Darte, Fabrice Rastello, Benoît Dupont de Dinechin, Christophe Guillon
  *
  * The Boissinot paper gives a more correct description, discussing the need for parallel copy
- * and sequencing the parallel copy, but the paper describes a
- * more complex approach that performs coalescing.
+ * and sequencing the parallel copy. Much of the Boissinot paper is about optimizing away any extra copies, but
+ * it seems to me that the Briggs algorithm achieves that anyway while being simpler
+ * to implement.
  *
  * Engineering a Compiler, 3rd edition, describes the simpler form - omitting the coalescing part.
- *
  * Our implementation is similar to EaC.
  *
  * We do not use the sequencing algo described in Boissinot paper. Instead, we use:
@@ -115,12 +117,14 @@ private void makeConventionalSSA() {
                     BasicBlock pred = block.predecessor(j);
                     var pCopyBEnd = getParallelCopyAtEnd(pred);
                     var oldInput = phi.input(j);
-                    var newInput = function.registerPool.newTempReg(oldInput.type);
+                    var newInput = oldInput instanceof Operand.RegisterOperand regInput ?
+                            function.registerPool.newTempReg(regInput.reg.name(), oldInput.type) :
+                            function.registerPool.newTempReg(oldInput.type);
                     pCopyBEnd.addCopy(oldInput,new Operand.RegisterOperand(newInput));
                     phi.replaceInput(j,newInput);
                 }
                 var oldPhiVar = phi.value();
-                var newPhiVar = function.registerPool.newTempReg(oldPhiVar.type);
+                var newPhiVar = function.registerPool.newTempReg(oldPhiVar.name(), oldPhiVar.type);
                 phi.replaceValue(newPhiVar);
                 var pCopyBBegin = getParallelCopyAtBegin(block);
                 pCopyBBegin.addCopy(new Operand.RegisterOperand(newPhiVar),new Operand.RegisterOperand(oldPhiVar));
diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSADestructB.java b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSADestructB.java
index b3f7144..9142a7c 100644
--- a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSADestructB.java
+++ b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSADestructB.java
@@ -192,18 +192,18 @@ func foo(d: Int)->Int {
 L2:
     %t2_0 = a_0+1
     a_2 = %t2_0
-    (%t11) = (a_2)
+    (a_2_11) = (a_2)
     goto  L4
 L4:
-    %t13 = phi(%t11, %t12)
-    (a_3) = (%t13)
+    a_3_13 = phi(a_2_11, a_1_12)
+    (a_3) = (a_3_13)
     ret a_3
     goto  L1
 L1:
 L3:
     %t3_0 = a_0-1
     a_1 = %t3_0
-    (%t12) = (a_1)
+    (a_1_12) = (a_1)
     goto  L4
 After removing phis from CSSA
 L0:
@@ -213,19 +213,19 @@ func foo(d: Int)->Int {
 L2:
     %t2_0 = a_0+1
     a_2 = %t2_0
-    (%t11) = (a_2)
-    %t13 = %t11
+    (a_2_11) = (a_2)
+    a_3_13 = a_2_11
     goto  L4
 L4:
-    (a_3) = (%t13)
+    (a_3) = (a_3_13)
     ret a_3
     goto  L1
 L1:
 L3:
     %t3_0 = a_0-1
     a_1 = %t3_0
-    (%t12) = (a_1)
-    %t13 = %t12
+    (a_1_12) = (a_1)
+    a_3_13 = a_1_12
     goto  L4
 After exiting SSA
 =================
@@ -236,19 +236,19 @@ func foo(d: Int)->Int {
 L2:
     %t2_0 = a_0+1
     a_2 = %t2_0
-    %t11 = a_2
-    %t13 = %t11
+    a_2_11 = a_2
+    a_3_13 = a_2_11
     goto  L4
 L4:
-    a_3 = %t13
+    a_3 = a_3_13
     ret a_3
     goto  L1
 L1:
 L3:
     %t3_0 = a_0-1
     a_1 = %t3_0
-    %t12 = a_1
-    %t13 = %t12
+    a_1_12 = a_1
+    a_3_13 = a_1_12
     goto  L4
 """, result);
 
@@ -314,12 +314,12 @@ func factorial(num: Int)->Int {
 L0:
     arg num_0
     result_0 = 1
-    (%t14,%t17) = (result_0,num_0)
+    (result_0_14,num_0_17) = (result_0,num_0)
     goto  L2
 L2:
-    %t16 = phi(%t14, %t15)
-    %t19 = phi(%t17, %t18)
-    (result_1,num_1) = (%t16,%t19)
+    result_1_16 = phi(result_0_14, result_2_15)
+    num_1_19 = phi(num_0_17, num_2_18)
+    (result_1,num_1) = (result_1_16,num_1_19)
     %t2_0 = num_1>1
     if %t2_0 goto L3 else goto L4
 L3:
@@ -327,7 +327,7 @@ func factorial(num: Int)->Int {
     result_2 = %t3_0
     %t4_0 = num_1-1
     num_2 = %t4_0
-    (%t15,%t18) = (result_2,num_2)
+    (result_2_15,num_2_18) = (result_2,num_2)
     goto  L2
 L4:
     ret result_1
@@ -337,12 +337,12 @@ func factorial(num: Int)->Int {
 L0:
     arg num_0
     result_0 = 1
-    (%t14,%t17) = (result_0,num_0)
-    %t16 = %t14
-    %t19 = %t17
+    (result_0_14,num_0_17) = (result_0,num_0)
+    result_1_16 = result_0_14
+    num_1_19 = num_0_17
     goto  L2
 L2:
-    (result_1,num_1) = (%t16,%t19)
+    (result_1,num_1) = (result_1_16,num_1_19)
     %t2_0 = num_1>1
     if %t2_0 goto L3 else goto L4
 L3:
@@ -350,9 +350,9 @@ func factorial(num: Int)->Int {
     result_2 = %t3_0
     %t4_0 = num_1-1
     num_2 = %t4_0
-    (%t15,%t18) = (result_2,num_2)
-    %t16 = %t15
-    %t19 = %t18
+    (result_2_15,num_2_18) = (result_2,num_2)
+    result_1_16 = result_2_15
+    num_1_19 = num_2_18
     goto  L2
 L4:
     ret result_1
@@ -363,14 +363,14 @@ func factorial(num: Int)->Int {
 L0:
     arg num_0
     result_0 = 1
-    %t14 = result_0
-    %t17 = num_0
-    %t16 = %t14
-    %t19 = %t17
+    result_0_14 = result_0
+    num_0_17 = num_0
+    result_1_16 = result_0_14
+    num_1_19 = num_0_17
     goto  L2
 L2:
-    result_1 = %t16
-    num_1 = %t19
+    result_1 = result_1_16
+    num_1 = num_1_19
     %t2_0 = num_1>1
     if %t2_0 goto L3 else goto L4
 L3:
@@ -378,10 +378,10 @@ func factorial(num: Int)->Int {
     result_2 = %t3_0
     %t4_0 = num_1-1
     num_2 = %t4_0
-    %t15 = result_2
-    %t18 = num_2
-    %t16 = %t15
-    %t19 = %t18
+    result_2_15 = result_2
+    num_2_18 = num_2
+    result_1_16 = result_2_15
+    num_1_19 = num_2_18
     goto  L2
 L4:
     ret result_1
@@ -633,14 +633,14 @@ func example14_66(p: Int, q: Int, r: Int, s: Int, t: Int) {
     j_0 = 1
     k_0 = 1
     l_0 = 1
-    (%t77,%t80,%t83,%t86) = (l_0,k_0,j_0,i_0)
+    (l_0_77,k_0_80,j_0_83,i_0_86) = (l_0,k_0,j_0,i_0)
     goto  L2
 L2:
-    %t79 = phi(%t77, %t78)
-    %t82 = phi(%t80, %t81)
-    %t85 = phi(%t83, %t84)
-    %t88 = phi(%t86, %t87)
-    (l_1,k_1,j_1,i_1) = (%t79,%t82,%t85,%t88)
+    l_1_79 = phi(l_0_77, l_9_78)
+    k_1_82 = phi(k_0_80, k_4_81)
+    j_1_85 = phi(j_0_83, j_3_84)
+    i_1_88 = phi(i_0_86, i_2_87)
+    (l_1,k_1,j_1,i_1) = (l_1_79,k_1_82,j_1_85,i_1_88)
     if 1 goto L3 else goto L4
 L3:
     if p_0 goto L5 else goto L6
@@ -649,51 +649,51 @@ func example14_66(p: Int, q: Int, r: Int, s: Int, t: Int) {
     if q_0 goto L8 else goto L9
 L8:
     l_3 = 2
-    (%t74) = (l_3)
+    (l_3_74) = (l_3)
     goto  L10
 L10:
-    %t76 = phi(%t74, %t75)
-    (l_4) = (%t76)
+    l_4_76 = phi(l_3_74, l_2_75)
+    (l_4) = (l_4_76)
     %t9_0 = k_1+1
     k_3 = %t9_0
-    (%t65,%t68,%t71) = (l_4,k_3,j_2)
+    (l_4_65,k_3_68,j_2_71) = (l_4,k_3,j_2)
     goto  L7
 L7:
-    %t67 = phi(%t65, %t66)
-    %t70 = phi(%t68, %t69)
-    %t73 = phi(%t71, %t72)
-    (l_5,k_4,j_3) = (%t67,%t70,%t73)
+    l_5_67 = phi(l_4_65, l_1_66)
+    k_4_70 = phi(k_3_68, k_2_69)
+    j_3_73 = phi(j_2_71, j_1_72)
+    (l_5,k_4,j_3) = (l_5_67,k_4_70,j_3_73)
     %t11_0 = i_1
     %t12_0 = j_3
     %t13_0 = k_4
     %t14_0 = l_5
     call print params %t11_0, %t12_0, %t13_0, %t14_0
-    (%t62) = (l_5)
+    (l_5_62) = (l_5)
     goto  L11
 L11:
-    %t64 = phi(%t62, %t63)
-    (l_6) = (%t64)
-    (%t56) = (l_6)
+    l_6_64 = phi(l_5_62, l_8_63)
+    (l_6) = (l_6_64)
+    (l_6_56) = (l_6)
     if 1 goto L12 else goto L13
 L12:
-    (%t59) = (l_6)
+    (l_6_59) = (l_6)
     if r_0 goto L14 else goto L15
 L14:
     %t15_0 = l_6+4
     l_7 = %t15_0
-    (%t60) = (l_7)
+    (l_7_60) = (l_7)
     goto  L15
 L15:
-    %t61 = phi(%t59, %t60)
-    (l_8) = (%t61)
+    l_8_61 = phi(l_6_59, l_7_60)
+    (l_8) = (l_8_61)
     %t16_0 = !s_0
     if %t16_0 goto L16 else goto L17
 L16:
-    (%t57) = (l_8)
+    (l_8_57) = (l_8)
     goto  L13
 L13:
-    %t58 = phi(%t56, %t57)
-    (l_9) = (%t58)
+    l_9_58 = phi(l_6_56, l_8_57)
+    (l_9) = (l_9_58)
     %t17_0 = i_1+6
     i_2 = %t17_0
     %t18_0 = !t_0
@@ -704,19 +704,19 @@ func example14_66(p: Int, q: Int, r: Int, s: Int, t: Int) {
     goto  L1
 L1:
 L19:
-    (%t78,%t81,%t84,%t87) = (l_9,k_4,j_3,i_2)
+    (l_9_78,k_4_81,j_3_84,i_2_87) = (l_9,k_4,j_3,i_2)
     goto  L2
 L17:
-    (%t63) = (l_8)
+    (l_8_63) = (l_8)
     goto  L11
 L9:
     l_2 = 3
-    (%t75) = (l_2)
+    (l_2_75) = (l_2)
     goto  L10
 L6:
     %t10_0 = k_1+2
     k_2 = %t10_0
-    (%t66,%t69,%t72) = (l_1,k_2,j_1)
+    (l_1_66,k_2_69,j_1_72) = (l_1,k_2,j_1)
     goto  L7
 After removing phis from CSSA
 L0:
@@ -729,14 +729,14 @@ func example14_66(p: Int, q: Int, r: Int, s: Int, t: Int) {
     j_0 = 1
     k_0 = 1
     l_0 = 1
-    (%t77,%t80,%t83,%t86) = (l_0,k_0,j_0,i_0)
-    %t79 = %t77
-    %t82 = %t80
-    %t85 = %t83
-    %t88 = %t86
+    (l_0_77,k_0_80,j_0_83,i_0_86) = (l_0,k_0,j_0,i_0)
+    l_1_79 = l_0_77
+    k_1_82 = k_0_80
+    j_1_85 = j_0_83
+    i_1_88 = i_0_86
     goto  L2
 L2:
-    (l_1,k_1,j_1,i_1) = (%t79,%t82,%t85,%t88)
+    (l_1,k_1,j_1,i_1) = (l_1_79,k_1_82,j_1_85,i_1_88)
     if 1 goto L3 else goto L4
 L3:
     if p_0 goto L5 else goto L6
@@ -745,53 +745,53 @@ func example14_66(p: Int, q: Int, r: Int, s: Int, t: Int) {
     if q_0 goto L8 else goto L9
 L8:
     l_3 = 2
-    (%t74) = (l_3)
-    %t76 = %t74
+    (l_3_74) = (l_3)
+    l_4_76 = l_3_74
     goto  L10
 L10:
-    (l_4) = (%t76)
+    (l_4) = (l_4_76)
     %t9_0 = k_1+1
     k_3 = %t9_0
-    (%t65,%t68,%t71) = (l_4,k_3,j_2)
-    %t67 = %t65
-    %t70 = %t68
-    %t73 = %t71
+    (l_4_65,k_3_68,j_2_71) = (l_4,k_3,j_2)
+    l_5_67 = l_4_65
+    k_4_70 = k_3_68
+    j_3_73 = j_2_71
     goto  L7
 L7:
-    (l_5,k_4,j_3) = (%t67,%t70,%t73)
+    (l_5,k_4,j_3) = (l_5_67,k_4_70,j_3_73)
     %t11_0 = i_1
     %t12_0 = j_3
     %t13_0 = k_4
     %t14_0 = l_5
     call print params %t11_0, %t12_0, %t13_0, %t14_0
-    (%t62) = (l_5)
-    %t64 = %t62
+    (l_5_62) = (l_5)
+    l_6_64 = l_5_62
     goto  L11
 L11:
-    (l_6) = (%t64)
-    (%t56) = (l_6)
-    %t58 = %t56
+    (l_6) = (l_6_64)
+    (l_6_56) = (l_6)
+    l_9_58 = l_6_56
     if 1 goto L12 else goto L13
 L12:
-    (%t59) = (l_6)
-    %t61 = %t59
+    (l_6_59) = (l_6)
+    l_8_61 = l_6_59
     if r_0 goto L14 else goto L15
 L14:
     %t15_0 = l_6+4
     l_7 = %t15_0
-    (%t60) = (l_7)
-    %t61 = %t60
+    (l_7_60) = (l_7)
+    l_8_61 = l_7_60
     goto  L15
 L15:
-    (l_8) = (%t61)
+    (l_8) = (l_8_61)
     %t16_0 = !s_0
     if %t16_0 goto L16 else goto L17
 L16:
-    (%t57) = (l_8)
-    %t58 = %t57
+    (l_8_57) = (l_8)
+    l_9_58 = l_8_57
     goto  L13
 L13:
-    (l_9) = (%t58)
+    (l_9) = (l_9_58)
     %t17_0 = i_1+6
     i_2 = %t17_0
     %t18_0 = !t_0
@@ -802,28 +802,28 @@ func example14_66(p: Int, q: Int, r: Int, s: Int, t: Int) {
     goto  L1
 L1:
 L19:
-    (%t78,%t81,%t84,%t87) = (l_9,k_4,j_3,i_2)
-    %t79 = %t78
-    %t82 = %t81
-    %t85 = %t84
-    %t88 = %t87
+    (l_9_78,k_4_81,j_3_84,i_2_87) = (l_9,k_4,j_3,i_2)
+    l_1_79 = l_9_78
+    k_1_82 = k_4_81
+    j_1_85 = j_3_84
+    i_1_88 = i_2_87
     goto  L2
 L17:
-    (%t63) = (l_8)
-    %t64 = %t63
+    (l_8_63) = (l_8)
+    l_6_64 = l_8_63
     goto  L11
 L9:
     l_2 = 3
-    (%t75) = (l_2)
-    %t76 = %t75
+    (l_2_75) = (l_2)
+    l_4_76 = l_2_75
     goto  L10
 L6:
     %t10_0 = k_1+2
     k_2 = %t10_0
-    (%t66,%t69,%t72) = (l_1,k_2,j_1)
-    %t67 = %t66
-    %t70 = %t69
-    %t73 = %t72
+    (l_1_66,k_2_69,j_1_72) = (l_1,k_2,j_1)
+    l_5_67 = l_1_66
+    k_4_70 = k_2_69
+    j_3_73 = j_1_72
     goto  L7
 After exiting SSA
 =================
@@ -837,20 +837,20 @@ func example14_66(p: Int, q: Int, r: Int, s: Int, t: Int) {
     j_0 = 1
     k_0 = 1
     l_0 = 1
-    %t77 = l_0
-    %t80 = k_0
-    %t83 = j_0
-    %t86 = i_0
-    %t79 = %t77
-    %t82 = %t80
-    %t85 = %t83
-    %t88 = %t86
-    goto  L2
-L2:
-    l_1 = %t79
-    k_1 = %t82
-    j_1 = %t85
-    i_1 = %t88
+    l_0_77 = l_0
+    k_0_80 = k_0
+    j_0_83 = j_0
+    i_0_86 = i_0
+    l_1_79 = l_0_77
+    k_1_82 = k_0_80
+    j_1_85 = j_0_83
+    i_1_88 = i_0_86
+    goto  L2
+L2:
+    l_1 = l_1_79
+    k_1 = k_1_82
+    j_1 = j_1_85
+    i_1 = i_1_88
     if 1 goto L3 else goto L4
 L3:
     if p_0 goto L5 else goto L6
@@ -859,57 +859,57 @@ func example14_66(p: Int, q: Int, r: Int, s: Int, t: Int) {
     if q_0 goto L8 else goto L9
 L8:
     l_3 = 2
-    %t74 = l_3
-    %t76 = %t74
+    l_3_74 = l_3
+    l_4_76 = l_3_74
     goto  L10
 L10:
-    l_4 = %t76
+    l_4 = l_4_76
     %t9_0 = k_1+1
     k_3 = %t9_0
-    %t65 = l_4
-    %t68 = k_3
-    %t71 = j_2
-    %t67 = %t65
-    %t70 = %t68
-    %t73 = %t71
+    l_4_65 = l_4
+    k_3_68 = k_3
+    j_2_71 = j_2
+    l_5_67 = l_4_65
+    k_4_70 = k_3_68
+    j_3_73 = j_2_71
     goto  L7
 L7:
-    l_5 = %t67
-    k_4 = %t70
-    j_3 = %t73
+    l_5 = l_5_67
+    k_4 = k_4_70
+    j_3 = j_3_73
     %t11_0 = i_1
     %t12_0 = j_3
     %t13_0 = k_4
     %t14_0 = l_5
     call print params %t11_0, %t12_0, %t13_0, %t14_0
-    %t62 = l_5
-    %t64 = %t62
+    l_5_62 = l_5
+    l_6_64 = l_5_62
     goto  L11
 L11:
-    l_6 = %t64
-    %t56 = l_6
-    %t58 = %t56
+    l_6 = l_6_64
+    l_6_56 = l_6
+    l_9_58 = l_6_56
     if 1 goto L12 else goto L13
 L12:
-    %t59 = l_6
-    %t61 = %t59
+    l_6_59 = l_6
+    l_8_61 = l_6_59
     if r_0 goto L14 else goto L15
 L14:
     %t15_0 = l_6+4
     l_7 = %t15_0
-    %t60 = l_7
-    %t61 = %t60
+    l_7_60 = l_7
+    l_8_61 = l_7_60
     goto  L15
 L15:
-    l_8 = %t61
+    l_8 = l_8_61
     %t16_0 = !s_0
     if %t16_0 goto L16 else goto L17
 L16:
-    %t57 = l_8
-    %t58 = %t57
+    l_8_57 = l_8
+    l_9_58 = l_8_57
     goto  L13
 L13:
-    l_9 = %t58
+    l_9 = l_9_58
     %t17_0 = i_1+6
     i_2 = %t17_0
     %t18_0 = !t_0
@@ -920,33 +920,33 @@ func example14_66(p: Int, q: Int, r: Int, s: Int, t: Int) {
     goto  L1
 L1:
 L19:
-    %t78 = l_9
-    %t81 = k_4
-    %t84 = j_3
-    %t87 = i_2
-    %t79 = %t78
-    %t82 = %t81
-    %t85 = %t84
-    %t88 = %t87
+    l_9_78 = l_9
+    k_4_81 = k_4
+    j_3_84 = j_3
+    i_2_87 = i_2
+    l_1_79 = l_9_78
+    k_1_82 = k_4_81
+    j_1_85 = j_3_84
+    i_1_88 = i_2_87
     goto  L2
 L17:
-    %t63 = l_8
-    %t64 = %t63
+    l_8_63 = l_8
+    l_6_64 = l_8_63
     goto  L11
 L9:
     l_2 = 3
-    %t75 = l_2
-    %t76 = %t75
+    l_2_75 = l_2
+    l_4_76 = l_2_75
     goto  L10
 L6:
     %t10_0 = k_1+2
     k_2 = %t10_0
-    %t66 = l_1
-    %t69 = k_2
-    %t72 = j_1
-    %t67 = %t66
-    %t70 = %t69
-    %t73 = %t72
+    l_1_66 = l_1
+    k_2_69 = k_2
+    j_1_72 = j_1
+    l_5_67 = l_1_66
+    k_4_70 = k_2_69
+    j_3_73 = j_1_72
     goto  L7
 """, result);
     }
@@ -1085,13 +1085,13 @@ public void testLostCopyProblem() {
 L0:
     arg p
     x1 = 1
-    (%t4) = (x1)
+    (x1_4) = (x1)
     goto  L2
 L2:
-    %t6 = phi(%t4, %t5)
-    (x2) = (%t6)
+    x2_6 = phi(x1_4, x3_5)
+    (x2) = (x2_6)
     x3 = x2+1
-    (%t5) = (x3)
+    (x3_5) = (x3)
     if p goto L2 else goto L1
 L1:
     ret x2
@@ -1099,14 +1099,14 @@ public void testLostCopyProblem() {
 L0:
     arg p
     x1 = 1
-    (%t4) = (x1)
-    %t6 = %t4
+    (x1_4) = (x1)
+    x2_6 = x1_4
     goto  L2
 L2:
-    (x2) = (%t6)
+    (x2) = (x2_6)
     x3 = x2+1
-    (%t5) = (x3)
-    %t6 = %t5
+    (x3_5) = (x3)
+    x2_6 = x3_5
     if p goto L2 else goto L1
 L1:
     ret x2
@@ -1114,14 +1114,14 @@ public void testLostCopyProblem() {
 L0:
     arg p
     x1 = 1
-    %t4 = x1
-    %t6 = %t4
+    x1_4 = x1
+    x2_6 = x1_4
     goto  L2
 L2:
-    x2 = %t6
+    x2 = x2_6
     x3 = x2+1
-    %t5 = x3
-    %t6 = %t5
+    x3_5 = x3
+    x2_6 = x3_5
     if p goto L2 else goto L1
 L1:
     ret x2
@@ -1192,13 +1192,13 @@ public void testSwapProblem() {
     arg p
     a1 = 42
     b1 = 24
-    (%t5,%t8) = (a1,b1)
+    (a1_5,b1_8) = (a1,b1)
     goto  L2
 L2:
-    %t7 = phi(%t5, %t6)
-    %t10 = phi(%t8, %t9)
-    (a2,b2) = (%t7,%t10)
-    (%t6,%t9) = (b2,a2)
+    a2_7 = phi(a1_5, b2_6)
+    b2_10 = phi(b1_8, a2_9)
+    (a2,b2) = (a2_7,b2_10)
+    (b2_6,a2_9) = (b2,a2)
     if p goto L2 else goto L1
 L1:
 After removing phis from CSSA
@@ -1206,15 +1206,15 @@ public void testSwapProblem() {
     arg p
     a1 = 42
     b1 = 24
-    (%t5,%t8) = (a1,b1)
-    %t7 = %t5
-    %t10 = %t8
+    (a1_5,b1_8) = (a1,b1)
+    a2_7 = a1_5
+    b2_10 = b1_8
     goto  L2
 L2:
-    (a2,b2) = (%t7,%t10)
-    (%t6,%t9) = (b2,a2)
-    %t7 = %t6
-    %t10 = %t9
+    (a2,b2) = (a2_7,b2_10)
+    (b2_6,a2_9) = (b2,a2)
+    a2_7 = b2_6
+    b2_10 = a2_9
     if p goto L2 else goto L1
 L1:
 After sequencing parallel copies
@@ -1222,18 +1222,18 @@ public void testSwapProblem() {
     arg p
     a1 = 42
     b1 = 24
-    %t5 = a1
-    %t8 = b1
-    %t7 = %t5
-    %t10 = %t8
-    goto  L2
-L2:
-    a2 = %t7
-    b2 = %t10
-    %t6 = b2
-    %t9 = a2
-    %t7 = %t6
-    %t10 = %t9
+    a1_5 = a1
+    b1_8 = b1
+    a2_7 = a1_5
+    b2_10 = b1_8
+    goto  L2
+L2:
+    a2 = a2_7
+    b2 = b2_10
+    b2_6 = b2
+    a2_9 = a2
+    a2_7 = b2_6
+    b2_10 = a2_9
     if p goto L2 else goto L1
 L1:
 """;
@@ -1336,11 +1336,11 @@ func foo(x: Int) {
 After converting from SSA to CSSA
 L0:
     arg x_0
-    (%t15) = (x_0)
+    (x_0_15) = (x_0)
     goto  L2
 L2:
-    %t17 = phi(%t15, %t16)
-    (x_1) = (%t17)
+    x_1_17 = phi(x_0_15, x_2_16)
+    (x_1) = (x_1_17)
     %t2_0 = x_1>0
     if %t2_0 goto L3 else goto L4
 L3:
@@ -1354,7 +1354,7 @@ func foo(x: Int) {
 L6:
     %t5_0 = x_1-1
     x_2 = %t5_0
-    (%t16) = (x_2)
+    (x_2_16) = (x_2)
     goto  L2
 L4:
     goto  L1
@@ -1362,11 +1362,11 @@ func foo(x: Int) {
 After removing phis from CSSA
 L0:
     arg x_0
-    (%t15) = (x_0)
-    %t17 = %t15
+    (x_0_15) = (x_0)
+    x_1_17 = x_0_15
     goto  L2
 L2:
-    (x_1) = (%t17)
+    (x_1) = (x_1_17)
     %t2_0 = x_1>0
     if %t2_0 goto L3 else goto L4
 L3:
@@ -1380,8 +1380,8 @@ func foo(x: Int) {
 L6:
     %t5_0 = x_1-1
     x_2 = %t5_0
-    (%t16) = (x_2)
-    %t17 = %t16
+    (x_2_16) = (x_2)
+    x_1_17 = x_2_16
     goto  L2
 L4:
     goto  L1
@@ -1390,11 +1390,11 @@ func foo(x: Int) {
 =================
 L0:
     arg x_0
-    %t15 = x_0
-    %t17 = %t15
+    x_0_15 = x_0
+    x_1_17 = x_0_15
     goto  L2
 L2:
-    x_1 = %t17
+    x_1 = x_1_17
     %t2_0 = x_1>0
     if %t2_0 goto L3 else goto L4
 L3:
@@ -1408,8 +1408,8 @@ func foo(x: Int) {
 L6:
     %t5_0 = x_1-1
     x_2 = %t5_0
-    %t16 = x_2
-    %t17 = %t16
+    x_2_16 = x_2
+    x_1_17 = x_2_16
     goto  L2
 L4:
     goto  L1
@@ -1521,31 +1521,31 @@ func foo(x: Int, y: Int)->Int {
 L1:
 L3:
     sum_0 = 0
-    (%t28,%t31) = (sum_0,x_0)
+    (sum_0_28,x_0_31) = (sum_0,x_0)
     goto  L4
 L4:
-    %t30 = phi(%t28, %t29)
-    %t33 = phi(%t31, %t32)
-    (sum_1,x_1) = (%t30,%t33)
+    sum_1_30 = phi(sum_0_28, sum_3_29)
+    x_1_33 = phi(x_0_31, x_2_32)
+    (sum_1,x_1) = (sum_1_30,x_1_33)
     %t4_0 = x_1<y_0
     if %t4_0 goto L5 else goto L6
 L5:
     %t5_0 = x_1/2
     %t6_0 = %t5_0*2
     %t7_0 = %t6_0==x_1
-    (%t25) = (sum_1)
+    (sum_1_25) = (sum_1)
     if %t7_0 goto L7 else goto L8
 L7:
     %t8_0 = sum_1+1
     sum_2 = %t8_0
-    (%t26) = (sum_2)
+    (sum_2_26) = (sum_2)
     goto  L8
 L8:
-    %t27 = phi(%t25, %t26)
-    (sum_3) = (%t27)
+    sum_3_27 = phi(sum_1_25, sum_2_26)
+    (sum_3) = (sum_3_27)
     %t9_0 = x_1+1
     x_2 = %t9_0
-    (%t29,%t32) = (sum_3,x_2)
+    (sum_3_29,x_2_32) = (sum_3,x_2)
     goto  L4
 L6:
     ret sum_1
@@ -1562,34 +1562,34 @@ func foo(x: Int, y: Int)->Int {
 L1:
 L3:
     sum_0 = 0
-    (%t28,%t31) = (sum_0,x_0)
-    %t30 = %t28
-    %t33 = %t31
+    (sum_0_28,x_0_31) = (sum_0,x_0)
+    sum_1_30 = sum_0_28
+    x_1_33 = x_0_31
     goto  L4
 L4:
-    (sum_1,x_1) = (%t30,%t33)
+    (sum_1,x_1) = (sum_1_30,x_1_33)
     %t4_0 = x_1<y_0
     if %t4_0 goto L5 else goto L6
 L5:
     %t5_0 = x_1/2
     %t6_0 = %t5_0*2
     %t7_0 = %t6_0==x_1
-    (%t25) = (sum_1)
-    %t27 = %t25
+    (sum_1_25) = (sum_1)
+    sum_3_27 = sum_1_25
     if %t7_0 goto L7 else goto L8
 L7:
     %t8_0 = sum_1+1
     sum_2 = %t8_0
-    (%t26) = (sum_2)
-    %t27 = %t26
+    (sum_2_26) = (sum_2)
+    sum_3_27 = sum_2_26
     goto  L8
 L8:
-    (sum_3) = (%t27)
+    (sum_3) = (sum_3_27)
     %t9_0 = x_1+1
     x_2 = %t9_0
-    (%t29,%t32) = (sum_3,x_2)
-    %t30 = %t29
-    %t33 = %t32
+    (sum_3_29,x_2_32) = (sum_3,x_2)
+    sum_1_30 = sum_3_29
+    x_1_33 = x_2_32
     goto  L4
 L6:
     ret sum_1
@@ -1607,37 +1607,37 @@ func foo(x: Int, y: Int)->Int {
 L1:
 L3:
     sum_0 = 0
-    %t28 = sum_0
-    %t31 = x_0
-    %t30 = %t28
-    %t33 = %t31
+    sum_0_28 = sum_0
+    x_0_31 = x_0
+    sum_1_30 = sum_0_28
+    x_1_33 = x_0_31
     goto  L4
 L4:
-    sum_1 = %t30
-    x_1 = %t33
+    sum_1 = sum_1_30
+    x_1 = x_1_33
     %t4_0 = x_1<y_0
     if %t4_0 goto L5 else goto L6
 L5:
     %t5_0 = x_1/2
     %t6_0 = %t5_0*2
     %t7_0 = %t6_0==x_1
-    %t25 = sum_1
-    %t27 = %t25
+    sum_1_25 = sum_1
+    sum_3_27 = sum_1_25
     if %t7_0 goto L7 else goto L8
 L7:
     %t8_0 = sum_1+1
     sum_2 = %t8_0
-    %t26 = sum_2
-    %t27 = %t26
+    sum_2_26 = sum_2
+    sum_3_27 = sum_2_26
     goto  L8
 L8:
-    sum_3 = %t27
+    sum_3 = sum_3_27
     %t9_0 = x_1+1
     x_2 = %t9_0
-    %t29 = sum_3
-    %t32 = x_2
-    %t30 = %t29
-    %t33 = %t32
+    sum_3_29 = sum_3
+    x_2_32 = x_2
+    sum_1_30 = sum_3_29
+    x_1_33 = x_2_32
     goto  L4
 L6:
     ret sum_1
@@ -1736,12 +1736,12 @@ func foo(x: Int)->Int {
     arg x_0
     sum_0 = 0
     i_0 = 0
-    (%t24,%t29) = (i_0,sum_0)
+    (i_0_24,sum_0_29) = (i_0,sum_0)
     goto  L2
 L2:
-    %t28 = phi(%t24, %t25, %t26, %t27)
-    %t33 = phi(%t29, %t30, %t31, %t32)
-    (i_1,sum_1) = (%t28,%t33)
+    i_1_28 = phi(i_0_24, i_1_25, i_1_26, i_2_27)
+    sum_1_33 = phi(sum_0_29, sum_1_30, sum_1_31, sum_2_32)
+    (i_1,sum_1) = (i_1_28,sum_1_33)
     %t3_0 = i_1<x_0
     if %t3_0 goto L3 else goto L4
 L3:
@@ -1749,21 +1749,21 @@ func foo(x: Int)->Int {
     %t5_0 = %t4_0==0
     if %t5_0 goto L5 else goto L6
 L5:
-    (%t25,%t30) = (i_1,sum_1)
+    (i_1_25,sum_1_30) = (i_1,sum_1)
     goto  L2
 L6:
     %t6_0 = i_1/3
     %t7_0 = %t6_0==1
     if %t7_0 goto L7 else goto L8
 L7:
-    (%t26,%t31) = (i_1,sum_1)
+    (i_1_26,sum_1_31) = (i_1,sum_1)
     goto  L2
 L8:
     %t8_0 = sum_1+1
     sum_2 = %t8_0
     %t9_0 = i_1+1
     i_2 = %t9_0
-    (%t27,%t32) = (i_2,sum_2)
+    (i_2_27,sum_2_32) = (i_2,sum_2)
     goto  L2
 L4:
     ret sum_1
@@ -1774,12 +1774,12 @@ func foo(x: Int)->Int {
     arg x_0
     sum_0 = 0
     i_0 = 0
-    (%t24,%t29) = (i_0,sum_0)
-    %t28 = %t24
-    %t33 = %t29
+    (i_0_24,sum_0_29) = (i_0,sum_0)
+    i_1_28 = i_0_24
+    sum_1_33 = sum_0_29
     goto  L2
 L2:
-    (i_1,sum_1) = (%t28,%t33)
+    (i_1,sum_1) = (i_1_28,sum_1_33)
     %t3_0 = i_1<x_0
     if %t3_0 goto L3 else goto L4
 L3:
@@ -1787,27 +1787,27 @@ func foo(x: Int)->Int {
     %t5_0 = %t4_0==0
     if %t5_0 goto L5 else goto L6
 L5:
-    (%t25,%t30) = (i_1,sum_1)
-    %t28 = %t25
-    %t33 = %t30
+    (i_1_25,sum_1_30) = (i_1,sum_1)
+    i_1_28 = i_1_25
+    sum_1_33 = sum_1_30
     goto  L2
 L6:
     %t6_0 = i_1/3
     %t7_0 = %t6_0==1
     if %t7_0 goto L7 else goto L8
 L7:
-    (%t26,%t31) = (i_1,sum_1)
-    %t28 = %t26
-    %t33 = %t31
+    (i_1_26,sum_1_31) = (i_1,sum_1)
+    i_1_28 = i_1_26
+    sum_1_33 = sum_1_31
     goto  L2
 L8:
     %t8_0 = sum_1+1
     sum_2 = %t8_0
     %t9_0 = i_1+1
     i_2 = %t9_0
-    (%t27,%t32) = (i_2,sum_2)
-    %t28 = %t27
-    %t33 = %t32
+    (i_2_27,sum_2_32) = (i_2,sum_2)
+    i_1_28 = i_2_27
+    sum_1_33 = sum_2_32
     goto  L2
 L4:
     ret sum_1
@@ -1819,14 +1819,14 @@ func foo(x: Int)->Int {
     arg x_0
     sum_0 = 0
     i_0 = 0
-    %t24 = i_0
-    %t29 = sum_0
-    %t28 = %t24
-    %t33 = %t29
+    i_0_24 = i_0
+    sum_0_29 = sum_0
+    i_1_28 = i_0_24
+    sum_1_33 = sum_0_29
     goto  L2
 L2:
-    i_1 = %t28
-    sum_1 = %t33
+    i_1 = i_1_28
+    sum_1 = sum_1_33
     %t3_0 = i_1<x_0
     if %t3_0 goto L3 else goto L4
 L3:
@@ -1834,30 +1834,30 @@ func foo(x: Int)->Int {
     %t5_0 = %t4_0==0
     if %t5_0 goto L5 else goto L6
 L5:
-    %t25 = i_1
-    %t30 = sum_1
-    %t28 = %t25
-    %t33 = %t30
+    i_1_25 = i_1
+    sum_1_30 = sum_1
+    i_1_28 = i_1_25
+    sum_1_33 = sum_1_30
     goto  L2
 L6:
     %t6_0 = i_1/3
     %t7_0 = %t6_0==1
     if %t7_0 goto L7 else goto L8
 L7:
-    %t26 = i_1
-    %t31 = sum_1
-    %t28 = %t26
-    %t33 = %t31
+    i_1_26 = i_1
+    sum_1_31 = sum_1
+    i_1_28 = i_1_26
+    sum_1_33 = sum_1_31
     goto  L2
 L8:
     %t8_0 = sum_1+1
     sum_2 = %t8_0
     %t9_0 = i_1+1
     i_2 = %t9_0
-    %t27 = i_2
-    %t32 = sum_2
-    %t28 = %t27
-    %t33 = %t32
+    i_2_27 = i_2
+    sum_2_32 = sum_2
+    i_1_28 = i_2_27
+    sum_1_33 = sum_2_32
     goto  L2
 L4:
     ret sum_1
@@ -2036,13 +2036,13 @@ func foo(n: Int)->Int {
     arg n_0
     a_0 = 1
     b_0 = 2
-    (%t18,%t21,%t24) = (b_0,a_0,n_0)
+    (b_0_18,a_0_21,n_0_24) = (b_0,a_0,n_0)
     goto  L2
 L2:
-    %t20 = phi(%t18, %t19)
-    %t23 = phi(%t21, %t22)
-    %t26 = phi(%t24, %t25)
-    (b_1,a_1,n_1) = (%t20,%t23,%t26)
+    b_1_20 = phi(b_0_18, b_2_19)
+    a_1_23 = phi(a_0_21, a_2_22)
+    n_1_26 = phi(n_0_24, n_2_25)
+    (b_1,a_1,n_1) = (b_1_20,a_1_23,n_1_26)
     %t4_0 = n_1>0
     if %t4_0 goto L3 else goto L4
 L3:
@@ -2051,7 +2051,7 @@ func foo(n: Int)->Int {
     b_2 = t_0
     %t5_0 = n_1-1
     n_2 = %t5_0
-    (%t19,%t22,%t25) = (b_2,a_2,n_2)
+    (b_2_19,a_2_22,n_2_25) = (b_2,a_2,n_2)
     goto  L2
 L4:
     ret a_1
@@ -2062,13 +2062,13 @@ func foo(n: Int)->Int {
     arg n_0
     a_0 = 1
     b_0 = 2
-    (%t18,%t21,%t24) = (b_0,a_0,n_0)
-    %t20 = %t18
-    %t23 = %t21
-    %t26 = %t24
+    (b_0_18,a_0_21,n_0_24) = (b_0,a_0,n_0)
+    b_1_20 = b_0_18
+    a_1_23 = a_0_21
+    n_1_26 = n_0_24
     goto  L2
 L2:
-    (b_1,a_1,n_1) = (%t20,%t23,%t26)
+    (b_1,a_1,n_1) = (b_1_20,a_1_23,n_1_26)
     %t4_0 = n_1>0
     if %t4_0 goto L3 else goto L4
 L3:
@@ -2077,10 +2077,10 @@ func foo(n: Int)->Int {
     b_2 = t_0
     %t5_0 = n_1-1
     n_2 = %t5_0
-    (%t19,%t22,%t25) = (b_2,a_2,n_2)
-    %t20 = %t19
-    %t23 = %t22
-    %t26 = %t25
+    (b_2_19,a_2_22,n_2_25) = (b_2,a_2,n_2)
+    b_1_20 = b_2_19
+    a_1_23 = a_2_22
+    n_1_26 = n_2_25
     goto  L2
 L4:
     ret a_1
@@ -2092,17 +2092,17 @@ func foo(n: Int)->Int {
     arg n_0
     a_0 = 1
     b_0 = 2
-    %t18 = b_0
-    %t21 = a_0
-    %t24 = n_0
-    %t20 = %t18
-    %t23 = %t21
-    %t26 = %t24
+    b_0_18 = b_0
+    a_0_21 = a_0
+    n_0_24 = n_0
+    b_1_20 = b_0_18
+    a_1_23 = a_0_21
+    n_1_26 = n_0_24
     goto  L2
 L2:
-    b_1 = %t20
-    a_1 = %t23
-    n_1 = %t26
+    b_1 = b_1_20
+    a_1 = a_1_23
+    n_1 = n_1_26
     %t4_0 = n_1>0
     if %t4_0 goto L3 else goto L4
 L3:
@@ -2111,12 +2111,12 @@ func foo(n: Int)->Int {
     b_2 = t_0
     %t5_0 = n_1-1
     n_2 = %t5_0
-    %t19 = b_2
-    %t22 = a_2
-    %t25 = n_2
-    %t20 = %t19
-    %t23 = %t22
-    %t26 = %t25
+    b_2_19 = b_2
+    a_2_22 = a_2
+    n_2_25 = n_2
+    b_1_20 = b_2_19
+    a_1_23 = a_2_22
+    n_1_26 = n_2_25
     goto  L2
 L4:
     ret a_1
@@ -2303,17 +2303,17 @@ func foo()->Int {
     if %t1_0 goto L2 else goto L3
 L2:
     a_2 = 10
-    (%t7) = (a_2)
+    (a_2_7) = (a_2)
     goto  L4
 L4:
-    %t9 = phi(%t7, %t8)
-    (a_3) = (%t9)
+    a_3_9 = phi(a_2_7, a_1_8)
+    (a_3) = (a_3_9)
     ret a_3
     goto  L1
 L1:
 L3:
     a_1 = 20
-    (%t8) = (a_1)
+    (a_1_8) = (a_1)
     goto  L4
 After removing phis from CSSA
 L0:
@@ -2322,18 +2322,18 @@ func foo()->Int {
     if %t1_0 goto L2 else goto L3
 L2:
     a_2 = 10
-    (%t7) = (a_2)
-    %t9 = %t7
+    (a_2_7) = (a_2)
+    a_3_9 = a_2_7
     goto  L4
 L4:
-    (a_3) = (%t9)
+    (a_3) = (a_3_9)
     ret a_3
     goto  L1
 L1:
 L3:
     a_1 = 20
-    (%t8) = (a_1)
-    %t9 = %t8
+    (a_1_8) = (a_1)
+    a_3_9 = a_1_8
     goto  L4
 After exiting SSA
 =================
@@ -2343,18 +2343,18 @@ func foo()->Int {
     if %t1_0 goto L2 else goto L3
 L2:
     a_2 = 10
-    %t7 = a_2
-    %t9 = %t7
+    a_2_7 = a_2
+    a_3_9 = a_2_7
     goto  L4
 L4:
-    a_3 = %t9
+    a_3 = a_3_9
     ret a_3
     goto  L1
 L1:
 L3:
     a_1 = 20
-    %t8 = a_1
-    %t9 = %t8
+    a_1_8 = a_1
+    a_3_9 = a_1_8
     goto  L4
 """, result);
     }
@@ -2409,17 +2409,17 @@ func foo()->Int {
 After converting from SSA to CSSA
 L0:
     a_0 = 0
-    (%t8) = (a_0)
+    (a_0_8) = (a_0)
     goto  L2
 L2:
-    %t10 = phi(%t8, %t9)
-    (a_1) = (%t10)
+    a_1_10 = phi(a_0_8, a_2_9)
+    (a_1) = (a_1_10)
     %t1_0 = a_1<5
     if %t1_0 goto L3 else goto L4
 L3:
     %t2_0 = a_1+1
     a_2 = %t2_0
-    (%t9) = (a_2)
+    (a_2_9) = (a_2)
     goto  L2
 L4:
     ret a_1
@@ -2428,18 +2428,18 @@ func foo()->Int {
 After removing phis from CSSA
 L0:
     a_0 = 0
-    (%t8) = (a_0)
-    %t10 = %t8
+    (a_0_8) = (a_0)
+    a_1_10 = a_0_8
     goto  L2
 L2:
-    (a_1) = (%t10)
+    (a_1) = (a_1_10)
     %t1_0 = a_1<5
     if %t1_0 goto L3 else goto L4
 L3:
     %t2_0 = a_1+1
     a_2 = %t2_0
-    (%t9) = (a_2)
-    %t10 = %t9
+    (a_2_9) = (a_2)
+    a_1_10 = a_2_9
     goto  L2
 L4:
     ret a_1
@@ -2449,18 +2449,18 @@ func foo()->Int {
 =================
 L0:
     a_0 = 0
-    %t8 = a_0
-    %t10 = %t8
+    a_0_8 = a_0
+    a_1_10 = a_0_8
     goto  L2
 L2:
-    a_1 = %t10
+    a_1 = a_1_10
     %t1_0 = a_1<5
     if %t1_0 goto L3 else goto L4
 L3:
     %t2_0 = a_1+1
     a_2 = %t2_0
-    %t9 = a_2
-    %t10 = %t9
+    a_2_9 = a_2
+    a_1_10 = a_2_9
     goto  L2
 L4:
     ret a_1
@@ -2553,26 +2553,26 @@ func foo()->Int {
     if %t3_0 goto L5 else goto L6
 L5:
     a_3 = 5
-    (%t16) = (a_3)
+    (a_3_16) = (a_3)
     goto  L7
 L7:
-    %t18 = phi(%t16, %t17)
-    (a_4) = (%t18)
-    (%t13) = (a_4)
+    a_4_18 = phi(a_3_16, a_2_17)
+    (a_4) = (a_4_18)
+    (a_4_13) = (a_4)
     goto  L4
 L4:
-    %t15 = phi(%t13, %t14)
-    (a_5) = (%t15)
+    a_5_15 = phi(a_4_13, a_1_14)
+    (a_5) = (a_5_15)
     ret a_5
     goto  L1
 L1:
 L6:
     a_2 = 15
-    (%t17) = (a_2)
+    (a_2_17) = (a_2)
     goto  L7
 L3:
     a_1 = 20
-    (%t14) = (a_1)
+    (a_1_14) = (a_1)
     goto  L4
 After removing phis from CSSA
 L0:
@@ -2585,28 +2585,28 @@ func foo()->Int {
     if %t3_0 goto L5 else goto L6
 L5:
     a_3 = 5
-    (%t16) = (a_3)
-    %t18 = %t16
+    (a_3_16) = (a_3)
+    a_4_18 = a_3_16
     goto  L7
 L7:
-    (a_4) = (%t18)
-    (%t13) = (a_4)
-    %t15 = %t13
+    (a_4) = (a_4_18)
+    (a_4_13) = (a_4)
+    a_5_15 = a_4_13
     goto  L4
 L4:
-    (a_5) = (%t15)
+    (a_5) = (a_5_15)
     ret a_5
     goto  L1
 L1:
 L6:
     a_2 = 15
-    (%t17) = (a_2)
-    %t18 = %t17
+    (a_2_17) = (a_2)
+    a_4_18 = a_2_17
     goto  L7
 L3:
     a_1 = 20
-    (%t14) = (a_1)
-    %t15 = %t14
+    (a_1_14) = (a_1)
+    a_5_15 = a_1_14
     goto  L4
 After exiting SSA
 =================
@@ -2620,28 +2620,28 @@ func foo()->Int {
     if %t3_0 goto L5 else goto L6
 L5:
     a_3 = 5
-    %t16 = a_3
-    %t18 = %t16
+    a_3_16 = a_3
+    a_4_18 = a_3_16
     goto  L7
 L7:
-    a_4 = %t18
-    %t13 = a_4
-    %t15 = %t13
+    a_4 = a_4_18
+    a_4_13 = a_4
+    a_5_15 = a_4_13
     goto  L4
 L4:
-    a_5 = %t15
+    a_5 = a_5_15
     ret a_5
     goto  L1
 L1:
 L6:
     a_2 = 15
-    %t17 = a_2
-    %t18 = %t17
+    a_2_17 = a_2
+    a_4_18 = a_2_17
     goto  L7
 L3:
     a_1 = 20
-    %t14 = a_1
-    %t15 = %t14
+    a_1_14 = a_1
+    a_5_15 = a_1_14
     goto  L4
 """, result);
     }
@@ -2924,30 +2924,30 @@ func main()->Int {
 L0:
     a_0 = 0
     b_0 = 1
-    (%t21) = (a_0)
+    (a_0_21) = (a_0)
     goto  L2
 L2:
-    %t23 = phi(%t21, %t22)
-    (a_1) = (%t23)
+    a_1_23 = phi(a_0_21, a_2_22)
+    (a_1) = (a_1_23)
     %t2_0 = a_1<10
     if %t2_0 goto L3 else goto L4
 L3:
     %t3_0 = a_1+2
     a_2 = %t3_0
-    (%t22) = (a_2)
+    (a_2_22) = (a_2)
     goto  L2
 L4:
-    (%t18) = (b_0)
+    (b_0_18) = (b_0)
     goto  L5
 L5:
-    %t20 = phi(%t18, %t19)
-    (b_1) = (%t20)
+    b_1_20 = phi(b_0_18, b_2_19)
+    (b_1) = (b_1_20)
     %t4_0 = b_1<20
     if %t4_0 goto L6 else goto L7
 L6:
     %t5_0 = b_1+3
     b_2 = %t5_0
-    (%t19) = (b_2)
+    (b_2_19) = (b_2)
     goto  L5
 L7:
     %t6_0 = a_1+b_1
@@ -2958,32 +2958,32 @@ func main()->Int {
 L0:
     a_0 = 0
     b_0 = 1
-    (%t21) = (a_0)
-    %t23 = %t21
+    (a_0_21) = (a_0)
+    a_1_23 = a_0_21
     goto  L2
 L2:
-    (a_1) = (%t23)
+    (a_1) = (a_1_23)
     %t2_0 = a_1<10
     if %t2_0 goto L3 else goto L4
 L3:
     %t3_0 = a_1+2
     a_2 = %t3_0
-    (%t22) = (a_2)
-    %t23 = %t22
+    (a_2_22) = (a_2)
+    a_1_23 = a_2_22
     goto  L2
 L4:
-    (%t18) = (b_0)
-    %t20 = %t18
+    (b_0_18) = (b_0)
+    b_1_20 = b_0_18
     goto  L5
 L5:
-    (b_1) = (%t20)
+    (b_1) = (b_1_20)
     %t4_0 = b_1<20
     if %t4_0 goto L6 else goto L7
 L6:
     %t5_0 = b_1+3
     b_2 = %t5_0
-    (%t19) = (b_2)
-    %t20 = %t19
+    (b_2_19) = (b_2)
+    b_1_20 = b_2_19
     goto  L5
 L7:
     %t6_0 = a_1+b_1
@@ -2995,32 +2995,32 @@ func main()->Int {
 L0:
     a_0 = 0
     b_0 = 1
-    %t21 = a_0
-    %t23 = %t21
+    a_0_21 = a_0
+    a_1_23 = a_0_21
     goto  L2
 L2:
-    a_1 = %t23
+    a_1 = a_1_23
     %t2_0 = a_1<10
     if %t2_0 goto L3 else goto L4
 L3:
     %t3_0 = a_1+2
     a_2 = %t3_0
-    %t22 = a_2
-    %t23 = %t22
+    a_2_22 = a_2
+    a_1_23 = a_2_22
     goto  L2
 L4:
-    %t18 = b_0
-    %t20 = %t18
+    b_0_18 = b_0
+    b_1_20 = b_0_18
     goto  L5
 L5:
-    b_1 = %t20
+    b_1 = b_1_20
     %t4_0 = b_1<20
     if %t4_0 goto L6 else goto L7
 L6:
     %t5_0 = b_1+3
     b_2 = %t5_0
-    %t19 = b_2
-    %t20 = %t19
+    b_2_19 = b_2
+    b_1_20 = b_2_19
     goto  L5
 L7:
     %t6_0 = a_1+b_1
@@ -3132,23 +3132,23 @@ func main()->Int {
     b_0 = 0
     i_0 = 0
     j_0 = 0
-    (%t38,%t41,%t44) = (i_0,b_0,a_0)
+    (i_0_38,b_0_41,a_0_44) = (i_0,b_0,a_0)
     goto  L2
 L2:
-    %t40 = phi(%t38, %t39)
-    %t43 = phi(%t41, %t42)
-    %t46 = phi(%t44, %t45)
-    (i_1,b_1,a_1) = (%t40,%t43,%t46)
+    i_1_40 = phi(i_0_38, i_3_39)
+    b_1_43 = phi(b_0_41, b_2_42)
+    a_1_46 = phi(a_0_44, a_2_45)
+    (i_1,b_1,a_1) = (i_1_40,b_1_43,a_1_46)
     %t4_0 = i_1<3
     if %t4_0 goto L3 else goto L4
 L3:
     j_1 = 0
-    (%t32,%t35) = (i_1,a_1)
+    (i_1_32,a_1_35) = (i_1,a_1)
     goto  L5
 L5:
-    %t34 = phi(%t32, %t33)
-    %t37 = phi(%t35, %t36)
-    (i_2,a_2) = (%t34,%t37)
+    i_2_34 = phi(i_1_32, i_4_33)
+    a_2_37 = phi(a_1_35, a_3_36)
+    (i_2,a_2) = (i_2_34,a_2_37)
     %t5_0 = j_1<2
     if %t5_0 goto L6 else goto L7
 L6:
@@ -3156,14 +3156,14 @@ func main()->Int {
     a_3 = %t6_0
     %t7_0 = j_1+1
     i_4 = %t7_0
-    (%t33,%t36) = (i_4,a_3)
+    (i_4_33,a_3_36) = (i_4,a_3)
     goto  L5
 L7:
     %t8_0 = b_1+1
     b_2 = %t8_0
     %t9_0 = i_2+1
     i_3 = %t9_0
-    (%t39,%t42,%t45) = (i_3,b_2,a_2)
+    (i_3_39,b_2_42,a_2_45) = (i_3,b_2,a_2)
     goto  L2
 L4:
     %t10_0 = a_1+b_1
@@ -3176,23 +3176,23 @@ func main()->Int {
     b_0 = 0
     i_0 = 0
     j_0 = 0
-    (%t38,%t41,%t44) = (i_0,b_0,a_0)
-    %t40 = %t38
-    %t43 = %t41
-    %t46 = %t44
+    (i_0_38,b_0_41,a_0_44) = (i_0,b_0,a_0)
+    i_1_40 = i_0_38
+    b_1_43 = b_0_41
+    a_1_46 = a_0_44
     goto  L2
 L2:
-    (i_1,b_1,a_1) = (%t40,%t43,%t46)
+    (i_1,b_1,a_1) = (i_1_40,b_1_43,a_1_46)
     %t4_0 = i_1<3
     if %t4_0 goto L3 else goto L4
 L3:
     j_1 = 0
-    (%t32,%t35) = (i_1,a_1)
-    %t34 = %t32
-    %t37 = %t35
+    (i_1_32,a_1_35) = (i_1,a_1)
+    i_2_34 = i_1_32
+    a_2_37 = a_1_35
     goto  L5
 L5:
-    (i_2,a_2) = (%t34,%t37)
+    (i_2,a_2) = (i_2_34,a_2_37)
     %t5_0 = j_1<2
     if %t5_0 goto L6 else goto L7
 L6:
@@ -3200,19 +3200,19 @@ func main()->Int {
     a_3 = %t6_0
     %t7_0 = j_1+1
     i_4 = %t7_0
-    (%t33,%t36) = (i_4,a_3)
-    %t34 = %t33
-    %t37 = %t36
+    (i_4_33,a_3_36) = (i_4,a_3)
+    i_2_34 = i_4_33
+    a_2_37 = a_3_36
     goto  L5
 L7:
     %t8_0 = b_1+1
     b_2 = %t8_0
     %t9_0 = i_2+1
     i_3 = %t9_0
-    (%t39,%t42,%t45) = (i_3,b_2,a_2)
-    %t40 = %t39
-    %t43 = %t42
-    %t46 = %t45
+    (i_3_39,b_2_42,a_2_45) = (i_3,b_2,a_2)
+    i_1_40 = i_3_39
+    b_1_43 = b_2_42
+    a_1_46 = a_2_45
     goto  L2
 L4:
     %t10_0 = a_1+b_1
@@ -3226,29 +3226,29 @@ func main()->Int {
     b_0 = 0
     i_0 = 0
     j_0 = 0
-    %t38 = i_0
-    %t41 = b_0
-    %t44 = a_0
-    %t40 = %t38
-    %t43 = %t41
-    %t46 = %t44
+    i_0_38 = i_0
+    b_0_41 = b_0
+    a_0_44 = a_0
+    i_1_40 = i_0_38
+    b_1_43 = b_0_41
+    a_1_46 = a_0_44
     goto  L2
 L2:
-    i_1 = %t40
-    b_1 = %t43
-    a_1 = %t46
+    i_1 = i_1_40
+    b_1 = b_1_43
+    a_1 = a_1_46
     %t4_0 = i_1<3
     if %t4_0 goto L3 else goto L4
 L3:
     j_1 = 0
-    %t32 = i_1
-    %t35 = a_1
-    %t34 = %t32
-    %t37 = %t35
+    i_1_32 = i_1
+    a_1_35 = a_1
+    i_2_34 = i_1_32
+    a_2_37 = a_1_35
     goto  L5
 L5:
-    i_2 = %t34
-    a_2 = %t37
+    i_2 = i_2_34
+    a_2 = a_2_37
     %t5_0 = j_1<2
     if %t5_0 goto L6 else goto L7
 L6:
@@ -3256,22 +3256,22 @@ func main()->Int {
     a_3 = %t6_0
     %t7_0 = j_1+1
     i_4 = %t7_0
-    %t33 = i_4
-    %t36 = a_3
-    %t34 = %t33
-    %t37 = %t36
+    i_4_33 = i_4
+    a_3_36 = a_3
+    i_2_34 = i_4_33
+    a_2_37 = a_3_36
     goto  L5
 L7:
     %t8_0 = b_1+1
     b_2 = %t8_0
     %t9_0 = i_2+1
     i_3 = %t9_0
-    %t39 = i_3
-    %t42 = b_2
-    %t45 = a_2
-    %t40 = %t39
-    %t43 = %t42
-    %t46 = %t45
+    i_3_39 = i_3
+    b_2_42 = b_2
+    a_2_45 = a_2
+    i_1_40 = i_3_39
+    b_1_43 = b_2_42
+    a_1_46 = a_2_45
     goto  L2
 L4:
     %t10_0 = a_1+b_1
@@ -3385,45 +3385,45 @@ func main()->Int {
     sum_0 = 0
     i_0 = 0
     j_0 = 0
-    (%t38,%t41) = (i_0,sum_0)
+    (i_0_38,sum_0_41) = (i_0,sum_0)
     goto  L2
 L2:
-    %t40 = phi(%t38, %t39)
-    %t43 = phi(%t41, %t42)
-    (i_1,sum_1) = (%t40,%t43)
+    i_1_40 = phi(i_0_38, i_2_39)
+    sum_1_43 = phi(sum_0_41, sum_2_42)
+    (i_1,sum_1) = (i_1_40,sum_1_43)
     %t3_0 = i_1<5
     if %t3_0 goto L3 else goto L4
 L3:
     j_1 = 0
-    (%t32,%t35) = (j_1,sum_1)
+    (j_1_32,sum_1_35) = (j_1,sum_1)
     goto  L5
 L5:
-    %t34 = phi(%t32, %t33)
-    %t37 = phi(%t35, %t36)
-    (j_2,sum_2) = (%t34,%t37)
+    j_2_34 = phi(j_1_32, j_3_33)
+    sum_2_37 = phi(sum_1_35, sum_4_36)
+    (j_2,sum_2) = (j_2_34,sum_2_37)
     %t4_0 = j_2<5
     if %t4_0 goto L6 else goto L7
 L6:
     %t5_0 = j_2%2
     %t6_0 = %t5_0==0
-    (%t29) = (sum_2)
+    (sum_2_29) = (sum_2)
     if %t6_0 goto L8 else goto L9
 L8:
     %t7_0 = sum_2+j_2
     sum_3 = %t7_0
-    (%t30) = (sum_3)
+    (sum_3_30) = (sum_3)
     goto  L9
 L9:
-    %t31 = phi(%t29, %t30)
-    (sum_4) = (%t31)
+    sum_4_31 = phi(sum_2_29, sum_3_30)
+    (sum_4) = (sum_4_31)
     %t8_0 = j_2+1
     j_3 = %t8_0
-    (%t33,%t36) = (j_3,sum_4)
+    (j_3_33,sum_4_36) = (j_3,sum_4)
     goto  L5
 L7:
     %t9_0 = i_1+1
     i_2 = %t9_0
-    (%t39,%t42) = (i_2,sum_2)
+    (i_2_39,sum_2_42) = (i_2,sum_2)
     goto  L2
 L4:
     ret sum_1
@@ -3434,50 +3434,50 @@ func main()->Int {
     sum_0 = 0
     i_0 = 0
     j_0 = 0
-    (%t38,%t41) = (i_0,sum_0)
-    %t40 = %t38
-    %t43 = %t41
+    (i_0_38,sum_0_41) = (i_0,sum_0)
+    i_1_40 = i_0_38
+    sum_1_43 = sum_0_41
     goto  L2
 L2:
-    (i_1,sum_1) = (%t40,%t43)
+    (i_1,sum_1) = (i_1_40,sum_1_43)
     %t3_0 = i_1<5
     if %t3_0 goto L3 else goto L4
 L3:
     j_1 = 0
-    (%t32,%t35) = (j_1,sum_1)
-    %t34 = %t32
-    %t37 = %t35
+    (j_1_32,sum_1_35) = (j_1,sum_1)
+    j_2_34 = j_1_32
+    sum_2_37 = sum_1_35
     goto  L5
 L5:
-    (j_2,sum_2) = (%t34,%t37)
+    (j_2,sum_2) = (j_2_34,sum_2_37)
     %t4_0 = j_2<5
     if %t4_0 goto L6 else goto L7
 L6:
     %t5_0 = j_2%2
     %t6_0 = %t5_0==0
-    (%t29) = (sum_2)
-    %t31 = %t29
+    (sum_2_29) = (sum_2)
+    sum_4_31 = sum_2_29
     if %t6_0 goto L8 else goto L9
 L8:
     %t7_0 = sum_2+j_2
     sum_3 = %t7_0
-    (%t30) = (sum_3)
-    %t31 = %t30
+    (sum_3_30) = (sum_3)
+    sum_4_31 = sum_3_30
     goto  L9
 L9:
-    (sum_4) = (%t31)
+    (sum_4) = (sum_4_31)
     %t8_0 = j_2+1
     j_3 = %t8_0
-    (%t33,%t36) = (j_3,sum_4)
-    %t34 = %t33
-    %t37 = %t36
+    (j_3_33,sum_4_36) = (j_3,sum_4)
+    j_2_34 = j_3_33
+    sum_2_37 = sum_4_36
     goto  L5
 L7:
     %t9_0 = i_1+1
     i_2 = %t9_0
-    (%t39,%t42) = (i_2,sum_2)
-    %t40 = %t39
-    %t43 = %t42
+    (i_2_39,sum_2_42) = (i_2,sum_2)
+    i_1_40 = i_2_39
+    sum_1_43 = sum_2_42
     goto  L2
 L4:
     ret sum_1
@@ -3489,56 +3489,56 @@ func main()->Int {
     sum_0 = 0
     i_0 = 0
     j_0 = 0
-    %t38 = i_0
-    %t41 = sum_0
-    %t40 = %t38
-    %t43 = %t41
+    i_0_38 = i_0
+    sum_0_41 = sum_0
+    i_1_40 = i_0_38
+    sum_1_43 = sum_0_41
     goto  L2
 L2:
-    i_1 = %t40
-    sum_1 = %t43
+    i_1 = i_1_40
+    sum_1 = sum_1_43
     %t3_0 = i_1<5
     if %t3_0 goto L3 else goto L4
 L3:
     j_1 = 0
-    %t32 = j_1
-    %t35 = sum_1
-    %t34 = %t32
-    %t37 = %t35
+    j_1_32 = j_1
+    sum_1_35 = sum_1
+    j_2_34 = j_1_32
+    sum_2_37 = sum_1_35
     goto  L5
 L5:
-    j_2 = %t34
-    sum_2 = %t37
+    j_2 = j_2_34
+    sum_2 = sum_2_37
     %t4_0 = j_2<5
     if %t4_0 goto L6 else goto L7
 L6:
     %t5_0 = j_2%2
     %t6_0 = %t5_0==0
-    %t29 = sum_2
-    %t31 = %t29
+    sum_2_29 = sum_2
+    sum_4_31 = sum_2_29
     if %t6_0 goto L8 else goto L9
 L8:
     %t7_0 = sum_2+j_2
     sum_3 = %t7_0
-    %t30 = sum_3
-    %t31 = %t30
+    sum_3_30 = sum_3
+    sum_4_31 = sum_3_30
     goto  L9
 L9:
-    sum_4 = %t31
+    sum_4 = sum_4_31
     %t8_0 = j_2+1
     j_3 = %t8_0
-    %t33 = j_3
-    %t36 = sum_4
-    %t34 = %t33
-    %t37 = %t36
+    j_3_33 = j_3
+    sum_4_36 = sum_4
+    j_2_34 = j_3_33
+    sum_2_37 = sum_4_36
     goto  L5
 L7:
     %t9_0 = i_1+1
     i_2 = %t9_0
-    %t39 = i_2
-    %t42 = sum_2
-    %t40 = %t39
-    %t43 = %t42
+    i_2_39 = i_2
+    sum_2_42 = sum_2
+    i_1_40 = i_2_39
+    sum_1_43 = sum_2_42
     goto  L2
 L4:
     ret sum_1
@@ -3672,22 +3672,22 @@ else if (i > j)
     a_0 = 0
     i_0 = 0
     j_0 = 0
-    (%t47,%t50) = (i_0,a_0)
+    (i_0_47,a_0_50) = (i_0,a_0)
     goto  L2
 L2:
-    %t49 = phi(%t47, %t48)
-    %t52 = phi(%t50, %t51)
-    (i_1,a_1) = (%t49,%t52)
+    i_1_49 = phi(i_0_47, i_2_48)
+    a_1_52 = phi(a_0_50, a_2_51)
+    (i_1,a_1) = (i_1_49,a_1_52)
     %t3_0 = i_1<3
     if %t3_0 goto L3 else goto L4
 L3:
     j_1 = 0
-    (%t41,%t44) = (j_1,a_1)
+    (j_1_41,a_1_44) = (j_1,a_1)
     goto  L5
 L5:
-    %t43 = phi(%t41, %t42)
-    %t46 = phi(%t44, %t45)
-    (j_2,a_2) = (%t43,%t46)
+    j_2_43 = phi(j_1_41, j_3_42)
+    a_2_46 = phi(a_1_44, a_6_45)
+    (j_2,a_2) = (j_2_43,a_2_46)
     %t4_0 = j_2<3
     if %t4_0 goto L6 else goto L7
 L6:
@@ -3697,33 +3697,33 @@ else if (i > j)
     %t6_0 = a_2+i_1
     %t7_0 = %t6_0+j_2
     a_5 = %t7_0
-    (%t35) = (a_5)
+    (a_5_35) = (a_5)
     goto  L10
 L10:
-    %t37 = phi(%t35, %t36)
-    (a_6) = (%t37)
+    a_6_37 = phi(a_5_35, a_4_36)
+    (a_6) = (a_6_37)
     %t10_0 = j_2+1
     j_3 = %t10_0
-    (%t42,%t45) = (j_3,a_6)
+    (j_3_42,a_6_45) = (j_3,a_6)
     goto  L5
 L9:
     %t8_0 = i_1>j_2
-    (%t38) = (a_2)
+    (a_2_38) = (a_2)
     if %t8_0 goto L11 else goto L12
 L11:
     %t9_0 = a_2-1
     a_3 = %t9_0
-    (%t39) = (a_3)
+    (a_3_39) = (a_3)
     goto  L12
 L12:
-    %t40 = phi(%t38, %t39)
-    (a_4) = (%t40)
-    (%t36) = (a_4)
+    a_4_40 = phi(a_2_38, a_3_39)
+    (a_4) = (a_4_40)
+    (a_4_36) = (a_4)
     goto  L10
 L7:
     %t11_0 = i_1+1
     i_2 = %t11_0
-    (%t48,%t51) = (i_2,a_2)
+    (i_2_48,a_2_51) = (i_2,a_2)
     goto  L2
 L4:
     ret a_1
@@ -3734,22 +3734,22 @@ else if (i > j)
     a_0 = 0
     i_0 = 0
     j_0 = 0
-    (%t47,%t50) = (i_0,a_0)
-    %t49 = %t47
-    %t52 = %t50
+    (i_0_47,a_0_50) = (i_0,a_0)
+    i_1_49 = i_0_47
+    a_1_52 = a_0_50
     goto  L2
 L2:
-    (i_1,a_1) = (%t49,%t52)
+    (i_1,a_1) = (i_1_49,a_1_52)
     %t3_0 = i_1<3
     if %t3_0 goto L3 else goto L4
 L3:
     j_1 = 0
-    (%t41,%t44) = (j_1,a_1)
-    %t43 = %t41
-    %t46 = %t44
+    (j_1_41,a_1_44) = (j_1,a_1)
+    j_2_43 = j_1_41
+    a_2_46 = a_1_44
     goto  L5
 L5:
-    (j_2,a_2) = (%t43,%t46)
+    (j_2,a_2) = (j_2_43,a_2_46)
     %t4_0 = j_2<3
     if %t4_0 goto L6 else goto L7
 L6:
@@ -3759,39 +3759,39 @@ else if (i > j)
     %t6_0 = a_2+i_1
     %t7_0 = %t6_0+j_2
     a_5 = %t7_0
-    (%t35) = (a_5)
-    %t37 = %t35
+    (a_5_35) = (a_5)
+    a_6_37 = a_5_35
     goto  L10
 L10:
-    (a_6) = (%t37)
+    (a_6) = (a_6_37)
     %t10_0 = j_2+1
     j_3 = %t10_0
-    (%t42,%t45) = (j_3,a_6)
-    %t43 = %t42
-    %t46 = %t45
+    (j_3_42,a_6_45) = (j_3,a_6)
+    j_2_43 = j_3_42
+    a_2_46 = a_6_45
     goto  L5
 L9:
     %t8_0 = i_1>j_2
-    (%t38) = (a_2)
-    %t40 = %t38
+    (a_2_38) = (a_2)
+    a_4_40 = a_2_38
     if %t8_0 goto L11 else goto L12
 L11:
     %t9_0 = a_2-1
     a_3 = %t9_0
-    (%t39) = (a_3)
-    %t40 = %t39
+    (a_3_39) = (a_3)
+    a_4_40 = a_3_39
     goto  L12
 L12:
-    (a_4) = (%t40)
-    (%t36) = (a_4)
-    %t37 = %t36
+    (a_4) = (a_4_40)
+    (a_4_36) = (a_4)
+    a_6_37 = a_4_36
     goto  L10
 L7:
     %t11_0 = i_1+1
     i_2 = %t11_0
-    (%t48,%t51) = (i_2,a_2)
-    %t49 = %t48
-    %t52 = %t51
+    (i_2_48,a_2_51) = (i_2,a_2)
+    i_1_49 = i_2_48
+    a_1_52 = a_2_51
     goto  L2
 L4:
     ret a_1
@@ -3803,26 +3803,26 @@ else if (i > j)
     a_0 = 0
     i_0 = 0
     j_0 = 0
-    %t47 = i_0
-    %t50 = a_0
-    %t49 = %t47
-    %t52 = %t50
+    i_0_47 = i_0
+    a_0_50 = a_0
+    i_1_49 = i_0_47
+    a_1_52 = a_0_50
     goto  L2
 L2:
-    i_1 = %t49
-    a_1 = %t52
+    i_1 = i_1_49
+    a_1 = a_1_52
     %t3_0 = i_1<3
     if %t3_0 goto L3 else goto L4
 L3:
     j_1 = 0
-    %t41 = j_1
-    %t44 = a_1
-    %t43 = %t41
-    %t46 = %t44
+    j_1_41 = j_1
+    a_1_44 = a_1
+    j_2_43 = j_1_41
+    a_2_46 = a_1_44
     goto  L5
 L5:
-    j_2 = %t43
-    a_2 = %t46
+    j_2 = j_2_43
+    a_2 = a_2_46
     %t4_0 = j_2<3
     if %t4_0 goto L6 else goto L7
 L6:
@@ -3832,41 +3832,41 @@ else if (i > j)
     %t6_0 = a_2+i_1
     %t7_0 = %t6_0+j_2
     a_5 = %t7_0
-    %t35 = a_5
-    %t37 = %t35
+    a_5_35 = a_5
+    a_6_37 = a_5_35
     goto  L10
 L10:
-    a_6 = %t37
+    a_6 = a_6_37
     %t10_0 = j_2+1
     j_3 = %t10_0
-    %t42 = j_3
-    %t45 = a_6
-    %t43 = %t42
-    %t46 = %t45
+    j_3_42 = j_3
+    a_6_45 = a_6
+    j_2_43 = j_3_42
+    a_2_46 = a_6_45
     goto  L5
 L9:
     %t8_0 = i_1>j_2
-    %t38 = a_2
-    %t40 = %t38
+    a_2_38 = a_2
+    a_4_40 = a_2_38
     if %t8_0 goto L11 else goto L12
 L11:
     %t9_0 = a_2-1
     a_3 = %t9_0
-    %t39 = a_3
-    %t40 = %t39
+    a_3_39 = a_3
+    a_4_40 = a_3_39
     goto  L12
 L12:
-    a_4 = %t40
-    %t36 = a_4
-    %t37 = %t36
+    a_4 = a_4_40
+    a_4_36 = a_4
+    a_6_37 = a_4_36
     goto  L10
 L7:
     %t11_0 = i_1+1
     i_2 = %t11_0
-    %t48 = i_2
-    %t51 = a_2
-    %t49 = %t48
-    %t52 = %t51
+    i_2_48 = i_2
+    a_2_51 = a_2
+    i_1_49 = i_2_48
+    a_1_52 = a_2_51
     goto  L2
 L4:
     ret a_1
@@ -3997,22 +3997,22 @@ func main()->Int {
     count_0 = 0
     i_0 = 0
     j_0 = 0
-    (%t41,%t44) = (i_0,count_0)
+    (i_0_41,count_0_44) = (i_0,count_0)
     goto  L2
 L2:
-    %t43 = phi(%t41, %t42)
-    %t46 = phi(%t44, %t45)
-    (i_1,count_1) = (%t43,%t46)
+    i_1_43 = phi(i_0_41, i_2_42)
+    count_1_46 = phi(count_0_44, count_2_45)
+    (i_1,count_1) = (i_1_43,count_1_46)
     %t3_0 = i_1<5
     if %t3_0 goto L3 else goto L4
 L3:
     j_1 = 0
-    (%t33,%t37) = (j_1,count_1)
+    (j_1_33,count_1_37) = (j_1,count_1)
     goto  L5
 L5:
-    %t36 = phi(%t33, %t34, %t35)
-    %t40 = phi(%t37, %t38, %t39)
-    (j_2,count_2) = (%t36,%t40)
+    j_2_36 = phi(j_1_33, j_4_34, j_3_35)
+    count_2_40 = phi(count_1_37, count_2_38, count_3_39)
+    (j_2,count_2) = (j_2_36,count_2_40)
     %t4_0 = j_2<5
     if %t4_0 goto L6 else goto L7
 L6:
@@ -4024,7 +4024,7 @@ func main()->Int {
 L7:
     %t11_0 = i_1+1
     i_2 = %t11_0
-    (%t42,%t45) = (i_2,count_2)
+    (i_2_42,count_2_45) = (i_2,count_2)
     goto  L2
 L9:
     %t7_0 = i_1==j_2
@@ -4032,14 +4032,14 @@ func main()->Int {
 L10:
     %t8_0 = j_2+1
     j_4 = %t8_0
-    (%t34,%t38) = (j_4,count_2)
+    (j_4_34,count_2_38) = (j_4,count_2)
     goto  L5
 L11:
     %t9_0 = count_2+1
     count_3 = %t9_0
     %t10_0 = j_2+1
     j_3 = %t10_0
-    (%t35,%t39) = (j_3,count_3)
+    (j_3_35,count_3_39) = (j_3,count_3)
     goto  L5
 L4:
     ret count_1
@@ -4050,22 +4050,22 @@ func main()->Int {
     count_0 = 0
     i_0 = 0
     j_0 = 0
-    (%t41,%t44) = (i_0,count_0)
-    %t43 = %t41
-    %t46 = %t44
+    (i_0_41,count_0_44) = (i_0,count_0)
+    i_1_43 = i_0_41
+    count_1_46 = count_0_44
     goto  L2
 L2:
-    (i_1,count_1) = (%t43,%t46)
+    (i_1,count_1) = (i_1_43,count_1_46)
     %t3_0 = i_1<5
     if %t3_0 goto L3 else goto L4
 L3:
     j_1 = 0
-    (%t33,%t37) = (j_1,count_1)
-    %t36 = %t33
-    %t40 = %t37
+    (j_1_33,count_1_37) = (j_1,count_1)
+    j_2_36 = j_1_33
+    count_2_40 = count_1_37
     goto  L5
 L5:
-    (j_2,count_2) = (%t36,%t40)
+    (j_2,count_2) = (j_2_36,count_2_40)
     %t4_0 = j_2<5
     if %t4_0 goto L6 else goto L7
 L6:
@@ -4077,9 +4077,9 @@ func main()->Int {
 L7:
     %t11_0 = i_1+1
     i_2 = %t11_0
-    (%t42,%t45) = (i_2,count_2)
-    %t43 = %t42
-    %t46 = %t45
+    (i_2_42,count_2_45) = (i_2,count_2)
+    i_1_43 = i_2_42
+    count_1_46 = count_2_45
     goto  L2
 L9:
     %t7_0 = i_1==j_2
@@ -4087,18 +4087,18 @@ func main()->Int {
 L10:
     %t8_0 = j_2+1
     j_4 = %t8_0
-    (%t34,%t38) = (j_4,count_2)
-    %t36 = %t34
-    %t40 = %t38
+    (j_4_34,count_2_38) = (j_4,count_2)
+    j_2_36 = j_4_34
+    count_2_40 = count_2_38
     goto  L5
 L11:
     %t9_0 = count_2+1
     count_3 = %t9_0
     %t10_0 = j_2+1
     j_3 = %t10_0
-    (%t35,%t39) = (j_3,count_3)
-    %t36 = %t35
-    %t40 = %t39
+    (j_3_35,count_3_39) = (j_3,count_3)
+    j_2_36 = j_3_35
+    count_2_40 = count_3_39
     goto  L5
 L4:
     ret count_1
@@ -4110,26 +4110,26 @@ func main()->Int {
     count_0 = 0
     i_0 = 0
     j_0 = 0
-    %t41 = i_0
-    %t44 = count_0
-    %t43 = %t41
-    %t46 = %t44
+    i_0_41 = i_0
+    count_0_44 = count_0
+    i_1_43 = i_0_41
+    count_1_46 = count_0_44
     goto  L2
 L2:
-    i_1 = %t43
-    count_1 = %t46
+    i_1 = i_1_43
+    count_1 = count_1_46
     %t3_0 = i_1<5
     if %t3_0 goto L3 else goto L4
 L3:
     j_1 = 0
-    %t33 = j_1
-    %t37 = count_1
-    %t36 = %t33
-    %t40 = %t37
+    j_1_33 = j_1
+    count_1_37 = count_1
+    j_2_36 = j_1_33
+    count_2_40 = count_1_37
     goto  L5
 L5:
-    j_2 = %t36
-    count_2 = %t40
+    j_2 = j_2_36
+    count_2 = count_2_40
     %t4_0 = j_2<5
     if %t4_0 goto L6 else goto L7
 L6:
@@ -4141,10 +4141,10 @@ func main()->Int {
 L7:
     %t11_0 = i_1+1
     i_2 = %t11_0
-    %t42 = i_2
-    %t45 = count_2
-    %t43 = %t42
-    %t46 = %t45
+    i_2_42 = i_2
+    count_2_45 = count_2
+    i_1_43 = i_2_42
+    count_1_46 = count_2_45
     goto  L2
 L9:
     %t7_0 = i_1==j_2
@@ -4152,20 +4152,20 @@ func main()->Int {
 L10:
     %t8_0 = j_2+1
     j_4 = %t8_0
-    %t34 = j_4
-    %t38 = count_2
-    %t36 = %t34
-    %t40 = %t38
+    j_4_34 = j_4
+    count_2_38 = count_2
+    j_2_36 = j_4_34
+    count_2_40 = count_2_38
     goto  L5
 L11:
     %t9_0 = count_2+1
     count_3 = %t9_0
     %t10_0 = j_2+1
     j_3 = %t10_0
-    %t35 = j_3
-    %t39 = count_3
-    %t36 = %t35
-    %t40 = %t39
+    j_3_35 = j_3
+    count_3_39 = count_3
+    j_2_36 = j_3_35
+    count_2_40 = count_3_39
     goto  L5
 L4:
     ret count_1
@@ -4290,49 +4290,49 @@ func main()->Int {
     innerSum_0 = 0
     i_0 = 0
     j_0 = 0
-    (%t46,%t49,%t52) = (i_0,innerSum_0,outerSum_0)
+    (i_0_46,innerSum_0_49,outerSum_0_52) = (i_0,innerSum_0,outerSum_0)
     goto  L2
 L2:
-    %t48 = phi(%t46, %t47)
-    %t51 = phi(%t49, %t50)
-    %t54 = phi(%t52, %t53)
-    (i_1,innerSum_1,outerSum_1) = (%t48,%t51,%t54)
+    i_1_48 = phi(i_0_46, i_2_47)
+    innerSum_1_51 = phi(innerSum_0_49, innerSum_2_50)
+    outerSum_1_54 = phi(outerSum_0_52, outerSum_2_53)
+    (i_1,innerSum_1,outerSum_1) = (i_1_48,innerSum_1_51,outerSum_1_54)
     %t4_0 = i_1<4
     if %t4_0 goto L3 else goto L4
 L3:
     j_1 = 0
-    (%t40,%t43) = (j_1,innerSum_1)
+    (j_1_40,innerSum_1_43) = (j_1,innerSum_1)
     goto  L5
 L5:
-    %t42 = phi(%t40, %t41)
-    %t45 = phi(%t43, %t44)
-    (j_2,innerSum_2) = (%t42,%t45)
+    j_2_42 = phi(j_1_40, j_3_41)
+    innerSum_2_45 = phi(innerSum_1_43, innerSum_4_44)
+    (j_2,innerSum_2) = (j_2_42,innerSum_2_45)
     %t5_0 = j_2<4
     if %t5_0 goto L6 else goto L7
 L6:
     %t6_0 = i_1+j_2
     %t7_0 = %t6_0%2
     %t8_0 = %t7_0==0
-    (%t37) = (innerSum_2)
+    (innerSum_2_37) = (innerSum_2)
     if %t8_0 goto L8 else goto L9
 L8:
     %t9_0 = innerSum_2+j_2
     innerSum_3 = %t9_0
-    (%t38) = (innerSum_3)
+    (innerSum_3_38) = (innerSum_3)
     goto  L9
 L9:
-    %t39 = phi(%t37, %t38)
-    (innerSum_4) = (%t39)
+    innerSum_4_39 = phi(innerSum_2_37, innerSum_3_38)
+    (innerSum_4) = (innerSum_4_39)
     %t10_0 = j_2+1
     j_3 = %t10_0
-    (%t41,%t44) = (j_3,innerSum_4)
+    (j_3_41,innerSum_4_44) = (j_3,innerSum_4)
     goto  L5
 L7:
     %t11_0 = outerSum_1+innerSum_2
     outerSum_2 = %t11_0
     %t12_0 = i_1+1
     i_2 = %t12_0
-    (%t47,%t50,%t53) = (i_2,innerSum_2,outerSum_2)
+    (i_2_47,innerSum_2_50,outerSum_2_53) = (i_2,innerSum_2,outerSum_2)
     goto  L2
 L4:
     ret outerSum_1
@@ -4344,55 +4344,55 @@ func main()->Int {
     innerSum_0 = 0
     i_0 = 0
     j_0 = 0
-    (%t46,%t49,%t52) = (i_0,innerSum_0,outerSum_0)
-    %t48 = %t46
-    %t51 = %t49
-    %t54 = %t52
+    (i_0_46,innerSum_0_49,outerSum_0_52) = (i_0,innerSum_0,outerSum_0)
+    i_1_48 = i_0_46
+    innerSum_1_51 = innerSum_0_49
+    outerSum_1_54 = outerSum_0_52
     goto  L2
 L2:
-    (i_1,innerSum_1,outerSum_1) = (%t48,%t51,%t54)
+    (i_1,innerSum_1,outerSum_1) = (i_1_48,innerSum_1_51,outerSum_1_54)
     %t4_0 = i_1<4
     if %t4_0 goto L3 else goto L4
 L3:
     j_1 = 0
-    (%t40,%t43) = (j_1,innerSum_1)
-    %t42 = %t40
-    %t45 = %t43
+    (j_1_40,innerSum_1_43) = (j_1,innerSum_1)
+    j_2_42 = j_1_40
+    innerSum_2_45 = innerSum_1_43
     goto  L5
 L5:
-    (j_2,innerSum_2) = (%t42,%t45)
+    (j_2,innerSum_2) = (j_2_42,innerSum_2_45)
     %t5_0 = j_2<4
     if %t5_0 goto L6 else goto L7
 L6:
     %t6_0 = i_1+j_2
     %t7_0 = %t6_0%2
     %t8_0 = %t7_0==0
-    (%t37) = (innerSum_2)
-    %t39 = %t37
+    (innerSum_2_37) = (innerSum_2)
+    innerSum_4_39 = innerSum_2_37
     if %t8_0 goto L8 else goto L9
 L8:
     %t9_0 = innerSum_2+j_2
     innerSum_3 = %t9_0
-    (%t38) = (innerSum_3)
-    %t39 = %t38
+    (innerSum_3_38) = (innerSum_3)
+    innerSum_4_39 = innerSum_3_38
     goto  L9
 L9:
-    (innerSum_4) = (%t39)
+    (innerSum_4) = (innerSum_4_39)
     %t10_0 = j_2+1
     j_3 = %t10_0
-    (%t41,%t44) = (j_3,innerSum_4)
-    %t42 = %t41
-    %t45 = %t44
+    (j_3_41,innerSum_4_44) = (j_3,innerSum_4)
+    j_2_42 = j_3_41
+    innerSum_2_45 = innerSum_4_44
     goto  L5
 L7:
     %t11_0 = outerSum_1+innerSum_2
     outerSum_2 = %t11_0
     %t12_0 = i_1+1
     i_2 = %t12_0
-    (%t47,%t50,%t53) = (i_2,innerSum_2,outerSum_2)
-    %t48 = %t47
-    %t51 = %t50
-    %t54 = %t53
+    (i_2_47,innerSum_2_50,outerSum_2_53) = (i_2,innerSum_2,outerSum_2)
+    i_1_48 = i_2_47
+    innerSum_1_51 = innerSum_2_50
+    outerSum_1_54 = outerSum_2_53
     goto  L2
 L4:
     ret outerSum_1
@@ -4405,64 +4405,64 @@ func main()->Int {
     innerSum_0 = 0
     i_0 = 0
     j_0 = 0
-    %t46 = i_0
-    %t49 = innerSum_0
-    %t52 = outerSum_0
-    %t48 = %t46
-    %t51 = %t49
-    %t54 = %t52
+    i_0_46 = i_0
+    innerSum_0_49 = innerSum_0
+    outerSum_0_52 = outerSum_0
+    i_1_48 = i_0_46
+    innerSum_1_51 = innerSum_0_49
+    outerSum_1_54 = outerSum_0_52
     goto  L2
 L2:
-    i_1 = %t48
-    innerSum_1 = %t51
-    outerSum_1 = %t54
+    i_1 = i_1_48
+    innerSum_1 = innerSum_1_51
+    outerSum_1 = outerSum_1_54
     %t4_0 = i_1<4
     if %t4_0 goto L3 else goto L4
 L3:
     j_1 = 0
-    %t40 = j_1
-    %t43 = innerSum_1
-    %t42 = %t40
-    %t45 = %t43
+    j_1_40 = j_1
+    innerSum_1_43 = innerSum_1
+    j_2_42 = j_1_40
+    innerSum_2_45 = innerSum_1_43
     goto  L5
 L5:
-    j_2 = %t42
-    innerSum_2 = %t45
+    j_2 = j_2_42
+    innerSum_2 = innerSum_2_45
     %t5_0 = j_2<4
     if %t5_0 goto L6 else goto L7
 L6:
     %t6_0 = i_1+j_2
     %t7_0 = %t6_0%2
     %t8_0 = %t7_0==0
-    %t37 = innerSum_2
-    %t39 = %t37
+    innerSum_2_37 = innerSum_2
+    innerSum_4_39 = innerSum_2_37
     if %t8_0 goto L8 else goto L9
 L8:
     %t9_0 = innerSum_2+j_2
     innerSum_3 = %t9_0
-    %t38 = innerSum_3
-    %t39 = %t38
+    innerSum_3_38 = innerSum_3
+    innerSum_4_39 = innerSum_3_38
     goto  L9
 L9:
-    innerSum_4 = %t39
+    innerSum_4 = innerSum_4_39
     %t10_0 = j_2+1
     j_3 = %t10_0
-    %t41 = j_3
-    %t44 = innerSum_4
-    %t42 = %t41
-    %t45 = %t44
+    j_3_41 = j_3
+    innerSum_4_44 = innerSum_4
+    j_2_42 = j_3_41
+    innerSum_2_45 = innerSum_4_44
     goto  L5
 L7:
     %t11_0 = outerSum_1+innerSum_2
     outerSum_2 = %t11_0
     %t12_0 = i_1+1
     i_2 = %t12_0
-    %t47 = i_2
-    %t50 = innerSum_2
-    %t53 = outerSum_2
-    %t48 = %t47
-    %t51 = %t50
-    %t54 = %t53
+    i_2_47 = i_2
+    innerSum_2_50 = innerSum_2
+    outerSum_2_53 = outerSum_2
+    i_1_48 = i_2_47
+    innerSum_1_51 = innerSum_2_50
+    outerSum_1_54 = outerSum_2_53
     goto  L2
 L4:
     ret outerSum_1
@@ -4516,35 +4516,35 @@ func foo()->Int
     if 1 goto L2 else goto L3
 L2:
     %t0_1 = 2
-    (%t4) = (%t0_1)
+    (%t0_1_4) = (%t0_1)
     goto  L4
 L4:
-    %t6 = phi(%t4, %t5)
-    (%t0_2) = (%t6)
+    %t0_2_6 = phi(%t0_1_4, %t0_0_5)
+    (%t0_2) = (%t0_2_6)
     ret %t0_2
     goto  L1
 L1:
 L3:
     %t0_0 = 0
-    (%t5) = (%t0_0)
+    (%t0_0_5) = (%t0_0)
     goto  L4
 After removing phis from CSSA
 L0:
     if 1 goto L2 else goto L3
 L2:
     %t0_1 = 2
-    (%t4) = (%t0_1)
-    %t6 = %t4
+    (%t0_1_4) = (%t0_1)
+    %t0_2_6 = %t0_1_4
     goto  L4
 L4:
-    (%t0_2) = (%t6)
+    (%t0_2) = (%t0_2_6)
     ret %t0_2
     goto  L1
 L1:
 L3:
     %t0_0 = 0
-    (%t5) = (%t0_0)
-    %t6 = %t5
+    (%t0_0_5) = (%t0_0)
+    %t0_2_6 = %t0_0_5
     goto  L4
 After exiting SSA
 =================
@@ -4552,18 +4552,18 @@ func foo()->Int
     if 1 goto L2 else goto L3
 L2:
     %t0_1 = 2
-    %t4 = %t0_1
-    %t6 = %t4
+    %t0_1_4 = %t0_1
+    %t0_2_6 = %t0_1_4
     goto  L4
 L4:
-    %t0_2 = %t6
+    %t0_2 = %t0_2_6
     ret %t0_2
     goto  L1
 L1:
 L3:
     %t0_0 = 0
-    %t5 = %t0_0
-    %t6 = %t5
+    %t0_0_5 = %t0_0
+    %t0_2_6 = %t0_0_5
     goto  L4
 """, result);
     }
@@ -4648,17 +4648,17 @@ func foo()->Int
     %t5_0 = f_0[0]
     %t6_0 = %t5_0.i
     %t7_1 = 1==%t6_0
-    (%t18) = (%t7_1)
+    (%t7_1_18) = (%t7_1)
     goto  L4
 L4:
-    %t20 = phi(%t18, %t19)
-    (%t7_2) = (%t20)
+    %t7_2_20 = phi(%t7_1_18, %t7_0_19)
+    (%t7_2) = (%t7_2_20)
     ret %t7_2
     goto  L1
 L1:
 L3:
     %t7_0 = 0
-    (%t19) = (%t7_0)
+    (%t7_0_19) = (%t7_0)
     goto  L4
 After removing phis from CSSA
 L0:
@@ -4675,18 +4675,18 @@ func foo()->Int
     %t5_0 = f_0[0]
     %t6_0 = %t5_0.i
     %t7_1 = 1==%t6_0
-    (%t18) = (%t7_1)
-    %t20 = %t18
+    (%t7_1_18) = (%t7_1)
+    %t7_2_20 = %t7_1_18
     goto  L4
 L4:
-    (%t7_2) = (%t20)
+    (%t7_2) = (%t7_2_20)
     ret %t7_2
     goto  L1
 L1:
 L3:
     %t7_0 = 0
-    (%t19) = (%t7_0)
-    %t20 = %t19
+    (%t7_0_19) = (%t7_0)
+    %t7_2_20 = %t7_0_19
     goto  L4
 After exiting SSA
 =================
@@ -4704,18 +4704,18 @@ func foo()->Int
     %t5_0 = f_0[0]
     %t6_0 = %t5_0.i
     %t7_1 = 1==%t6_0
-    %t18 = %t7_1
-    %t20 = %t18
+    %t7_1_18 = %t7_1
+    %t7_2_20 = %t7_1_18
     goto  L4
 L4:
-    %t7_2 = %t20
+    %t7_2 = %t7_2_20
     ret %t7_2
     goto  L1
 L1:
 L3:
     %t7_0 = 0
-    %t19 = %t7_0
-    %t20 = %t19
+    %t7_0_19 = %t7_0
+    %t7_2_20 = %t7_0_19
     goto  L4
 """, result);
     }
@@ -4813,24 +4813,24 @@ func merge(begin: Int, middle: Int, end: Int)
 L2:
     cond_0 = 0
     %t5_0 = begin_0<middle_0
-    (%t18) = (cond_0)
+    (cond_0_18) = (cond_0)
     if %t5_0 goto L4 else goto L5
 L4:
     %t6_0 = begin_0>=end_0
-    (%t21) = (cond_0)
+    (cond_0_21) = (cond_0)
     if %t6_0 goto L6 else goto L7
 L6:
     cond_1 = 1
-    (%t22) = (cond_1)
+    (cond_1_22) = (cond_1)
     goto  L7
 L7:
-    %t23 = phi(%t21, %t22)
-    (cond_2) = (%t23)
-    (%t19) = (cond_2)
+    cond_2_23 = phi(cond_0_21, cond_1_22)
+    (cond_2) = (cond_2_23)
+    (cond_2_19) = (cond_2)
     goto  L5
 L5:
-    %t20 = phi(%t18, %t19)
-    (cond_3) = (%t20)
+    cond_3_20 = phi(cond_0_18, cond_2_19)
+    (cond_3) = (cond_3_20)
     if cond_3 goto L8 else goto L9
 L8:
     cond_4 = 0
@@ -4850,26 +4850,26 @@ func merge(begin: Int, middle: Int, end: Int)
 L2:
     cond_0 = 0
     %t5_0 = begin_0<middle_0
-    (%t18) = (cond_0)
-    %t20 = %t18
+    (cond_0_18) = (cond_0)
+    cond_3_20 = cond_0_18
     if %t5_0 goto L4 else goto L5
 L4:
     %t6_0 = begin_0>=end_0
-    (%t21) = (cond_0)
-    %t23 = %t21
+    (cond_0_21) = (cond_0)
+    cond_2_23 = cond_0_21
     if %t6_0 goto L6 else goto L7
 L6:
     cond_1 = 1
-    (%t22) = (cond_1)
-    %t23 = %t22
+    (cond_1_22) = (cond_1)
+    cond_2_23 = cond_1_22
     goto  L7
 L7:
-    (cond_2) = (%t23)
-    (%t19) = (cond_2)
-    %t20 = %t19
+    (cond_2) = (cond_2_23)
+    (cond_2_19) = (cond_2)
+    cond_3_20 = cond_2_19
     goto  L5
 L5:
-    (cond_3) = (%t20)
+    (cond_3) = (cond_3_20)
     if cond_3 goto L8 else goto L9
 L8:
     cond_4 = 0
@@ -4890,26 +4890,26 @@ func merge(begin: Int, middle: Int, end: Int)
 L2:
     cond_0 = 0
     %t5_0 = begin_0<middle_0
-    %t18 = cond_0
-    %t20 = %t18
+    cond_0_18 = cond_0
+    cond_3_20 = cond_0_18
     if %t5_0 goto L4 else goto L5
 L4:
     %t6_0 = begin_0>=end_0
-    %t21 = cond_0
-    %t23 = %t21
+    cond_0_21 = cond_0
+    cond_2_23 = cond_0_21
     if %t6_0 goto L6 else goto L7
 L6:
     cond_1 = 1
-    %t22 = cond_1
-    %t23 = %t22
+    cond_1_22 = cond_1
+    cond_2_23 = cond_1_22
     goto  L7
 L7:
-    cond_2 = %t23
-    %t19 = cond_2
-    %t20 = %t19
+    cond_2 = cond_2_23
+    cond_2_19 = cond_2
+    cond_3_20 = cond_2_19
     goto  L5
 L5:
-    cond_3 = %t20
+    cond_3 = cond_3_20
     if cond_3 goto L8 else goto L9
 L8:
     cond_4 = 0
@@ -4986,19 +4986,19 @@ func foo(len: Int, val: Int, x: Int, y: Int)->[Int] {
     arg x_0
     arg y_0
     %t4_0 = x_0>y_0
-    (%t20,%t23) = (val_0,len_0)
+    (val_0_20,len_0_23) = (val_0,len_0)
     if %t4_0 goto L2 else goto L3
 L2:
     %t5_0 = len_0+x_0
     len_1 = %t5_0
     %t6_0 = val_0+x_0
     val_1 = %t6_0
-    (%t21,%t24) = (val_1,len_1)
+    (val_1_21,len_1_24) = (val_1,len_1)
     goto  L3
 L3:
-    %t22 = phi(%t20, %t21)
-    %t25 = phi(%t23, %t24)
-    (val_2,len_2) = (%t22,%t25)
+    val_2_22 = phi(val_0_20, val_1_21)
+    len_2_25 = phi(len_0_23, len_1_24)
+    (val_2,len_2) = (val_2_22,len_2_25)
     %t7_0 = New([Int], len=len_2, initValue=val_2)
     ret %t7_0
     goto  L1
@@ -5010,21 +5010,21 @@ func foo(len: Int, val: Int, x: Int, y: Int)->[Int] {
     arg x_0
     arg y_0
     %t4_0 = x_0>y_0
-    (%t20,%t23) = (val_0,len_0)
-    %t22 = %t20
-    %t25 = %t23
+    (val_0_20,len_0_23) = (val_0,len_0)
+    val_2_22 = val_0_20
+    len_2_25 = len_0_23
     if %t4_0 goto L2 else goto L3
 L2:
     %t5_0 = len_0+x_0
     len_1 = %t5_0
     %t6_0 = val_0+x_0
     val_1 = %t6_0
-    (%t21,%t24) = (val_1,len_1)
-    %t22 = %t21
-    %t25 = %t24
+    (val_1_21,len_1_24) = (val_1,len_1)
+    val_2_22 = val_1_21
+    len_2_25 = len_1_24
     goto  L3
 L3:
-    (val_2,len_2) = (%t22,%t25)
+    (val_2,len_2) = (val_2_22,len_2_25)
     %t7_0 = New([Int], len=len_2, initValue=val_2)
     ret %t7_0
     goto  L1
@@ -5037,24 +5037,24 @@ func foo(len: Int, val: Int, x: Int, y: Int)->[Int] {
     arg x_0
     arg y_0
     %t4_0 = x_0>y_0
-    %t20 = val_0
-    %t23 = len_0
-    %t22 = %t20
-    %t25 = %t23
+    val_0_20 = val_0
+    len_0_23 = len_0
+    val_2_22 = val_0_20
+    len_2_25 = len_0_23
     if %t4_0 goto L2 else goto L3
 L2:
     %t5_0 = len_0+x_0
     len_1 = %t5_0
     %t6_0 = val_0+x_0
     val_1 = %t6_0
-    %t21 = val_1
-    %t24 = len_1
-    %t22 = %t21
-    %t25 = %t24
+    val_1_21 = val_1
+    len_1_24 = len_1
+    val_2_22 = val_1_21
+    len_2_25 = len_1_24
     goto  L3
 L3:
-    val_2 = %t22
-    len_2 = %t25
+    val_2 = val_2_22
+    len_2 = len_2_25
     %t7_0 = New([Int], len=len_2, initValue=val_2)
     ret %t7_0
     goto  L1
@@ -5147,38 +5147,38 @@ func bug(N: Int)
 L0:
     arg N_0
     p_0 = 2
-    (%t23) = (p_0)
+    (p_0_23) = (p_0)
     goto  L2
 L2:
-    %t25 = phi(%t23, %t24)
-    (p_1) = (%t25)
+    p_1_25 = phi(p_0_23, p_5_24)
+    (p_1) = (p_1_25)
     %t2_0 = p_1<N_0
     if %t2_0 goto L3 else goto L4
 L3:
-    (%t17) = (p_1)
+    (p_1_17) = (p_1)
     if p_1 goto L5 else goto L6
 L5:
     %t3_0 = p_1+1
     p_4 = %t3_0
-    (%t18) = (p_4)
+    (p_4_18) = (p_4)
     goto  L6
 L6:
-    %t19 = phi(%t17, %t18)
-    (p_5) = (%t19)
-    (%t24) = (p_5)
+    p_5_19 = phi(p_1_17, p_4_18)
+    (p_5) = (p_5_19)
+    (p_5_24) = (p_5)
     goto  L2
 L4:
-    (%t20) = (p_1)
+    (p_1_20) = (p_1)
     goto  L7
 L7:
-    %t22 = phi(%t20, %t21)
-    (p_2) = (%t22)
+    p_2_22 = phi(p_1_20, p_3_21)
+    (p_2) = (p_2_22)
     %t4_0 = p_2<N_0
     if %t4_0 goto L8 else goto L9
 L8:
     %t5_0 = p_2+1
     p_3 = %t5_0
-    (%t21) = (p_3)
+    (p_3_21) = (p_3)
     goto  L7
 L9:
     goto  L1
@@ -5187,41 +5187,41 @@ func bug(N: Int)
 L0:
     arg N_0
     p_0 = 2
-    (%t23) = (p_0)
-    %t25 = %t23
+    (p_0_23) = (p_0)
+    p_1_25 = p_0_23
     goto  L2
 L2:
-    (p_1) = (%t25)
+    (p_1) = (p_1_25)
     %t2_0 = p_1<N_0
     if %t2_0 goto L3 else goto L4
 L3:
-    (%t17) = (p_1)
-    %t19 = %t17
+    (p_1_17) = (p_1)
+    p_5_19 = p_1_17
     if p_1 goto L5 else goto L6
 L5:
     %t3_0 = p_1+1
     p_4 = %t3_0
-    (%t18) = (p_4)
-    %t19 = %t18
+    (p_4_18) = (p_4)
+    p_5_19 = p_4_18
     goto  L6
 L6:
-    (p_5) = (%t19)
-    (%t24) = (p_5)
-    %t25 = %t24
+    (p_5) = (p_5_19)
+    (p_5_24) = (p_5)
+    p_1_25 = p_5_24
     goto  L2
 L4:
-    (%t20) = (p_1)
-    %t22 = %t20
+    (p_1_20) = (p_1)
+    p_2_22 = p_1_20
     goto  L7
 L7:
-    (p_2) = (%t22)
+    (p_2) = (p_2_22)
     %t4_0 = p_2<N_0
     if %t4_0 goto L8 else goto L9
 L8:
     %t5_0 = p_2+1
     p_3 = %t5_0
-    (%t21) = (p_3)
-    %t22 = %t21
+    (p_3_21) = (p_3)
+    p_2_22 = p_3_21
     goto  L7
 L9:
     goto  L1
@@ -5231,41 +5231,41 @@ func bug(N: Int)
 L0:
     arg N_0
     p_0 = 2
-    %t23 = p_0
-    %t25 = %t23
+    p_0_23 = p_0
+    p_1_25 = p_0_23
     goto  L2
 L2:
-    p_1 = %t25
+    p_1 = p_1_25
     %t2_0 = p_1<N_0
     if %t2_0 goto L3 else goto L4
 L3:
-    %t17 = p_1
-    %t19 = %t17
+    p_1_17 = p_1
+    p_5_19 = p_1_17
     if p_1 goto L5 else goto L6
 L5:
     %t3_0 = p_1+1
     p_4 = %t3_0
-    %t18 = p_4
-    %t19 = %t18
+    p_4_18 = p_4
+    p_5_19 = p_4_18
     goto  L6
 L6:
-    p_5 = %t19
-    %t24 = p_5
-    %t25 = %t24
+    p_5 = p_5_19
+    p_5_24 = p_5
+    p_1_25 = p_5_24
     goto  L2
 L4:
-    %t20 = p_1
-    %t22 = %t20
+    p_1_20 = p_1
+    p_2_22 = p_1_20
     goto  L7
 L7:
-    p_2 = %t22
+    p_2 = p_2_22
     %t4_0 = p_2<N_0
     if %t4_0 goto L8 else goto L9
 L8:
     %t5_0 = p_2+1
     p_3 = %t5_0
-    %t21 = p_3
-    %t22 = %t21
+    p_3_21 = p_3
+    p_2_22 = p_3_21
     goto  L7
 L9:
     goto  L1
@@ -5526,27 +5526,27 @@ func main()->Int
     primes_0 = %t9_0
     nprimes_0 = 0
     p_0 = 2
-    (%t91,%t94) = (p_0,nprimes_0)
+    (p_0_91,nprimes_0_94) = (p_0,nprimes_0)
     goto  L2
 L2:
-    %t93 = phi(%t91, %t92)
-    %t96 = phi(%t94, %t95)
-    (p_1,nprimes_1) = (%t93,%t96)
+    p_1_93 = phi(p_0_91, p_5_92)
+    nprimes_1_96 = phi(nprimes_0_94, nprimes_5_95)
+    (p_1,nprimes_1) = (p_1_93,nprimes_1_96)
     %t11_0 = p_1*p_1
     %t12_0 = %t11_0<N_0
     if %t12_0 goto L3 else goto L4
 L3:
-    (%t76) = (p_1)
+    (p_1_76) = (p_1)
     goto  L5
 L5:
-    %t78 = phi(%t76, %t77)
-    (p_4) = (%t78)
+    p_4_78 = phi(p_1_76, p_6_77)
+    (p_4) = (p_4_78)
     %t13_0 = ary_0[p_4]
     if %t13_0 goto L6 else goto L7
 L6:
     %t14_0 = p_4+1
     p_6 = %t14_0
-    (%t77) = (p_6)
+    (p_6_77) = (p_6)
     goto  L5
 L7:
     primes_0[nprimes_1] = p_4
@@ -5554,60 +5554,60 @@ func main()->Int
     nprimes_5 = %t15_0
     %t16_0 = p_4+p_4
     i_0 = %t16_0
-    (%t73) = (i_0)
+    (i_0_73) = (i_0)
     goto  L8
 L8:
-    %t75 = phi(%t73, %t74)
-    (i_1) = (%t75)
+    i_1_75 = phi(i_0_73, i_2_74)
+    (i_1) = (i_1_75)
     %t17_0 = i_1<N_0
     if %t17_0 goto L9 else goto L10
 L9:
     ary_0[i_1] = 1
     %t18_0 = i_1+p_4
     i_2 = %t18_0
-    (%t74) = (i_2)
+    (i_2_74) = (i_2)
     goto  L8
 L10:
     %t19_0 = p_4+1
     p_5 = %t19_0
-    (%t92,%t95) = (p_5,nprimes_5)
+    (p_5_92,nprimes_5_95) = (p_5,nprimes_5)
     goto  L2
 L4:
-    (%t85,%t88) = (p_1,nprimes_1)
+    (p_1_85,nprimes_1_88) = (p_1,nprimes_1)
     goto  L11
 L11:
-    %t87 = phi(%t85, %t86)
-    %t90 = phi(%t88, %t89)
-    (p_2,nprimes_2) = (%t87,%t90)
+    p_2_87 = phi(p_1_85, p_3_86)
+    nprimes_2_90 = phi(nprimes_1_88, nprimes_4_89)
+    (p_2,nprimes_2) = (p_2_87,nprimes_2_90)
     %t20_0 = p_2<N_0
     if %t20_0 goto L12 else goto L13
 L12:
     %t21_0 = ary_0[p_2]
     %t22_0 = !%t21_0
-    (%t79) = (nprimes_2)
+    (nprimes_2_79) = (nprimes_2)
     if %t22_0 goto L14 else goto L15
 L14:
     primes_0[nprimes_2] = p_2
     %t23_0 = nprimes_2+1
     nprimes_3 = %t23_0
-    (%t80) = (nprimes_3)
+    (nprimes_3_80) = (nprimes_3)
     goto  L15
 L15:
-    %t81 = phi(%t79, %t80)
-    (nprimes_4) = (%t81)
+    nprimes_4_81 = phi(nprimes_2_79, nprimes_3_80)
+    (nprimes_4) = (nprimes_4_81)
     %t24_0 = p_2+1
     p_3 = %t24_0
-    (%t86,%t89) = (p_3,nprimes_4)
+    (p_3_86,nprimes_4_89) = (p_3,nprimes_4)
     goto  L11
 L13:
     %t25_0 = New([Int], len=nprimes_2, initValue=0)
     rez_0 = %t25_0
     j_0 = 0
-    (%t82) = (j_0)
+    (j_0_82) = (j_0)
     goto  L16
 L16:
-    %t84 = phi(%t82, %t83)
-    (j_1) = (%t84)
+    j_1_84 = phi(j_0_82, j_2_83)
+    (j_1) = (j_1_84)
     %t26_0 = j_1<nprimes_2
     if %t26_0 goto L17 else goto L18
 L17:
@@ -5615,7 +5615,7 @@ func main()->Int
     rez_0[j_1] = %t27_0
     %t28_0 = j_1+1
     j_2 = %t28_0
-    (%t83) = (j_2)
+    (j_2_83) = (j_2)
     goto  L16
 L18:
     ret rez_0
@@ -5631,28 +5631,28 @@ func main()->Int
     primes_0 = %t9_0
     nprimes_0 = 0
     p_0 = 2
-    (%t91,%t94) = (p_0,nprimes_0)
-    %t93 = %t91
-    %t96 = %t94
+    (p_0_91,nprimes_0_94) = (p_0,nprimes_0)
+    p_1_93 = p_0_91
+    nprimes_1_96 = nprimes_0_94
     goto  L2
 L2:
-    (p_1,nprimes_1) = (%t93,%t96)
+    (p_1,nprimes_1) = (p_1_93,nprimes_1_96)
     %t11_0 = p_1*p_1
     %t12_0 = %t11_0<N_0
     if %t12_0 goto L3 else goto L4
 L3:
-    (%t76) = (p_1)
-    %t78 = %t76
+    (p_1_76) = (p_1)
+    p_4_78 = p_1_76
     goto  L5
 L5:
-    (p_4) = (%t78)
+    (p_4) = (p_4_78)
     %t13_0 = ary_0[p_4]
     if %t13_0 goto L6 else goto L7
 L6:
     %t14_0 = p_4+1
     p_6 = %t14_0
-    (%t77) = (p_6)
-    %t78 = %t77
+    (p_6_77) = (p_6)
+    p_4_78 = p_6_77
     goto  L5
 L7:
     primes_0[nprimes_1] = p_4
@@ -5660,66 +5660,66 @@ func main()->Int
     nprimes_5 = %t15_0
     %t16_0 = p_4+p_4
     i_0 = %t16_0
-    (%t73) = (i_0)
-    %t75 = %t73
+    (i_0_73) = (i_0)
+    i_1_75 = i_0_73
     goto  L8
 L8:
-    (i_1) = (%t75)
+    (i_1) = (i_1_75)
     %t17_0 = i_1<N_0
     if %t17_0 goto L9 else goto L10
 L9:
     ary_0[i_1] = 1
     %t18_0 = i_1+p_4
     i_2 = %t18_0
-    (%t74) = (i_2)
-    %t75 = %t74
+    (i_2_74) = (i_2)
+    i_1_75 = i_2_74
     goto  L8
 L10:
     %t19_0 = p_4+1
     p_5 = %t19_0
-    (%t92,%t95) = (p_5,nprimes_5)
-    %t93 = %t92
-    %t96 = %t95
+    (p_5_92,nprimes_5_95) = (p_5,nprimes_5)
+    p_1_93 = p_5_92
+    nprimes_1_96 = nprimes_5_95
     goto  L2
 L4:
-    (%t85,%t88) = (p_1,nprimes_1)
-    %t87 = %t85
-    %t90 = %t88
+    (p_1_85,nprimes_1_88) = (p_1,nprimes_1)
+    p_2_87 = p_1_85
+    nprimes_2_90 = nprimes_1_88
     goto  L11
 L11:
-    (p_2,nprimes_2) = (%t87,%t90)
+    (p_2,nprimes_2) = (p_2_87,nprimes_2_90)
     %t20_0 = p_2<N_0
     if %t20_0 goto L12 else goto L13
 L12:
     %t21_0 = ary_0[p_2]
     %t22_0 = !%t21_0
-    (%t79) = (nprimes_2)
-    %t81 = %t79
+    (nprimes_2_79) = (nprimes_2)
+    nprimes_4_81 = nprimes_2_79
     if %t22_0 goto L14 else goto L15
 L14:
     primes_0[nprimes_2] = p_2
     %t23_0 = nprimes_2+1
     nprimes_3 = %t23_0
-    (%t80) = (nprimes_3)
-    %t81 = %t80
+    (nprimes_3_80) = (nprimes_3)
+    nprimes_4_81 = nprimes_3_80
     goto  L15
 L15:
-    (nprimes_4) = (%t81)
+    (nprimes_4) = (nprimes_4_81)
     %t24_0 = p_2+1
     p_3 = %t24_0
-    (%t86,%t89) = (p_3,nprimes_4)
-    %t87 = %t86
-    %t90 = %t89
+    (p_3_86,nprimes_4_89) = (p_3,nprimes_4)
+    p_2_87 = p_3_86
+    nprimes_2_90 = nprimes_4_89
     goto  L11
 L13:
     %t25_0 = New([Int], len=nprimes_2, initValue=0)
     rez_0 = %t25_0
     j_0 = 0
-    (%t82) = (j_0)
-    %t84 = %t82
+    (j_0_82) = (j_0)
+    j_1_84 = j_0_82
     goto  L16
 L16:
-    (j_1) = (%t84)
+    (j_1) = (j_1_84)
     %t26_0 = j_1<nprimes_2
     if %t26_0 goto L17 else goto L18
 L17:
@@ -5727,8 +5727,8 @@ func main()->Int
     rez_0[j_1] = %t27_0
     %t28_0 = j_1+1
     j_2 = %t28_0
-    (%t83) = (j_2)
-    %t84 = %t83
+    (j_2_83) = (j_2)
+    j_1_84 = j_2_83
     goto  L16
 L18:
     ret rez_0
@@ -5745,30 +5745,30 @@ func main()->Int
     primes_0 = %t9_0
     nprimes_0 = 0
     p_0 = 2
-    %t91 = p_0
-    %t94 = nprimes_0
-    %t93 = %t91
-    %t96 = %t94
+    p_0_91 = p_0
+    nprimes_0_94 = nprimes_0
+    p_1_93 = p_0_91
+    nprimes_1_96 = nprimes_0_94
     goto  L2
 L2:
-    p_1 = %t93
-    nprimes_1 = %t96
+    p_1 = p_1_93
+    nprimes_1 = nprimes_1_96
     %t11_0 = p_1*p_1
     %t12_0 = %t11_0<N_0
     if %t12_0 goto L3 else goto L4
 L3:
-    %t76 = p_1
-    %t78 = %t76
+    p_1_76 = p_1
+    p_4_78 = p_1_76
     goto  L5
 L5:
-    p_4 = %t78
+    p_4 = p_4_78
     %t13_0 = ary_0[p_4]
     if %t13_0 goto L6 else goto L7
 L6:
     %t14_0 = p_4+1
     p_6 = %t14_0
-    %t77 = p_6
-    %t78 = %t77
+    p_6_77 = p_6
+    p_4_78 = p_6_77
     goto  L5
 L7:
     primes_0[nprimes_1] = p_4
@@ -5776,70 +5776,70 @@ func main()->Int
     nprimes_5 = %t15_0
     %t16_0 = p_4+p_4
     i_0 = %t16_0
-    %t73 = i_0
-    %t75 = %t73
+    i_0_73 = i_0
+    i_1_75 = i_0_73
     goto  L8
 L8:
-    i_1 = %t75
+    i_1 = i_1_75
     %t17_0 = i_1<N_0
     if %t17_0 goto L9 else goto L10
 L9:
     ary_0[i_1] = 1
     %t18_0 = i_1+p_4
     i_2 = %t18_0
-    %t74 = i_2
-    %t75 = %t74
+    i_2_74 = i_2
+    i_1_75 = i_2_74
     goto  L8
 L10:
     %t19_0 = p_4+1
     p_5 = %t19_0
-    %t92 = p_5
-    %t95 = nprimes_5
-    %t93 = %t92
-    %t96 = %t95
+    p_5_92 = p_5
+    nprimes_5_95 = nprimes_5
+    p_1_93 = p_5_92
+    nprimes_1_96 = nprimes_5_95
     goto  L2
 L4:
-    %t85 = p_1
-    %t88 = nprimes_1
-    %t87 = %t85
-    %t90 = %t88
+    p_1_85 = p_1
+    nprimes_1_88 = nprimes_1
+    p_2_87 = p_1_85
+    nprimes_2_90 = nprimes_1_88
     goto  L11
 L11:
-    p_2 = %t87
-    nprimes_2 = %t90
+    p_2 = p_2_87
+    nprimes_2 = nprimes_2_90
     %t20_0 = p_2<N_0
     if %t20_0 goto L12 else goto L13
 L12:
     %t21_0 = ary_0[p_2]
     %t22_0 = !%t21_0
-    %t79 = nprimes_2
-    %t81 = %t79
+    nprimes_2_79 = nprimes_2
+    nprimes_4_81 = nprimes_2_79
     if %t22_0 goto L14 else goto L15
 L14:
     primes_0[nprimes_2] = p_2
     %t23_0 = nprimes_2+1
     nprimes_3 = %t23_0
-    %t80 = nprimes_3
-    %t81 = %t80
+    nprimes_3_80 = nprimes_3
+    nprimes_4_81 = nprimes_3_80
     goto  L15
 L15:
-    nprimes_4 = %t81
+    nprimes_4 = nprimes_4_81
     %t24_0 = p_2+1
     p_3 = %t24_0
-    %t86 = p_3
-    %t89 = nprimes_4
-    %t87 = %t86
-    %t90 = %t89
+    p_3_86 = p_3
+    nprimes_4_89 = nprimes_4
+    p_2_87 = p_3_86
+    nprimes_2_90 = nprimes_4_89
     goto  L11
 L13:
     %t25_0 = New([Int], len=nprimes_2, initValue=0)
     rez_0 = %t25_0
     j_0 = 0
-    %t82 = j_0
-    %t84 = %t82
+    j_0_82 = j_0
+    j_1_84 = j_0_82
     goto  L16
 L16:
-    j_1 = %t84
+    j_1 = j_1_84
     %t26_0 = j_1<nprimes_2
     if %t26_0 goto L17 else goto L18
 L17:
@@ -5847,8 +5847,8 @@ func main()->Int
     rez_0[j_1] = %t27_0
     %t28_0 = j_1+1
     j_2 = %t28_0
-    %t83 = j_2
-    %t84 = %t83
+    j_2_83 = j_2
+    j_1_84 = j_2_83
     goto  L16
 L18:
     ret rez_0
@@ -5920,13 +5920,13 @@ func main()->Int
     arg n_0
     result_0 = 1
     i_0 = 0
-    (%t27) = (i_0)
+    (i_0_27) = (i_0)
     goto  L2
 L2:
-    %t29 = phi(%t27, %t28)
-    (i_1) = (%t29)
+    i_1_29 = phi(i_0_27, i_2_28)
+    (i_1) = (i_1_29)
     %t5_0 = i_1<n_0
-    (%t24) = (result_0)
+    (result_0_24) = (result_0)
     if %t5_0 goto L3 else goto L4
 L3:
     %t6_0 = a_0[i_1]
@@ -5935,18 +5935,18 @@ func main()->Int
     if %t8_0 goto L5 else goto L6
 L5:
     result_1 = 0
-    (%t25) = (result_1)
+    (result_1_25) = (result_1)
     goto  L4
 L4:
-    %t26 = phi(%t24, %t25)
-    (result_2) = (%t26)
+    result_2_26 = phi(result_0_24, result_1_25)
+    (result_2) = (result_2_26)
     ret result_2
     goto  L1
 L1:
 L6:
     %t9_0 = i_1+1
     i_2 = %t9_0
-    (%t28) = (i_2)
+    (i_2_28) = (i_2)
     goto  L2
 After removing phis from CSSA
 L0:
@@ -5955,14 +5955,14 @@ func main()->Int
     arg n_0
     result_0 = 1
     i_0 = 0
-    (%t27) = (i_0)
-    %t29 = %t27
+    (i_0_27) = (i_0)
+    i_1_29 = i_0_27
     goto  L2
 L2:
-    (i_1) = (%t29)
+    (i_1) = (i_1_29)
     %t5_0 = i_1<n_0
-    (%t24) = (result_0)
-    %t26 = %t24
+    (result_0_24) = (result_0)
+    result_2_26 = result_0_24
     if %t5_0 goto L3 else goto L4
 L3:
     %t6_0 = a_0[i_1]
@@ -5971,19 +5971,19 @@ func main()->Int
     if %t8_0 goto L5 else goto L6
 L5:
     result_1 = 0
-    (%t25) = (result_1)
-    %t26 = %t25
+    (result_1_25) = (result_1)
+    result_2_26 = result_1_25
     goto  L4
 L4:
-    (result_2) = (%t26)
+    (result_2) = (result_2_26)
     ret result_2
     goto  L1
 L1:
 L6:
     %t9_0 = i_1+1
     i_2 = %t9_0
-    (%t28) = (i_2)
-    %t29 = %t28
+    (i_2_28) = (i_2)
+    i_1_29 = i_2_28
     goto  L2
 After exiting SSA
 =================
@@ -5993,14 +5993,14 @@ func main()->Int
     arg n_0
     result_0 = 1
     i_0 = 0
-    %t27 = i_0
-    %t29 = %t27
+    i_0_27 = i_0
+    i_1_29 = i_0_27
     goto  L2
 L2:
-    i_1 = %t29
+    i_1 = i_1_29
     %t5_0 = i_1<n_0
-    %t24 = result_0
-    %t26 = %t24
+    result_0_24 = result_0
+    result_2_26 = result_0_24
     if %t5_0 goto L3 else goto L4
 L3:
     %t6_0 = a_0[i_1]
@@ -6009,19 +6009,19 @@ func main()->Int
     if %t8_0 goto L5 else goto L6
 L5:
     result_1 = 0
-    %t25 = result_1
-    %t26 = %t25
+    result_1_25 = result_1
+    result_2_26 = result_1_25
     goto  L4
 L4:
-    result_2 = %t26
+    result_2 = result_2_26
     ret result_2
     goto  L1
 L1:
 L6:
     %t9_0 = i_1+1
     i_2 = %t9_0
-    %t28 = i_2
-    %t29 = %t28
+    i_2_28 = i_2
+    i_1_29 = i_2_28
     goto  L2
 func main
 Before SSA
@@ -6228,13 +6228,13 @@ func eq(a: [Int], b: [Int], n: Int)->Int
     arg n_0
     result_0 = 1
     i_0 = 0
-    (%t27) = (i_0)
+    (i_0_27) = (i_0)
     goto  L2
 L2:
-    %t29 = phi(%t27, %t28)
-    (i_1) = (%t29)
+    i_1_29 = phi(i_0_27, i_2_28)
+    (i_1) = (i_1_29)
     %t5_0 = i_1<n_0
-    (%t24) = (result_0)
+    (result_0_24) = (result_0)
     if %t5_0 goto L3 else goto L4
 L3:
     %t6_0 = a_0[i_1]
@@ -6243,18 +6243,18 @@ func eq(a: [Int], b: [Int], n: Int)->Int
     if %t8_0 goto L5 else goto L6
 L5:
     result_1 = 0
-    (%t25) = (result_1)
+    (result_1_25) = (result_1)
     goto  L4
 L4:
-    %t26 = phi(%t24, %t25)
-    (result_2) = (%t26)
+    result_2_26 = phi(result_0_24, result_1_25)
+    (result_2) = (result_2_26)
     ret result_2
     goto  L1
 L1:
 L6:
     %t9_0 = i_1+1
     i_2 = %t9_0
-    (%t28) = (i_2)
+    (i_2_28) = (i_2)
     goto  L2
 After removing phis from CSSA
 L0:
@@ -6263,14 +6263,14 @@ func eq(a: [Int], b: [Int], n: Int)->Int
     arg n_0
     result_0 = 1
     i_0 = 0
-    (%t27) = (i_0)
-    %t29 = %t27
+    (i_0_27) = (i_0)
+    i_1_29 = i_0_27
     goto  L2
 L2:
-    (i_1) = (%t29)
+    (i_1) = (i_1_29)
     %t5_0 = i_1<n_0
-    (%t24) = (result_0)
-    %t26 = %t24
+    (result_0_24) = (result_0)
+    result_2_26 = result_0_24
     if %t5_0 goto L3 else goto L4
 L3:
     %t6_0 = a_0[i_1]
@@ -6279,19 +6279,19 @@ func eq(a: [Int], b: [Int], n: Int)->Int
     if %t8_0 goto L5 else goto L6
 L5:
     result_1 = 0
-    (%t25) = (result_1)
-    %t26 = %t25
+    (result_1_25) = (result_1)
+    result_2_26 = result_1_25
     goto  L4
 L4:
-    (result_2) = (%t26)
+    (result_2) = (result_2_26)
     ret result_2
     goto  L1
 L1:
 L6:
     %t9_0 = i_1+1
     i_2 = %t9_0
-    (%t28) = (i_2)
-    %t29 = %t28
+    (i_2_28) = (i_2)
+    i_1_29 = i_2_28
     goto  L2
 After exiting SSA
 =================
@@ -6301,14 +6301,14 @@ func eq(a: [Int], b: [Int], n: Int)->Int
     arg n_0
     result_0 = 1
     i_0 = 0
-    %t27 = i_0
-    %t29 = %t27
+    i_0_27 = i_0
+    i_1_29 = i_0_27
     goto  L2
 L2:
-    i_1 = %t29
+    i_1 = i_1_29
     %t5_0 = i_1<n_0
-    %t24 = result_0
-    %t26 = %t24
+    result_0_24 = result_0
+    result_2_26 = result_0_24
     if %t5_0 goto L3 else goto L4
 L3:
     %t6_0 = a_0[i_1]
@@ -6317,19 +6317,19 @@ func eq(a: [Int], b: [Int], n: Int)->Int
     if %t8_0 goto L5 else goto L6
 L5:
     result_1 = 0
-    %t25 = result_1
-    %t26 = %t25
+    result_1_25 = result_1
+    result_2_26 = result_1_25
     goto  L4
 L4:
-    result_2 = %t26
+    result_2 = result_2_26
     ret result_2
     goto  L1
 L1:
 L6:
     %t9_0 = i_1+1
     i_2 = %t9_0
-    %t28 = i_2
-    %t29 = %t28
+    i_2_28 = i_2
+    i_1_29 = i_2_28
     goto  L2
 """, result);
     }

From de34763b1e7df70ce91f2a3801df6d8921f9e4e9 Mon Sep 17 00:00:00 2001
From: dibyendumajumdar <mobile@majumdar.org.uk>
Date: Mon, 18 Aug 2025 00:33:21 +0100
Subject: [PATCH 119/119] Document the order of traversal for liveness and make
 it also more explicit in the method names.

---
 .../ezlang/compiler/BBHelper.java             | 40 +++++++++++++++++--
 .../ezlang/compiler/Liveness.java             | 11 +++--
 2 files changed, 45 insertions(+), 6 deletions(-)

diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/BBHelper.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/BBHelper.java
index c20521d..b8d8f35 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/BBHelper.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/BBHelper.java
@@ -11,17 +11,51 @@ public class BBHelper {
      */
     public static List<BasicBlock> findAllBlocks(BasicBlock root) {
         List<BasicBlock> nodes = new ArrayList<>();
-        postOrderWalk(root, (n) -> nodes.add(n), new HashSet<>());
+        postOrderWalkForwardCFG(root, (n) -> nodes.add(n), new HashSet<>());
         return nodes;
     }
 
-    static void postOrderWalk(BasicBlock n, Consumer<BasicBlock> consumer, HashSet<BasicBlock> visited) {
+    public static List<BasicBlock> findAllBlocksPostOrderForwardCFG(CompiledFunction function) {
+        List<BasicBlock> nodes = new ArrayList<>();
+        postOrderWalkForwardCFG(function.entry, (n) -> nodes.add(n), new HashSet<>());
+        return nodes;
+    }
+
+    public static List<BasicBlock> findAllBlocksReversePostOrderForwardCFG(CompiledFunction function) {
+        List<BasicBlock> nodes = new ArrayList<>();
+        postOrderWalkForwardCFG(function.entry, (n) -> nodes.add(0,n), new HashSet<>());
+        return nodes;
+    }
+
+    static void postOrderWalkForwardCFG(BasicBlock n, Consumer<BasicBlock> consumer, HashSet<BasicBlock> visited) {
         visited.add(n);
         /* For each successor node */
         for (BasicBlock s : n.successors) {
             if (!visited.contains(s))
-                postOrderWalk(s, consumer, visited);
+                postOrderWalkForwardCFG(s, consumer, visited);
         }
         consumer.accept(n);
     }
+
+    static void postOrderWalkReverseCFG(BasicBlock n, Consumer<BasicBlock> consumer, HashSet<BasicBlock> visited) {
+        visited.add(n);
+        /* For each successor node */
+        for (BasicBlock s : n.predecessors) {
+            if (!visited.contains(s))
+                postOrderWalkReverseCFG(s, consumer, visited);
+        }
+        consumer.accept(n);
+    }
+
+    public static List<BasicBlock> findAllBlocksPostOrderReverseCFG(CompiledFunction function) {
+        List<BasicBlock> nodes = new ArrayList<>();
+        postOrderWalkReverseCFG(function.exit, (n) -> nodes.add(n), new HashSet<>());
+        return nodes;
+    }
+
+    public static List<BasicBlock> findAllBlocksReversePostOrderReverseCFG(CompiledFunction function) {
+        List<BasicBlock> nodes = new ArrayList<>();
+        postOrderWalkReverseCFG(function.exit, (n) -> nodes.add(0,n), new HashSet<>());
+        return nodes;
+    }
 }
diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Liveness.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Liveness.java
index 4eb56a3..61bd186 100644
--- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Liveness.java
+++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Liveness.java
@@ -22,7 +22,7 @@
  * Computing Liveness Sets for SSA-Form Programs
  * Florian Brandner, Benoit Boissinot, Alain Darte, Benoît Dupont de Dinechin, Fabrice Rastello
  *
- * The implementation is the unoptimized simple one.
+ * The implementation is the unoptimized simple data analysis form.
  * However, we have a modification to ensure that if we see a block
  * which loops to itself and has Phi cycles, then the Phi is only added to
  * PhiDefs.
@@ -30,7 +30,13 @@
 public class Liveness {
 
     public Liveness(CompiledFunction function) {
-        List<BasicBlock> blocks = BBHelper.findAllBlocks(function.entry);
+        // EaC states that it is most efficient to do RPO on reverse CFG.
+        // The problem is that if there are infinite loops, we will not visit all basic blocks
+        // if we started at the exit block (this could be solved by adding artificial edges from infinite loop
+        // to exit block, but we do not do that yet).
+        // For a forward CFG traversal, we cannot do RPO as this is a backward dataflow
+        // problem, i.e. successors must be processed first.
+        List<BasicBlock> blocks = BBHelper.findAllBlocksPostOrderForwardCFG(function);
         RegisterPool regPool = function.registerPool;
         initBlocks(regPool, blocks);
         init(blocks);
@@ -110,7 +116,6 @@ private void computeLiveness(List<BasicBlock> blocks) {
         boolean changed = true;
         while (changed) {
             changed = false;
-            // TODO we should process in RPO order
             for (BasicBlock block : blocks) {
                 if (recomputeLiveOut(block))
                     changed = true;
