diff options
| author | Cliff Click <cliffc@acm.org> | 2025-01-07 15:31:54 -0800 |
|---|---|---|
| committer | Cliff Click <cliffc@acm.org> | 2025-01-15 15:07:30 -0800 |
| commit | e0d7b4f8ef0e886f8efae62b77402007e6b2476c (patch) | |
| tree | 042c6df4fc96b6f3bd128f03f1cfda4c1f66704e /chapter18/src | |
| parent | c2169787d92da57aa0e11cd026e398b3d5fcda12 (diff) | |
| download | Simple-e0d7b4f8ef0e886f8efae62b77402007e6b2476c.tar.gz Simple-e0d7b4f8ef0e886f8efae62b77402007e6b2476c.zip | |
Forward Ref type to Bottom
So FRefs can strictly lift when defined.
Parser can support forward refs in more cases, by upcasting now and proving the cast is correct later.
Monotonic for some float ops.
Diffstat (limited to 'chapter18/src')
9 files changed, 96 insertions, 22 deletions
diff --git a/chapter18/src/main/java/com/seaofnodes/simple/Parser.java b/chapter18/src/main/java/com/seaofnodes/simple/Parser.java index 2d98287d..d888f17e 100644 --- a/chapter18/src/main/java/com/seaofnodes/simple/Parser.java +++ b/chapter18/src/main/java/com/seaofnodes/simple/Parser.java @@ -651,7 +651,7 @@ public class Parser { // Auto-narrow wide ints to narrow ints expr = zsMask(expr,t); // Type is sane - if( !expr._type.isa(t) ) + if( expr._type!=Type.BOTTOM && !expr._type.isa(t) ) throw error("Type " + expr._type.str() + " is not of declared type " + t.str()); return expr; } @@ -711,14 +711,16 @@ public class Parser { if( inferType && !_scope.inCon() ) throw errorSyntax("=expression"); // Initial value for uninitialized struct fields. - expr = t instanceof TypeMemPtr tn + expr = switch( t ) { // Nullable pointers get a NIL; not-null get a TOP which // signals that they *must* be initialized in the constructor. - ? (tn.nullable() ? NIL : con(Type.TOP)) - // Bottom signals type inference: they must be initialized in - // the constructor and that's when we'll discover the type. - // Otherwise, its an Integer and use 0. - : (t==Type.BOTTOM ? con(t) : (t instanceof TypeInteger ? ZERO : con(TypeFloat.FZERO))); + case TypeNil tn -> tn.nullable() ? NIL : con(Type.TOP); + case TypeInteger ti -> ZERO; + case TypeFloat tf -> con(TypeFloat.FZERO); + // Bottom signals type inference: they must be initialized in + // the constructor and that's when we'll discover the type. + case Type tt -> { assert tt==Type.BOTTOM; yield con(tt); } + }; } // Lift expression, based on type @@ -727,6 +729,10 @@ public class Parser { if( xfinal && t instanceof TypeMemPtr tmp ) t = tmp.makeRO(); + // Lift type to the declaration. This will report as an error later if + // we cannot lift the type. + if( !lift._type.isa(t) ) + lift = peep(new CastNode(t,null,lift)); // Define a new name if( !_scope.define(name,t,xfinal,lift, loc) ) throw error("Redefining name '" + name + "'", loc); @@ -1356,7 +1362,7 @@ public class Parser { private Node functionCall(Node expr) { if( expr._type == Type.NIL ) throw error("Calling a null function pointer"); - if( !expr._type.isa(TypeFunPtr.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 diff --git a/chapter18/src/main/java/com/seaofnodes/simple/node/CastNode.java b/chapter18/src/main/java/com/seaofnodes/simple/node/CastNode.java index 98d0ac71..d8a968c1 100644 --- a/chapter18/src/main/java/com/seaofnodes/simple/node/CastNode.java +++ b/chapter18/src/main/java/com/seaofnodes/simple/node/CastNode.java @@ -1,5 +1,7 @@ package com.seaofnodes.simple.node; +import com.seaofnodes.simple.Parser; +import com.seaofnodes.simple.Utils; import com.seaofnodes.simple.type.Type; import com.seaofnodes.simple.type.TypeInteger; import java.util.BitSet; @@ -45,4 +47,12 @@ public class CastNode extends Node { @Override int hash() { return _t.hashCode(); } + + @Override + public Parser.ParseException 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/chapter18/src/main/java/com/seaofnodes/simple/node/FRefNode.java b/chapter18/src/main/java/com/seaofnodes/simple/node/FRefNode.java index beb14870..f0c4045d 100644 --- a/chapter18/src/main/java/com/seaofnodes/simple/node/FRefNode.java +++ b/chapter18/src/main/java/com/seaofnodes/simple/node/FRefNode.java @@ -11,7 +11,7 @@ import java.util.BitSet; * peepholes to the Def. */ public class FRefNode extends ConstantNode { - public static final Type FREF_TYPE = Type.TOP; + public static final Type FREF_TYPE = Type.BOTTOM; public final Var _n; public FRefNode( Var n ) { super(FREF_TYPE); _n = n; } diff --git a/chapter18/src/main/java/com/seaofnodes/simple/node/StoreNode.java b/chapter18/src/main/java/com/seaofnodes/simple/node/StoreNode.java index 576f8d92..ccabadb4 100644 --- a/chapter18/src/main/java/com/seaofnodes/simple/node/StoreNode.java +++ b/chapter18/src/main/java/com/seaofnodes/simple/node/StoreNode.java @@ -43,6 +43,7 @@ public class StoreNode extends MemOpNode { 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 ) { diff --git a/chapter18/src/main/java/com/seaofnodes/simple/node/ToFloatNode.java b/chapter18/src/main/java/com/seaofnodes/simple/node/ToFloatNode.java index 0661ac2c..1f4d67e0 100644 --- a/chapter18/src/main/java/com/seaofnodes/simple/node/ToFloatNode.java +++ b/chapter18/src/main/java/com/seaofnodes/simple/node/ToFloatNode.java @@ -21,8 +21,11 @@ public class ToFloatNode extends Node { @Override public Type compute() { if( in(1)._type == Type.NIL ) return TypeFloat.FZERO; - if (in(1)._type instanceof TypeInteger i0 && i0.isConstant() ) - return TypeFloat.constant(i0.value()); + if (in(1)._type instanceof TypeInteger i0 ) { + if( i0.isHigh() ) return TypeFloat.F64.dual(); + if( i0.isConstant() ) + return TypeFloat.constant(i0.value()); + } return TypeFloat.F64; } diff --git a/chapter18/src/main/java/com/seaofnodes/simple/type/TypeFloat.java b/chapter18/src/main/java/com/seaofnodes/simple/type/TypeFloat.java index 258cda03..aa205149 100644 --- a/chapter18/src/main/java/com/seaofnodes/simple/type/TypeFloat.java +++ b/chapter18/src/main/java/com/seaofnodes/simple/type/TypeFloat.java @@ -47,6 +47,7 @@ public class TypeFloat extends Type { } 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; } diff --git a/chapter18/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java b/chapter18/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java index 78a94dff..5799fea9 100644 --- a/chapter18/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java +++ b/chapter18/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java @@ -63,7 +63,7 @@ public class TypeFunPtr extends TypeNil { @Override 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); + return TypeFunPtr.make(xmeet0(that),(TypeTuple)_sig.meet(that._sig), _ret.meet(that._ret), _fidxs | that._fidxs); } @Override @@ -94,8 +94,9 @@ public class TypeFunPtr extends TypeNil { public TypeFunPtr setName(String name) { if( name==null ) return this; - assert _name==null || _name.equals(name); assert _fidxs > 0 && Long.bitCount(_fidxs) == 1; + // Name can be set to different things, just for debug + //assert _name==null || _name.equals(name); _name = name; return this; } @@ -109,9 +110,11 @@ public class TypeFunPtr extends TypeNil { sb.p(x()).p("{ "); if( n && _name!=null ) sb.p(_name); else { - for( Type t : _sig._types ) - _print(sb,g,t).p(" "); + if( _sig._types!=null ) + for( Type t : _sig._types ) + _print(sb,g,t).p(" "); _print(sb.p(g ? "→ " : "-> "),g,_ret).p(" #"); + if( isHigh() ) sb.p("~"); long fidxs = isHigh() ? ~_fidxs : _fidxs; String fidx = fidxs==0 ? "" : Long.bitCount(fidxs) == 1 ? ""+Long.numberOfTrailingZeros(fidxs) diff --git a/chapter18/src/test/java/com/seaofnodes/simple/Chapter18Test.java b/chapter18/src/test/java/com/seaofnodes/simple/Chapter18Test.java index 8f8d15a1..9e74ce6d 100644 --- a/chapter18/src/test/java/com/seaofnodes/simple/Chapter18Test.java +++ b/chapter18/src/test/java/com/seaofnodes/simple/Chapter18Test.java @@ -11,14 +11,29 @@ public class Chapter18Test { public void testJig() { CodeGen code = new CodeGen( """ -if( arg ? f : g ) return 1; -val f = {->1}; -val g = {->2}; -return 2; +return 0; +"""); + code.parse().opto().typeCheck().GCM(); + assertEquals("return 0;", code._stop.toString()); + assertEquals("0", Eval2.eval(code, 0)); + } + + @Test + public void testJig2() { + CodeGen code = new CodeGen( +""" +struct S { S? next; }; +val f = { int dep, S s -> + if (dep-- == 0) return s; + S? v = f(dep, s); + return v ? v.next : null; +}; +return f(1, new S); """); code.parse().opto().typeCheck().GCM(); - assertEquals("return 0.0f;", code._stop.toString()); - assertEquals("0.0", Eval2.eval(code, 0)); + assertEquals("Stop[ return f( 1,S); return Phi(Region,Parm_s(f,*S {*S? !next; },S,s),Phi(Region,.next,null)); ]", code._stop.toString()); + assertEquals("null", Eval2.eval(code, 0)); + assertEquals("null", Eval2.eval(code, 1)); } @Test @@ -149,6 +164,22 @@ return newS(1).i; assertEquals("1", Eval2.eval(code, 0)); } + // Double forward reference + @Test + public void testFcn7() { + CodeGen code = new CodeGen( +""" +if( arg ? f : g ) return 1; +val f = {->1;}; +val g = {->2;}; +return 2; +"""); + code.parse().opto().typeCheck().GCM(); + assertEquals("Stop[ return 1; return 1; return 2; ]", code._stop.toString()); + assertEquals("1", Eval2.eval(code, 0)); + } + + // Function break @Test public void testErr1() { @@ -211,4 +242,23 @@ return is_even(arg); assertEquals("0", Eval2.eval(code, 3)); } + // Forward ref to 'x' means that + @Ignore @Test + public void testForwardRef1() { + CodeGen code = new CodeGen( +""" +struct S { + { int } f = { -> return x(); }; +}; +val x = { -> return 1; }; +S? s = null; +for(;;) { + if (s) return s.f; +} +"""); + code.parse().opto().typeCheck().GCM(); + assertEquals("return 0;", code._stop.toString()); + assertEquals("0", Eval2.eval(code, 0)); + } + } diff --git a/chapter18/src/test/java/com/seaofnodes/simple/Eval2.java b/chapter18/src/test/java/com/seaofnodes/simple/Eval2.java index e68c8fd3..006034e9 100644 --- a/chapter18/src/test/java/com/seaofnodes/simple/Eval2.java +++ b/chapter18/src/test/java/com/seaofnodes/simple/Eval2.java @@ -224,7 +224,7 @@ public abstract class Eval2 { F.dec(); // Lower ref count // Install replacement Frame; set return result over the CallEnd F = cont._frame; - F.put(cont._cc,rez); // Set return value into CallEnd + F.put0(cont._cc,rez); // Set return value into CallEnd return cont._cc; // Return CallEnd as the new control } |
