From b59828ac09a88310640d8b8d3d95a8be0cee1187 Mon Sep 17 00:00:00 2001 From: Cliff Click Date: Sun, 29 Oct 2023 13:09:14 -0700 Subject: Rethink & cleanup Add peeps More rigorously head for a left-spline sum-of-products format. --- .../java/com/seaofnodes/simple/node/AddNode.java | 100 +++++++++++++++------ .../main/java/com/seaofnodes/simple/node/Node.java | 29 +++++- .../java/com/seaofnodes/simple/node/PhiNode.java | 32 ++++++- 3 files changed, 133 insertions(+), 28 deletions(-) (limited to 'chapter05/src/main') diff --git a/chapter05/src/main/java/com/seaofnodes/simple/node/AddNode.java b/chapter05/src/main/java/com/seaofnodes/simple/node/AddNode.java index a726a22d..7cb0f822 100644 --- a/chapter05/src/main/java/com/seaofnodes/simple/node/AddNode.java +++ b/chapter05/src/main/java/com/seaofnodes/simple/node/AddNode.java @@ -36,42 +36,92 @@ public class AddNode extends Node { Type t1 = lhs._type; Type t2 = rhs._type; + // Already handled by peephole constant folding + assert !(t1.isConstant() && t2.isConstant()); + // Add of 0. We do not check for (0+x) because this will already // canonicalize to (x+0) - if ( t2.isConstant() && t2 instanceof TypeInteger i && i.value()==0 ) + if( t2 instanceof TypeInteger i && i.value()==0 ) return lhs; - - // Move constants to RHS: con+arg becomes arg+con - if ( t1.isConstant() && !t2.isConstant() ) { - Node tmp = in(1); // Swap inputs without letting either input go dead during the swap - _inputs.set(1,in(2)); - _inputs.set(2,tmp); - return this; - } - + + // Add of same to a multiply by 2 + if( lhs==rhs ) + return new MulNode(lhs,new ConstantNode(TypeInteger.constant(2)).peephole()); + // Goal: a left-spine set of adds, with constants on the rhs (which then fold). - // Do we have (x + con1) + con2 ? - // Rotate to x +(con1 + con2) - // The constants will fold on the next peephole. - if (t2.isConstant() && lhs instanceof AddNode && lhs.in(2)._type.isConstant() ) + // Move non-adds to RHS + if( !(lhs instanceof AddNode) && rhs instanceof AddNode ) + return swap12(); + + // 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) ) + return spline_cmp(lhs,rhs) ? swap12() : null; + + // Now we only see (add add non) + + // Do we have (x + con1) + con2? + // Replace with (x + (con1+con2) which then fold the constants + if( lhs.in(2)._type.isConstant() && t2.isConstant() ) return new AddNode(lhs.in(1),new AddNode(lhs.in(2),rhs).peephole()); - // Do we have x + (y + non_add) ? - // Swap to (x + y) + non_add - if (rhs instanceof AddNode add && !(add.in(2) instanceof AddNode) ) - return new AddNode(new AddNode(lhs,add.in(1)).peephole(), add.in(2)); + if( lhs.in(2) instanceof PhiNode phi && phi.all_cons() && + // 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...) + + // Note that this is the exact reverse of Phi pulling a common op + // down to reduce total op-count. We don't get in a endless push- + // up push-down peephole cycle because the constants all fold first. + (t2.isConstant() || (rhs instanceof PhiNode && rhs.all_cons()) ) ) { + Node[] ns = new Node[phi.nIns()]; + ns[0] = phi.in(0); + // Push constant up through the phi: x + (phi con0+con0 con1+con1...) + for( int i=1; i hi._nid; + } + + @Override + Node copy(Node lhs, Node rhs) { return new AddNode(lhs,rhs); } } diff --git a/chapter05/src/main/java/com/seaofnodes/simple/node/Node.java b/chapter05/src/main/java/com/seaofnodes/simple/node/Node.java index 49426c26..dc4d7a33 100644 --- a/chapter05/src/main/java/com/seaofnodes/simple/node/Node.java +++ b/chapter05/src/main/java/com/seaofnodes/simple/node/Node.java @@ -215,7 +215,6 @@ public abstract class Node { return new_def; } - // Breaks the edge invariants, used temporarily private void addUse(Node n) { _outputs.add(n); } @@ -258,7 +257,7 @@ public abstract class Node { // Mostly used for asserts and printing. boolean isDead() { return isUnused() && nIns()==0 && _type==null; } - + /** * This function needs to be * Monotonic @@ -324,6 +323,32 @@ public abstract class Node { */ public abstract Node idealize(); + + // ----------------------- + // Peephole utilities + + // Swap inputs without letting either input go dead during the swap. + Node swap12() { + Node tmp = in(1); + _inputs.set(1,in(2)); + _inputs.set(2,tmp); + return this; + } + + // does this node contain all constants? + // Ignores in(0), as is usually control. + boolean all_cons() { + for( int i=1; i