From 1e28b27ec2d1129d4a219a57ec0c661c74720493 Mon Sep 17 00:00:00 2001 From: Cliff Click Date: Sun, 13 Apr 2025 10:23:18 -0700 Subject: Handle infinite loops Loop tree building (and breaking infinite loops) needs to happen *before* instruction selection, which is optional. Best done as a new compiler phase... which now wants a driver. Add a compiler driver; adjust tests to use. Add "never-branch" handling to the 3 CPU port main branch ops. --- .../com/seaofnodes/simple/codegen/CodeGen.java | 41 +++++++++++++++++++--- .../java/com/seaofnodes/simple/node/CFGNode.java | 2 +- .../java/com/seaofnodes/simple/node/IfNode.java | 4 ++- .../java/com/seaofnodes/simple/node/LoopNode.java | 2 +- .../java/com/seaofnodes/simple/node/NeverNode.java | 1 + .../java/com/seaofnodes/simple/node/PhiNode.java | 1 + .../seaofnodes/simple/node/cpus/arm/BranchARM.java | 21 ++++++++--- .../seaofnodes/simple/node/cpus/arm/FunARM.java | 11 +++--- .../seaofnodes/simple/node/cpus/arm/SplitARM.java | 8 +++++ .../com/seaofnodes/simple/node/cpus/arm/arm.java | 8 +++-- .../simple/node/cpus/riscv/BranchRISC.java | 33 ++++++++++++----- .../seaofnodes/simple/node/cpus/riscv/FunRISC.java | 11 +++--- .../simple/node/cpus/riscv/SplitRISC.java | 7 ++++ .../seaofnodes/simple/node/cpus/riscv/riscv.java | 2 +- .../simple/node/cpus/x86_64_v2/FunX86.java | 3 +- .../simple/node/cpus/x86_64_v2/JmpX86.java | 32 ++++++++++++----- .../simple/node/cpus/x86_64_v2/x86_64_v2.java | 26 +++++--------- 17 files changed, 154 insertions(+), 59 deletions(-) (limited to 'chapter21/src/main/java') diff --git a/chapter21/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java b/chapter21/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java index 114d0b5c..fb46a6a2 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java @@ -20,6 +20,7 @@ public class CodeGen { 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 @@ -49,6 +50,28 @@ public class CodeGen { P = new Parser(this,arg); } + + // 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 @@ -172,18 +195,30 @@ public class CodeGen { 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; // 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; @@ -261,8 +296,6 @@ public class CodeGen { _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 ) diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/CFGNode.java b/chapter21/src/main/java/com/seaofnodes/simple/node/CFGNode.java index 0b97c574..0aeee5f3 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/node/CFGNode.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/node/CFGNode.java @@ -57,7 +57,7 @@ public abstract class CFGNode extends Node { // 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; diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/IfNode.java b/chapter21/src/main/java/com/seaofnodes/simple/node/IfNode.java index f00f4f9f..9bb269d5 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/node/IfNode.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/node/IfNode.java @@ -21,7 +21,9 @@ public class IfNode extends CFGNode implements MultiNode { @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); } diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/LoopNode.java b/chapter21/src/main/java/com/seaofnodes/simple/node/LoopNode.java index 778f2a8d..847a4aa0 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/node/LoopNode.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/node/LoopNode.java @@ -51,7 +51,7 @@ public class LoopNode extends RegionNode { : 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,t); + setDef(2,t); // True continues loop, False (never) exits loop iff._ltree = t._ltree = _ltree; ReturnNode ret = fun.ret(); f._ltree = ret._ltree; diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/NeverNode.java b/chapter21/src/main/java/com/seaofnodes/simple/node/NeverNode.java index 1dcad1db..516f218b 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/node/NeverNode.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/node/NeverNode.java @@ -9,6 +9,7 @@ import java.util.BitSet; // "Never true" for infinite loop exits public class NeverNode extends IfNode { + public NeverNode(NeverNode ctrl) { super(ctrl); } public NeverNode(Node ctrl) { super(ctrl,null); } @Override public String label() { return "Never"; } diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/PhiNode.java b/chapter21/src/main/java/com/seaofnodes/simple/node/PhiNode.java index be31dd38..f0bf1166 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/node/PhiNode.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/node/PhiNode.java @@ -90,6 +90,7 @@ public class PhiNode extends Node { { solo=false; break; } if( solo ) { Node cp = op.copy(); + cp._type = null; // Fresh type cp.addDef(null); // No control for( int j=1; j" -> 0x8F; case "<" -> 0x8C; case "<=" -> 0x8E; - case ">=" -> 0X8D; + case ">=" -> 0x8D; default -> throw new IllegalArgumentException("Too many arguments"); }; } @@ -119,14 +117,6 @@ public class x86_64_v2 extends Machine { 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); } @@ -421,10 +411,10 @@ public class x86_64_v2 extends Machine { 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 ConstantNode con -> con(con); case DivFNode divf -> new DivFX86(divf); case DivNode div -> new DivX86(div); @@ -451,6 +441,7 @@ public class x86_64_v2 extends Machine { case SubFNode subf -> new SubFX86(subf); case SubNode sub -> sub(sub); case ToFloatNode tfn -> i2f8(tfn); + case XCtrlNode x -> new ConstantNode(Type.XCONTROL); case XorNode xor -> xor(xor); case LoopNode loop -> new LoopNode(loop); @@ -460,7 +451,7 @@ public class x86_64_v2 extends Machine { } 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. @@ -595,10 +586,11 @@ public class x86_64_v2 extends Machine { // 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(TypeInteger.ZERO))); - return new JmpX86(iff, bool.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(TypeInteger.ZERO))); + return new JmpX86(iff, op); } private Node ld(LoadNode ld) { -- cgit v1.2.3