diff options
| author | Cliff Click <cliffc@acm.org> | 2026-10-03 14:03:48 -0700 |
|---|---|---|
| committer | Cliff Click <cliffc@acm.org> | 2026-10-03 14:03:48 -0700 |
| commit | 22df891893d30e9906d0b14fc3ae5d8e67d500b2 (patch) | |
| tree | 2bda142f92febc8b26f1cabd4bb0c46d1864f34b /chapter25 | |
| parent | c3958e34c0de21df52e57e47e4f47ffe6464f138 (diff) | |
| download | Simple-22df891893d30e9906d0b14fc3ae5d8e67d500b2.tar.gz Simple-22df891893d30e9906d0b14fc3ae5d8e67d500b2.zip | |
Chapter renumber, and split ch 17
Renumber chapters 10a, 10b, 11, 12, 13, 14 -> 10, 11, 13, 12, [11,14].
Split ch 17 into a 17a and 17b; shuffle around im/mutable syntax to be consistent, and present it more slowly.
THe new immutable syntax is pushed through all later chapters
Diffstat (limited to 'chapter25')
56 files changed, 1021 insertions, 897 deletions
diff --git a/chapter25/Makefile b/chapter25/Makefile index 980342c8..aa7f0a17 100644 --- a/chapter25/Makefile +++ b/chapter25/Makefile @@ -71,7 +71,7 @@ include $(firstword $(wildcard ../isa/isa.mk isa/isa.mk)) test_classes := $(patsubst $(TST)/%java,$(CLZDIR)/test/%class,$(test_javas)) # These tests do not rely on the stdlib test_alone := com.seaofnodes.simple.BrainFuckTest com.seaofnodes.simple.MergeSortTest com.seaofnodes.simple.TypeTest -test_raw_nums0 := 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 +test_raw_nums0 := 01 02 03 04 05 06 07 08 09 10 12 13 14Float 14 15 16 17a 17b 18 19 20 21 test_raw_nums1 := 22 23 24 # These tests DO rely on the stdlib test_raw_sys := 25 diff --git a/chapter25/README.md b/chapter25/README.md index 3261637c..7ab7c89e 100644 --- a/chapter25/README.md +++ b/chapter25/README.md @@ -156,6 +156,10 @@ regressions. `make release` builds the compiler jar and native library artifacts `make tags` builds editor tags. Native tools and the selected CPU/ABI must match the environment; the Makefile currently defaults to x86-64/win64. +Serialized IR now uses the `C0D2` header: pointer types include an independent +read-only access bit. Older object files must be rebuilt; the reader rejects +their old header rather than interpreting their types with the new layout. + Compiler changes can invalidate both serialized IR and native code in `sys.o`. Rebuild it before interpreting linked-program test results. A source-only subset is not equivalent to this chapter's full `make tests`. diff --git a/chapter25/ROADMAP.md b/chapter25/ROADMAP.md index 981f65e3..30495a69 100644 --- a/chapter25/ROADMAP.md +++ b/chapter25/ROADMAP.md @@ -120,11 +120,11 @@ Any number of named fields are allowed: ```java struct Class { - str className; - str professor; + !str !className; + !str !professor; int time; int credits; - Class prerequisite; + !Class !prerequisite; u16 #; // Limit of 65535 students per class Student []; }; @@ -195,7 +195,7 @@ new object, only growing as needed. ```java val vec1 = new vecInt[0].add(2); // len=1, capacity=1 -val vec2 = vec1.add(3); // NEW vecInt returned, only is deleted; len=2; capacity=2; +val vec2 = vec1.add(3); // NEW !vecInt !returned, only is deleted; len=2; capacity=2; vec1[0]; // ERROR, vec1 has been freed by call to `vec1.add(3)` vec2[0]; // OK vec2[1]; // OK @@ -284,7 +284,7 @@ extra indirection with each usage. ```java struct xstr { - str !_str; // The extra indirection is here + !str !_str; // The extra indirection is here val add = { str str -> _str = str.add2(str); }; val write = { int fd -> _str.write(fd); }; }; @@ -355,7 +355,7 @@ struct Complex { f64 x,y; val len = { ->Math.sqrt(x*x+y*y); }; }; // Current Simple rules: always by-reference struct ByRef { - Complex c; // 4-byte pointer to a Complex + !Complex !c; // 4-byte pointer to a Complex }; print(new ByRef.c.y); // Lookup requires 1 extra memory load from ref to c @@ -363,7 +363,7 @@ print(new ByRef.c.y); // Lookup requires 1 extra memory load from ref to c // The '*' syntax indicator can be something else, e.g. a keyword "inline" // I am pronouncing '*' as "contents of" struct ByValue { - *Complex c; // c is inlined, full 16 bytes into ByValue + *!Complex !c; // c is inlined, full 16 bytes into ByValue }; print(new ByValue.c.y); // x,y inlined into ByValue, no extra memory load @@ -382,7 +382,7 @@ val *c = val.c; // Error, cannot take "contents of" a value ref.c = val.*c; // Assign into ref.c the "contents of" val.c // Arrays of inlined structures -var ary = new *Complex[99]; // Array of 99 Complex objects, inlined +var ary = new *Complex[99]; // Array of 99 !Complex !objects, inlined // Calling methods has the same syntax ref.c.len(); diff --git a/chapter25/docs/examples/BubbleSort.smp b/chapter25/docs/examples/BubbleSort.smp index 1b3b83e6..ca6906b5 100644 --- a/chapter25/docs/examples/BubbleSort.smp +++ b/chapter25/docs/examples/BubbleSort.smp @@ -4,7 +4,7 @@ // // Bubble sort, with reading (parsing) and writing (formatting) the data. -val sort = { sys.aryi64 nums -> +val sort = { !sys.aryi64 nums -> sys.aryi64?! more = nums; while( more ) { more = null; diff --git a/chapter25/docs/examples/Dijkstra.smp b/chapter25/docs/examples/Dijkstra.smp index d2f1dc4f..fd595eb5 100644 --- a/chapter25/docs/examples/Dijkstra.smp +++ b/chapter25/docs/examples/Dijkstra.smp @@ -2,7 +2,7 @@ // An implementation of // https://sampleprograms.io/projects/dijkstra/ -val shortestPath = { sys.aryi64 matrix, int nodes, int src, int dst -> +val shortestPath = { !sys.aryi64 matrix, int nodes, int src, int dst -> i64 infinity = (1 << 63)-1; var dist = new i64[nodes]; var visited = new sys.adt.bitset(); diff --git a/chapter25/src/main/java/com/seaofnodes/simple/Parser.java b/chapter25/src/main/java/com/seaofnodes/simple/Parser.java index 19894357..42c83f93 100644 --- a/chapter25/src/main/java/com/seaofnodes/simple/Parser.java +++ b/chapter25/src/main/java/com/seaofnodes/simple/Parser.java @@ -217,13 +217,17 @@ public class Parser { * Parses a function body, assuming the header is parsed. */ private ReturnNode parseFunctionBody( TypeFunPtr sig, Lexer loc, String[] ids) { + return parseFunctionBody(sig, loc, null, ids); + } + + private ReturnNode parseFunctionBody( TypeFunPtr sig, Lexer loc, java.util.BitSet finals, String[] ids) { // Record & restore the existing scope vars. The function parse might // update fields, so when the parse is done reset back to the pre-parse // state. Node[] preParse = _scope.save(); // Parse function body normally - ReturnNode ret = _parseFunctionBody(null, sig,loc,ids); + ReturnNode ret = _parseFunctionBody(null, sig,loc,finals,ids); // Reset all fields to pre-parse days _scope.restore(preParse); @@ -246,6 +250,10 @@ public class Parser { // Unstack the {break,continue,return} scopes. private ReturnNode _parseFunctionBody( String funName, TypeFunPtr sig, Lexer loc, String[] ids) { + return _parseFunctionBody(funName, sig, loc, null, ids); + } + + private ReturnNode _parseFunctionBody( String funName, TypeFunPtr sig, Lexer loc, java.util.BitSet finals, String[] ids) { boolean isInit = FunNode.isInit(funName); boolean isCtor = isInit || isExplicitConstructorName(funName); String typeName = isCtor ? ((TypeMemPtr)sig._sig[0])._obj._name : null; @@ -278,7 +286,7 @@ public class Parser { for( int i=0; i<ids.length; i++ ) { Type t = sig.arg(i); // Args take a default input if called from Start/external-world - _scope.define(ids[i], t, i==0 && ids[0]=="self", new ParmNode(ids[i],i+2,t,fun,ConstantNode.seed(t).peephole()).peephole(), loc); + _scope.define(ids[i], t, (i==0 && ids[0]=="self") || (finals!=null && finals.get(i)), new ParmNode(ids[i],i+2,t,fun,ConstantNode.seed(t).peephole()).peephole(), loc); } // Parse the function body. @@ -434,7 +442,7 @@ public class Parser { // value; otherwise a user constructor must initialize it. Node val = _returnScope.in(vidx); if( upgrade && v._uninit && - !(val._type instanceof TypeNil tn && tn.notNull() && !val._type.isHigh()) ) continue; + !initialized(val._type) ) continue; // Store value into extended struct Node prior = mmm.keep(); Node st = peep(new StoreNode(null, fld._fname, fld._alias, fld._t, null, prior, self, off(tself,fld._fname), val, fld._final)); @@ -455,6 +463,10 @@ public class Parser { // Validate each user constructor, or an implicit empty one, before the // declaration scope goes away. Later fields use their declaration defaults. + private static boolean initialized(Type t) { + return t!=Type.BOTTOM && !t.isHigh() && (!(t instanceof TypeNil tn) || tn.notNull()); + } + private void checkInitFields(TypeStruct ts, int base) { if( _returnScope==null || _returnScope.ctrl()._type.isHigh() ) return; boolean implicit = _ctorExits.isEmpty(); @@ -467,7 +479,7 @@ public class Parser { Var v = _scope.var(idx); if( !v._uninit || v._fref ) continue; Type t = exit.getOrDefault(v,_returnScope.in(idx)._type); - if( !(t instanceof TypeNil tn && tn.notNull() && !t.isHigh()) ) + if( !initialized(t) ) throw error("'"+ts._name+"' is not fully initialized, field '"+fld._fname+ (implicit ? "' needs to be set in a constructor" : "' is only partially set in the constructor"),v._loc); @@ -1025,6 +1037,10 @@ public class Parser { // Widen ints to floats; narrow wide int types. // Early error if types do not match variable. private Node convertExpr( Node expr, Type t ) { + if( !expr._type.accessISA(t) ) + throw error("Type "+expr._type.str()+" is not of declared type "+t.str()); + if( t instanceof TypeMemPtr tmp && tmp.isFinal() ) + expr = peep(new ReadOnlyNode(expr)); // A FunPtr carries the semantic edge to its function's Return. Do not // hide that edge behind a Convert which can fold to a plain constant. if( expr instanceof FunPtrNode && t instanceof TypeFunPtr ) @@ -1090,17 +1106,24 @@ public class Parser { return require(n,";"); } - /** Parse final: [!]var['=' asgn] + // The binding modifier sits immediately before the name. It never + // changes permissions on the referenced object. + private boolean bindingFinal(Type t) { + if( match("!") ) return false; + if( match("~") ) return true; + return t==Type.TOP || t instanceof TypeNil; + } + + /** Parse final: [!|~]var['=' asgn] */ private Node parseDeclaration(Type t) { assert t!=null; // Has var/val instead of a user-declared type boolean inferType = t==Type.TOP || t==Type.BOTTOM; - boolean hasBang = match("!"); + boolean xfinal = bindingFinal(t); Lexer loc = loc(); String name = requireId(); // Optional initializing expression follows - boolean xfinal = false; boolean fld_final = false; // Field is final, but not deeply final boolean uninit = false; // Required non-null constructor field Node expr; @@ -1121,18 +1144,6 @@ public class Parser { } else { expr = parseAsgn(); } - // TOP means val and val is always final - xfinal = (t==Type.TOP) || - expr instanceof ExternNode || - // BOTTOM is var and var is always not-final - (t!=Type.BOTTOM && - // no Bang AND - !hasBang && - // not-null (expecting null to be set to not-null) - expr._type != Type.NIL && - // Pointers are final by default; int/flt are not-final by default. - (t instanceof TypeNil)); - // var/val, then type comes from expression if( inferType ) { if( expr._type==Type.NIL ) @@ -1141,12 +1152,6 @@ public class Parser { if( !xfinal ) t = mutableInferredType(t); } - // Final is deep on ptrs - if( xfinal && t instanceof TypeMemPtr tmp ) { - t = tmp.makeRO(); - expr = peep(new ReadOnlyNode(expr)); - } - // expr is a constant function if( t instanceof TypeFunPtr decl && expr._type instanceof TypeFunPtr tfp && tfp.isConstant() ) { t = decl.makeFrom(tfp.fidx()); @@ -1168,8 +1173,8 @@ public class Parser { // Required pointers retain their declared shape, but include // null until every constructor path assigns them. case TypeNil tn -> tn.nullable() ? con(Type.NIL) : con(t.meet(Type.NIL)); - case TypeInteger ti -> _code.ZERO; - case TypeFloat tf -> con(TypeFloat.FZERO); + case TypeInteger ti -> xfinal ? con(Type.BOTTOM) : _code.ZERO; + case TypeFloat tf -> con(xfinal ? Type.BOTTOM : 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); } @@ -1177,9 +1182,9 @@ public class Parser { // Every non-null pointer without a default is constructor-required. // Without `!` it is also shallow-final; mutability and definite // initialization are independent properties. - if( t instanceof TypeNil tn && !tn.nullable() ) { + if( (xfinal && !(t instanceof TypeNil)) || (t instanceof TypeNil tn && !tn.nullable()) ) { uninit = true; - if( !hasBang ) fld_final = true; + if( xfinal ) fld_final = true; } } @@ -1430,8 +1435,10 @@ public class Parser { // Otherwise you get a type name int old1 = pos(); + boolean writable = match("!"); + boolean readonly = !writable && match("~"); String tname = _lexer.matchId(); - if( tname==null ) return null; + if( tname==null ) return posT(old1); // Convert the type name to a type. Type t0 = TYPES.get(tname), t1 = t0; @@ -1544,6 +1551,13 @@ public class Parser { if( t1 instanceof TypeStruct ts1 ) t1 = TypeMemPtr.make(ts1); + // A qualifier before the name applies to the struct reference, not + // to an enclosing array. Arrays have their own [] / [~] qualifier. + if( t1 instanceof TypeMemPtr tmp ) { + if( readonly || !writable ) t1 = tmp.makeRO(); + } else if( writable || readonly ) + return posT(old1); + // Nest arrays and '?' as needed Type t2 = t1; while( true ) { @@ -1554,7 +1568,7 @@ public class Parser { } else throw error("Type "+t0+" cannot be null"); } else if( match("[~]") ) { - t2 = TypeMemPtr.make(typeAry(t2,true)); + t2 = TypeMemPtr.make(typeAry(t2,true)).makeRO(); } else if( match("[]") ) { t2 = TypeMemPtr.make(typeAry(t2,false)); } else @@ -1566,7 +1580,7 @@ public class Parser { // Check valid forward ref, after parsing all the type extra bits. // Cannot check earlier, because cannot find required 'id' until after "[]?" syntax int old2 = pos(); - match("!"); + if( !match("!") ) match("~"); String id = _lexer.matchId(); if( !(peek(',') || peek(';') || peek('=') || match("->")) || id==null ) return posT(old1); // Reset lexer to reparse @@ -1616,9 +1630,8 @@ public class Parser { ta = TypeStruct.makeAry(tname,TypeInteger.U32,_code.alias(rcname),t,_code.alias(rcname), false ); TYPES.put(tname,ta); } - if( !efinal ) return ta; // Already have the aliases, just efinal is wrong - return TypeStruct.makeAry(tname,TypeInteger.U32,ta._fields[0]._alias,t,ta._fields[1]._alias,true ); + return TypeStruct.makeAry(tname,TypeInteger.U32,ta._fields[0]._alias,t,ta._fields[1]._alias,efinal ); } private Type implicitSelfType() { @@ -2187,7 +2200,7 @@ public class Parser { // array body. TypeMemPtr str = TypeMemPtr.make((byte)2,TypeStruct.makeAry("[]u8", slen, lenAlias, body, elemAlias, true),true); assert str.isConstant(); - return con(str); + return con(str.makeRO()); } /** @@ -2305,6 +2318,8 @@ public class Parser { Node mem = mem(); // Store to field mem.keep(); + if( expr._type instanceof TypeMemPtr view && view._ro && ctrl()._type!=Type.XCONTROL ) + throw error("Cannot modify final field '"+name+"'"); Node st = new StoreNode(loc(), name, alias, decl, ctrl(), mem, expr.unkeep(), off.unkeep(), lift, false).peephole().keep(); storeMem(st,alias,mem); st.unkeep(); @@ -2330,6 +2345,8 @@ public class Parser { throw error( "'" + ts._name + "' is not fully initialized, field '" + fld._fname + "' needs to be set in a constructor" ); Node op = opAssign(ch,load, decl ).keep(); mem.keep(); + if( expr._type instanceof TypeMemPtr view && view._ro && ctrl()._type!=Type.XCONTROL ) + throw error("Cannot modify final field '"+name+"'"); Node st = new StoreNode(loc(), name, alias, decl, ctrl(), mem, expr.unkeep(), off.unkeep(), op, false).peephole().keep(); storeMem(st,alias,mem); st.unkeep(); @@ -2375,6 +2392,7 @@ public class Parser { private Node func() { Ary<Type> ts = new Ary<>(Type.class); Ary<String> ids = new Ary<>(String.class); + java.util.BitSet finals = new java.util.BitSet(); Type self = implicitSelfType(); ts.push(self); ids.push(self == TypePtr.PTR ? "#self" : "self"); @@ -2385,6 +2403,7 @@ public class Parser { while( true ) { Type t = type(); // Arg type if( t==null ) break; + finals.set(ids.size(),bindingFinal(t)); String id = requireId(); ts .push(t ); // Push type/arg pairs ids.push(id); @@ -2393,7 +2412,7 @@ public class Parser { require("->"); // Make a concrete function type, with a fidx TypeFunPtr tfp = TypeFunPtr.make1((byte)2,true,ts.asAry(),Type.BOTTOM,_code.fidx(_ref._cname)); - ReturnNode ret = parseFunctionBody(tfp,loc,ids.asAry()); + ReturnNode ret = parseFunctionBody(tfp,loc,finals,ids.asAry()); return new FunPtrNode(tfp,_code._start,ret).peephole(); } @@ -2401,6 +2420,7 @@ public class Parser { require("{"); Ary<Type> ts = new Ary<>(Type.class); Ary<String> ids = new Ary<>(String.class); + java.util.BitSet finals = new java.util.BitSet(); TypeStruct self = _scope.constructorSelf(); assert self != null; ts.push(TypeMemPtr.make(constructorRecv(self))); @@ -2416,6 +2436,7 @@ public class Parser { while( true ) { Type t = type(); if( t==null ) break; + finals.set(ids.size(),bindingFinal(t)); String id = requireId(); ts .push(t ); ids.push(id); @@ -2424,7 +2445,7 @@ public class Parser { require("->"); TypeFunPtr tfp = TypeFunPtr.make1((byte)2,true,ts.asAry(),TypeMem.makePrivate(self),_code.fidx(_ref._cname)); Node[] preParse = _scope.save(); - ReturnNode ret = _parseFunctionBody((self._name+"."+name).intern(),tfp,loc,ids.asAry()); + ReturnNode ret = _parseFunctionBody((self._name+"."+name).intern(),tfp,loc,finals,ids.asAry()); _scope.restore(preParse); require("}"); return new FunPtrNode(tfp,_code._start,ret).peephole(); diff --git a/chapter25/src/main/java/com/seaofnodes/simple/codegen/Serialize.java b/chapter25/src/main/java/com/seaofnodes/simple/codegen/Serialize.java index ee5c48df..a8cd315f 100644 --- a/chapter25/src/main/java/com/seaofnodes/simple/codegen/Serialize.java +++ b/chapter25/src/main/java/com/seaofnodes/simple/codegen/Serialize.java @@ -63,8 +63,9 @@ abstract public class Serialize { Type.TAGOFFS(); BAOS baos = new BAOS(); + // C0D2 adds independent pointer access permissions to serialized types. // A - Print a header - baos.write('C').write('0').write('D').write('E'); + baos.write('C').write('0').write('D').write('2'); // Count unique Types var types = new HashMap<Type,Integer>(); @@ -217,8 +218,8 @@ abstract public class Serialize { Type.TAGOFFS(); // A - Read a header - if( bais.read()!='C' || bais.read()!='0' || bais.read()!='D' || bais.read()!='E' ) - throw new IllegalArgumentException("Missing magic word"); + if( bais.read()!='C' || bais.read()!='0' || bais.read()!='D' || bais.read()!='2' ) + throw new IllegalArgumentException("Unsupported serialized IR format; rebuild the object file"); // B - Packed read of #strings, then strings int ndependents = bais.packed4(); // Number of dependent object files @@ -246,8 +247,8 @@ abstract public class Serialize { String[] strs = elf._strs; if( strs == null ) { // A - Read a header - if( bais.read() != 'C' || bais.read() != '0' || bais.read() != 'D' || bais.read() != 'E' ) - throw new IllegalArgumentException( "Missing magic word" ); + if( bais.read() != 'C' || bais.read() != '0' || bais.read() != 'D' || bais.read() != '2' ) + throw new IllegalArgumentException( "Unsupported serialized IR format; rebuild the object file" ); // B - Packed read of #strings, then strings int ndependents = bais.packed4(); // Number of dependent object files diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/CallEndNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/CallEndNode.java index 2e921301..162f9c24 100644 --- a/chapter25/src/main/java/com/seaofnodes/simple/node/CallEndNode.java +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/CallEndNode.java @@ -227,7 +227,7 @@ public class CallEndNode extends CFGNode implements MultiNode { // references here. Compare against its recursively upgraded form, // so deep mutability requirements participate before inlining. Type formal = tfp.arg(i).upgradeType(Parser.TYPES); - if( !arg._type.isa(formal) ) + if( !arg._type.isa(formal) || !arg._type.accessISA(formal) ) { addDep(arg); return false; } } return true; diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/CallNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/CallNode.java index 84f1e705..4372e2d5 100644 --- a/chapter25/src/main/java/com/seaofnodes/simple/node/CallNode.java +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/CallNode.java @@ -187,7 +187,7 @@ public class CallNode extends CFGNode { // Check for args for( int i=0; i<tfp.nargs(); i++ ) - if( !arg(i+2)._type.isa(tfp.arg(i)) ) { + if( !arg(i+2)._type.isa(tfp.arg(i)) || !arg(i+2)._type.accessISA(tfp.arg(i)) ) { // Constructors return private memory and carry hidden self and // selfMem arguments ahead of the source-level arguments. int argno = i - (tfp.ret() instanceof TypeMem ? 2 : 0); diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/ConvertNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/ConvertNode.java index 4ee5521a..b038366a 100644 --- a/chapter25/src/main/java/com/seaofnodes/simple/node/ConvertNode.java +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/ConvertNode.java @@ -35,7 +35,7 @@ public class ConvertNode extends Node { Type src = val()._type; if( CodeGen.CODE._phase == CodeGen.Phase.Opto ) { if( src.isHigh() ) return Type.TOP; - if( src.isa(_dst) ) return src; + if( (src.isa(_dst) && src.accessISA(_dst)) ) return src; } return _dst; } @@ -43,7 +43,7 @@ public class ConvertNode extends Node { @Override public Node idealize() { Type src = val()._type; if( src==Type.BOTTOM || src==Type.TOP ) return null; - if( src.isa(_dst) ) return val(); + if( (src.isa(_dst) && src.accessISA(_dst)) ) return val(); // Managed arrays are represented by an object pointer, while C wants // the address of the first element. The representation cast itself is diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/LoadNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/LoadNode.java index a2345d92..a557f97c 100644 --- a/chapter25/src/main/java/com/seaofnodes/simple/node/LoadNode.java +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/LoadNode.java @@ -85,6 +85,7 @@ public class LoadNode extends MemOpNode { return tmp.isHigh() || tmp._obj.isHigh() ? TypeScalar.TOP : TypeScalar.BOT; Type t = pfld._t; + boolean readonly = t instanceof TypeMemPtr view && view._ro; // Load member of constant array if( t instanceof TypeConAry ary ) t = ary.elem(); // TODO: if offset is known, can peek the constant @@ -93,7 +94,7 @@ public class LoadNode extends MemOpNode { // A deeply read-only base produces a deeply read-only value. The // generic declared field type must not cast this information away. - if( ptr0.isFinal() ) + if( ptr0.isFinal() || readonly ) t = t.makeRO(); return scalar(t); } @@ -306,7 +307,7 @@ public class LoadNode extends MemOpNode { // Read-Only is a deep property, and cannot be cast-away private Node castRO(Node rez) { - if( ptr()._type.isFinal() && !rez._type.isFinal() ) + if( (ptr()._type.isFinal() || declaredType().isFinal()) && !rez._type.isFinal() ) return new ReadOnlyNode(rez).peephole(); return rez; } diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/StoreNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/StoreNode.java index 7e2963f2..415ade34 100644 --- a/chapter25/src/main/java/com/seaofnodes/simple/node/StoreNode.java +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/StoreNode.java @@ -269,7 +269,7 @@ public class StoreNode extends MemOpNode { return null; TypeMemPtr tmp = (TypeMemPtr)ptr()._type; Field f = tmp._obj.field(_name); - if( f!=null && f._final && !_init ) + if( f!=null && (tmp._ro || f._final) && !_init ) return Parser.error("Cannot modify final field '"+_name+"'",_loc); return null; } diff --git a/chapter25/src/main/java/com/seaofnodes/simple/type/Type.java b/chapter25/src/main/java/com/seaofnodes/simple/type/Type.java index d1af6ca8..980daf0f 100644 --- a/chapter25/src/main/java/com/seaofnodes/simple/type/Type.java +++ b/chapter25/src/main/java/com/seaofnodes/simple/type/Type.java @@ -468,6 +468,10 @@ public class Type /*implements Cloneable*/ { boolean _isConstant() { return _type==TNIL; } // Are all reachable struct Fields are final? + // Writable array slots are invariant in their element access permissions. + // Otherwise an alias could insert a readonly pointer into a writable slot. + public boolean accessISA(Type dst) { return true; } + public final boolean isFinal() { return recurClose(recurOpen()._isFinal()); } boolean _isFinal() { assert is_nokids(); return true; } diff --git a/chapter25/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java b/chapter25/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java index cfd213b7..175b1c63 100644 --- a/chapter25/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java +++ b/chapter25/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java @@ -27,12 +27,15 @@ public class TypeMemPtr extends TypeNil { // (TOP ,true ) - a nil public TypeStruct _obj; + // Deep read-only access through this pointer; independent of slot finality. + public boolean _ro; + public boolean _one; // Singleton pointer value; the pointer itself is constant. public boolean _pub; // Escaped/public pointer. False means constructor-private. private static final Ary<TypeMemPtr> FREE = new Ary<>(TypeMemPtr.class); private TypeMemPtr(byte nil, TypeStruct obj, boolean one, boolean pub) { super(TMEMPTR,nil); init(nil,obj,one,pub); } - private TypeMemPtr init(byte nil, TypeStruct obj, boolean one, boolean pub) { _nil=nil; _obj=obj; _one=one; _pub=pub; return this; } + private TypeMemPtr init(byte nil, TypeStruct obj, boolean one, boolean pub) { _nil=nil; _obj=obj; _ro=obj==TypeStruct.BOT; _one=one; _pub=pub; return this; } // Return a filled-in TypeMemPtr; either from free list or alloc new. static TypeMemPtr malloc(byte nil, TypeStruct obj, boolean one, boolean pub) { return FREE.isEmpty() ? new TypeMemPtr(nil,obj,one,pub) : FREE.pop().init(nil,obj,one,pub); @@ -43,7 +46,10 @@ public class TypeMemPtr extends TypeNil { return make(nil,obj,one,true); } public static TypeMemPtr make(byte nil, TypeStruct obj, boolean one, boolean pub) { - TypeMemPtr tmp = malloc(nil, obj, one, pub); + return make(nil,obj,one, pub,obj==TypeStruct.BOT); + } + private static TypeMemPtr make(byte nil, TypeStruct obj, boolean one, boolean pub, boolean ro) { + TypeMemPtr tmp = malloc(nil, obj, one, pub).access(ro); TypeMemPtr t2 = tmp.intern(); if( t2==tmp ) return tmp; return VISIT.isEmpty() ? t2.free(tmp) : t2.delayFree(tmp); @@ -66,15 +72,36 @@ public class TypeMemPtr extends TypeNil { public static TypeMemPtr makeNullable(TypeStruct obj) { return make((byte)3, obj); } public static TypeMemPtr makePrivate (TypeStruct obj) { return make((byte)2, obj, false, false); } - public TypeMemPtr makeFrom(TypeStruct obj) { return obj==_obj ? this : make(_nil, obj, _one, _pub); } + public TypeMemPtr makeFrom(TypeStruct obj) { return obj==_obj ? this : make(_nil, obj, _one, _pub,_ro); } public TypeMemPtr makeNullable() { return makeFrom((byte)3); } /** Inferred mutable local: nullable, public and non-singleton. */ public TypeMemPtr makeVar() { return (TypeMemPtr)makeStorage(); } /** Public-memory shape: nullable and non-singleton, preserving the referent. */ - @Override TypeMemPtr _makeStorage() { return make(_nil,_obj._makeStorage(),false,true); } - @Override TypeMemPtr makeFrom(byte nil) { return nil==_nil ? this : make(nil, _obj, _one, _pub); } + @Override TypeMemPtr _makeStorage() { return make(_nil,_obj._makeStorage(),false,true,_ro); } + @Override TypeMemPtr makeFrom(byte nil) { return nil==_nil ? this : make(nil, _obj, _one, _pub,_ro); } @Override public Type nonZero() { return makeFrom((byte)(_nil <= 1 ? 1 : 2)); } + private TypeMemPtr access(boolean ro) { _ro=ro; return this; } + + public TypeMemPtr withAccess(boolean ro) { + if( _ro==ro ) return this; + TypeMemPtr ptr = malloc(_nil, _obj, _one, _pub); + ptr._ro = ro; + TypeMemPtr res = ptr.intern(); + if( res==ptr ) return ptr; + return VISIT.isEmpty() ? res.free(ptr) : res.delayFree(ptr); + } + + // Writable slots can load and store references, so their element access + // must agree in both directions. A read-only outer view can weaken access. + @Override public boolean accessISA(Type dst) { + if( !(dst instanceof TypeMemPtr ptr) ) return true; + if( _ro && !ptr._ro ) return false; + if( ptr._ro || _obj._fields==null || ptr._obj._fields==null || !_obj.isAry() || !ptr._obj.isAry() ) return true; + Type a = _obj._fields[1]._t, b = ptr._obj._fields[1]._t; + return a.accessISA(b) && b.accessISA(a); + } + // 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 TypeMemPtr BOT = make((byte)3, TypeStruct.BOT); @@ -82,7 +109,7 @@ public class TypeMemPtr extends TypeNil { public static final TypeMemPtr NOTBOT = make((byte)2,TypeStruct.BOT); public static final TypeMemPtr TEST= make((byte)2, TypeStruct.TEST); - public static void gather(ArrayList<Type> 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); ts.add(TEST.makeRO()); } @Override public TypeNil xmeet(Type t) { @@ -91,28 +118,28 @@ public class TypeMemPtr extends TypeNil { // Can't keep _one if mixing two unequal singletons; the result is not // either singleton. Once either side is public, the meet is public. boolean one = _one && that._one && (_obj==that._obj || !(_obj.isConAry() || that._obj.isConAry())); - return make(xmeet0(that), (TypeStruct)_obj.meet(that._obj), one, _pub | that._pub); + return make(xmeet0(that), (TypeStruct)_obj.meet(that._obj), one, _pub | that._pub,_ro | that._ro); } @Override - TypeMemPtr xdual() { return malloc( dual0(), _obj.dual(), !_one, !_pub); } + TypeMemPtr xdual() { return malloc( dual0(), _obj.dual(), !_one, !_pub).access(!_ro); } @Override TypeMemPtr rdual() { if( _dual!=null ) return dual(); assert !_terned; - TypeMemPtr d = malloc(dual0(), null, !_one, !_pub); + TypeMemPtr d = malloc(dual0(), null, !_one, !_pub).access(!_ro); (_dual = d)._dual = this; // Cross link duals d._obj = _obj._terned ? _obj.dual() : _obj.rdual(); return d; } // RHS is NIL; do not deep-dual when crossing the center line - @Override Type meet0() { return _nil==3 ? this : make((byte)3,_obj); } + @Override Type meet0() { return _nil==3 ? this : make((byte)3,_obj,false,true,_ro); } @Override boolean _isConstant() { return _one; } - @Override boolean _isFinal() { return _obj._isFinal(); } - @Override TypeMemPtr _makeRO() { return makeFrom(_obj._makeRO()); } - @Override TypeMemPtr _close( String name, HashMap<String, Type> TYPES ) { return malloc(_nil,_obj._close(name, TYPES ),_one,_pub); } + @Override boolean _isFinal() { return _ro; } + @Override TypeMemPtr _makeRO() { return withAccess(true); } + @Override TypeMemPtr _close( String name, HashMap<String, Type> TYPES ) { return malloc(_nil,_obj._close(name, TYPES ),_one,_pub).access(_ro); } @Override Type _upgradeType(HashMap<String,Type> TYPES) { return makeFrom((TypeStruct)_obj._upgradeType(TYPES)); @@ -120,33 +147,34 @@ public class TypeMemPtr extends TypeNil { @Override public int log_size() { return 3; } // (1<<3)==8-byte pointers - @Override int hash() { return _obj.hashCode() ^ super.hash() ^ (_one ? 2048 : 0) ^ (_pub ? 4096 : 0); } + @Override int hash() { return (_ro ? 8192 : 0) ^ _obj.hashCode() ^ super.hash() ^ (_one ? 2048 : 0) ^ (_pub ? 4096 : 0); } @Override boolean eq(Type t) { TypeMemPtr ptr = (TypeMemPtr)t; // Invariant - return super.eq(ptr) && _one == ptr._one && _pub == ptr._pub && _obj == ptr._obj; + return _ro==ptr._ro && super.eq(ptr) && _one == ptr._one && _pub == ptr._pub && _obj == ptr._obj; } @Override boolean cycle_eq(Type t) { if( t._type != TMEMPTR ) return false; TypeMemPtr ptr = (TypeMemPtr)t; // Invariant - return super.eq(ptr) && _one == ptr._one && _pub == ptr._pub && _obj.cycle_eq(ptr._obj); + return _ro==ptr._ro && super.eq(ptr) && _one == ptr._one && _pub == ptr._pub && _obj.cycle_eq(ptr._obj); } @Override public int nkids() { return 1; } @Override public Type at( int idx ) { return _obj; } @Override public void set( int idx, Type t ) { _obj = (TypeStruct)t; } - // Reserve tags for null/not, one/general and public/private - @Override int TAGOFF() { return 8; } + // Reserve tags for null/not, one/general, public/private and read-only access + @Override int TAGOFF() { return 16; } @Override public void packed( BAOS baos, HashMap<String,Integer> strs ) { assert _nil>=2; baos.write(TAGOFFS[_type] + (_nil==2 ? 0 : 1) + (!_one ? 0 : 2) - + (!_pub ? 0 : 4) ); + + (!_pub ? 0 : 4) + + (!_ro ? 0 : 8) ); } static TypeMemPtr packed( int tag, BAOS bais ) { - return malloc((byte)((tag&1)+2),null,(tag&2)==2,(tag&4)==4); + return malloc((byte)((tag&1)+2),null,(tag&2)==2,(tag&4)==4).access((tag&8)==8); } @Override public String str() { diff --git a/chapter25/src/main/smp/sys/aryi64.smp b/chapter25/src/main/smp/sys/aryi64.smp index 22ee082e..98b72605 100644 --- a/chapter25/src/main/smp/sys/aryi64.smp +++ b/chapter25/src/main/smp/sys/aryi64.smp @@ -53,7 +53,7 @@ struct aryi64 { // parseAryI64 & aryi64.str are bijections modulo whitespace. // Returns null if the parse failed, without giving a clue as to why. val parseAryI64 = { u8[~] str -> - scan !s = new scan(str); + !scan !s = new scan(str); if( !s.match('[') ) return null; // Array of parsed results var nums = new aryi64(new i64[1]); diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter10Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter10Test.java index f41a1904..cdafb43b 100644 --- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter10Test.java +++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter10Test.java @@ -24,7 +24,7 @@ public class Chapter10Test { private static final String NULLABLE_POINT_SOURCE = """ struct Point { int x; new Point = { int v -> x = v; }; }; - Point?[] !points = new Point?[2]; + !Point?[] !points = new !Point?[2]; points[arg] = new Point(42); Point? p = points[1]; """; @@ -117,7 +117,7 @@ struct _Foo { int x; }; _Foo? foo = null; -_Bar !bar = new _Bar; +!_Bar !bar = new _Bar; bar.a = 1; bar.a = 2; return bar.a; @@ -130,7 +130,7 @@ return bar.a; public void testExample() { CodeGen code = new CodeGen(""" struct Vector2D { int x; int y; }; -Vector2D !v = new Vector2D; +!Vector2D !v = new Vector2D; v.x = 1; if (arg) v.y = 2; @@ -169,7 +169,7 @@ arg=0+new s0.0; public void testLoop() { CodeGen code = new CodeGen(""" struct Bar { int a; }; -Bar !bar = new Bar; +!Bar !bar = new Bar; while (arg) { bar.a = bar.a + 2; arg = arg + 1; @@ -184,7 +184,7 @@ return bar.a; public void testIf() { CodeGen code = new CodeGen(""" struct Bar { int a; }; -Bar !bar = new Bar; +!Bar !bar = new Bar; if (arg) bar = null; bar.a = 1; return bar.a; @@ -197,7 +197,7 @@ return bar.a; public void testIf2() { CodeGen code = new CodeGen(""" struct Bar { int a; }; -Bar? !bar = null; +!Bar? !bar = null; if (arg) bar = new Bar; bar.a = 1; return bar.a; @@ -210,7 +210,7 @@ return bar.a; public void testIf3() { CodeGen code = new CodeGen(""" struct Bar { int a; }; -Bar bar = null; +!Bar !bar = null; if (arg) bar = null; bar.a = 1; return bar.a; @@ -223,7 +223,7 @@ return bar.a; public void testIfOrNull() { CodeGen code = new CodeGen(""" struct _Bar { int a; }; -_Bar? !bar = new _Bar; +!_Bar? !bar = new _Bar; if (arg) bar = null; if( bar ) bar.a = 1; return bar.a; @@ -237,7 +237,7 @@ return bar.a; CodeGen code = new CodeGen( """ struct _Bar { int a; }; -_Bar? !bar = new _Bar; +!_Bar? !bar = new _Bar; if (arg) bar = null; int rez = 3; if( !bar ) rez=4; @@ -252,7 +252,7 @@ return rez; public void testWhileWithNullInside() { CodeGen code = new CodeGen(""" struct s0 {int v0;}; -s0? !v0 = new s0; +!s0? !v0 = new s0; int ret = 0; while(arg) { ret = v0.v0; @@ -273,7 +273,7 @@ struct s0 { int v0; }; s0? v1=new s0; -s0? v1; +!s0? !v1; v1=new s0; """); try { code.parse(); fail(); } @@ -289,7 +289,7 @@ struct Iter { int x; int len; }; -Iter !i = new Iter; +!Iter !i = new Iter; i.len = arg; int sum=0; while( i.x < i.len ) { @@ -307,9 +307,9 @@ return sum; public void test1() { CodeGen code = new CodeGen(""" struct s0 {int v0;}; -s0 !ret = new s0; +!s0 !ret = new s0; while(arg) { - s0 !v0 = new s0; + !s0 !v0 = new s0; v0.v0 = arg; arg = arg-1; if (arg==5) ret=v0; @@ -325,8 +325,8 @@ return ret; public void test2() { CodeGen code = new CodeGen(""" struct _s0 {int v0;}; -_s0 !ret = new _s0; -_s0 !v0 = new _s0; +!_s0 !ret = new _s0; +!_s0 !v0 = new _s0; while(arg) { v0.v0 = arg; arg = arg-1; @@ -343,9 +343,9 @@ return ret.v0; public void test3() { CodeGen code = new CodeGen(""" struct _s0 {int v0;}; -_s0 !ret = new _s0; +!_s0 !ret = new _s0; while(arg < 10) { - _s0 !v1 = new _s0; + !_s0 !v1 = new _s0; if (arg == 5) ret=v1; arg = arg + 1; } diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter11Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter11Test.java deleted file mode 100644 index d515df8d..00000000 --- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter11Test.java +++ /dev/null @@ -1,284 +0,0 @@ -package com.seaofnodes.simple; -import com.seaofnodes.simple.codegen.CodeGen; - - -import org.junit.Test; -import static org.junit.Assert.assertEquals; - -public class Chapter11Test { - - @Test - public void testPrimes() { - CodeGen code = new CodeGen( -""" -if( arg < 2 ) return 0; -int primeCount = 1; -int prime = 3; -while( prime <= arg ) { - int isPrime = 1; - // Check for even case, so the next loop need only check odds - if( (prime/2)*2 == prime ) - continue; - // Check odds up to sqrt of prime - int j = 3; - while( j*j <= prime ) { - if( (prime/j)*j == prime ) { - isPrime = 0; - break; - } - j = j + 2; - } - if( isPrime ) - primeCount = primeCount + 1; - prime = prime + 2; -} -return primeCount; -"""); - code.parse().opto(); - assertEquals("return Phi(Region,0,Phi(Loop,1,Phi(Region,Phi_primeCount,Phi_primeCount,(Phi_primeCount+1))));", code.print()); - assertEquals("0", Eval2.eval(code, 1)); // No primes 1 or below - assertEquals("1", Eval2.eval(code, 2)); // 2 - assertEquals("2", Eval2.eval(code, 3)); // 2, 3 - assertEquals("2", Eval2.eval(code, 4)); // 2, 3 - assertEquals("3", Eval2.eval(code, 5)); // 2, 3, 5 - assertEquals("4", Eval2.eval(code, 10)); // 2, 3, 5, 7 - } - - @Test - public void testClassField() { - CodeGen code = new CodeGen( -""" -if( arg < 2 ) return 0; -int primeCount = 1; -while( arg-- ) - primeCount++; -return primeCount; -"""); - code.driver(CodeGen.Phase.LocalSched); - assertEquals("0", Eval2.eval(code, 1)); // No primes 1 or below - assertEquals("3", Eval2.eval(code, 2)); // -} - - - @Test - public void testAntiDeps1() { - CodeGen code = new CodeGen( -""" -struct _S { int f; }; -_S !v=new _S; -v.f = 2; -_S q=new _S; -int i=q.f; -i=v.f; -if (arg) v.f=1; -return i; -"""); - code.parse().opto(); - assertEquals("return 2;", code.print()); - assertEquals("2", Eval2.eval(code, 0)); - assertEquals("2", Eval2.eval(code, 1)); - } - - @Test - public void testAntiDeps2() { - CodeGen code = new CodeGen( -""" -struct _S { int f; }; -_S !v = new _S; -v.f = arg; -_S !t = new _S; -int i = 0; -if (arg) { - if (arg+1) v = t; - i = v.f; -} else { - v.f = 2; -} -return i; -"""); - code.parse().opto(); - assertEquals("return Phi(Region,.f,0);", code.print()); - } - - @Test - public void testAntiDeps3() { - CodeGen code = new CodeGen( -""" -struct _S { int f; }; -_S !v0 = new _S; -_S? v1; -if (arg) v1 = new _S; -if (v1) { - v0.f = v1.f; -} else { - v0.f = 2; -} -return v0.f; -"""); - code.parse().opto(); - assertEquals("return Phi(Region,.f,2);", code.print()); - } - - - @Test - public void testAntiDeps4() { - CodeGen code = new CodeGen( -""" -struct _S { int f; }; -_S !v = new _S; -v.f = arg; -_S t = new _S; -int i = v.f; -if (arg+1) arg= 0; -while (arg) v.f = 2; -return i; -"""); - code.parse().opto(); - assertEquals("return arg;", code.print()); - } - - @Test - public void testAntiDeps5() { - CodeGen code = new CodeGen( -""" -struct _S { int f; }; -_S !v = new _S; -while(1) { - while(arg+1) { arg=arg-1; } - if (arg) break; - v.f = 2; -} -return v.f; -"""); - code.parse().opto(); - assertEquals("return Phi(Loop,0,2);", code.print()); - for( int arg : new int[]{-1,0,1,3,100} ) - assertEquals("0",Eval2.eval(code,arg)); - } - - @Test - public void testAntiDeps6() { - CodeGen code = new CodeGen( -""" -struct _S { int v; }; -_S !ptr=new _S; -while( -arg ) - ptr = new _S; -while(1) - arg = arg+ptr.v; -"""); - code.parse().opto(); - assertEquals("return Top;", code.print()); - } - - @Test - public void testAntiDeps7() { - CodeGen code = new CodeGen( -""" -struct _S { int f; }; -_S !v = new _S; -_S t = new _S; -int i = v.f; -while (arg) { - v.f = arg; - arg = arg-1; -} -return i; -"""); - code.parse().opto(); - assertEquals("return 0;", code.print()); - } - - @Test - public void testAntiDeps8() { - CodeGen code = new CodeGen( -""" -struct _S { int f; }; -_S !v = new _S; -_S t = new _S; -while(arg) { - arg=arg-1; - int f = v.f; - v.f = 2; - if (arg) arg = f; - else v.f = 3; -} -return arg; -"""); - code.parse().opto(); - assertEquals("return 0;", code.print()); - } - - @Test - public void testAntiDeps9() { - CodeGen code = new CodeGen( -""" -struct _S { int f; }; -_S !v = new _S; -_S t = new _S; -if (arg) { - v.f=2; - int i=t.f; - v.f=i; -} -return v.f; -"""); - code.parse().opto(); - assertEquals("return 0;", code.print()); - } - - @Test - public void testExample2() { - CodeGen code = new CodeGen( -""" -struct _S { int f; }; -_S !v = new _S; -int i = arg; -while (arg > 0) { - int j = i/3; - if (arg == 5) - v.f = j; - arg = arg - 1; -} -return v; - """); - code.parse().opto(); - //assertEquals("return new _S;", code.print()); - } - - @Test - public void testScheduleUse() { - CodeGen code = new CodeGen( -""" -int v0=0; -while(0>=0) { - u1 v1=0; - v1=v0; - if(v1*0) - v0=-v1; -} -return 0; -"""); - code.parse().opto(); - assertEquals("return Top;", code.print()); - } - - @Test - public void testLoopCarriedDep() { - CodeGen code = new CodeGen( -""" -u32 v0=0; -{ - int v1=0; - while(v1) { - v1=1>>>v0!=0; - v0=v1/0; - } - return v1; -} -"""); - code.parse().opto(); - assertEquals("return 0;", code.print()); - } - -} diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter12Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter12Test.java index 0f4bb0b4..a71cc167 100644 --- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter12Test.java +++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter12Test.java @@ -3,45 +3,149 @@ package com.seaofnodes.simple; import com.seaofnodes.simple.codegen.CodeGen; import org.junit.Test; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; public class Chapter12Test { - @Test public void testSubZeroFloat() { - var code = new CodeGen("flt x = arg; return 0-x;").parse().opto().typeCheck(); - // Compare strings so +0.0 and -0.0 remain distinct. - assertEquals("0.0", Eval2.eval(code,0)); - assertEquals("-1.0", Eval2.eval(code,1)); + @Test + public void testJig() { + CodeGen code = new CodeGen( +""" +return 3; +"""); + code.parse().opto(); + assertEquals("return 3;", code.print()); + assertEquals("3", Eval2.eval(code, 0)); } - @Test public void testDeadNumericReturns() { - for( String[] test : new String[][] { - {"if(1) return 7; else return 2.5;", "7"}, - {"if(0) return 7; else return 2.5;", "2.5"}, - {"return 7; return 2.5;", "7"}, - {"return 2.5; return 7;", "2.5"} - } ) { - String src = test[0]; - var code = new CodeGen(src).parse().opto().typeCheck(); - assertEquals(src,test[1],Eval2.eval(code,0)); - } + @Test + public void testLinkedList0() { + CodeGen code = new CodeGen( +""" +struct LLI { !LLI? !next; int i; }; +!LLI? !head = null; +while( arg ) { + !LLI !x = new LLI; + x.next = head; + x.i = arg; + head = x; + arg = arg-1; +} +return head.next.i; +"""); + try { code.parse().opto().typeCheck(); fail(); } + catch( Exception e ) { assertEquals("Might be null accessing 'next'",e.getMessage()); } } + @Test + public void testLinkedList1() { + CodeGen code = new CodeGen( +""" +struct _LLI { !_LLI? !next; int q; }; +!_LLI? !head = null; +while( arg ) { + !_LLI !x = new _LLI; + x.next = head; + x.q = arg; + head = x; + arg = arg-1; +} +if( !head ) return 0; +_LLI? next = head.next; +if( next==null ) return 1; +return next.q; +"""); + code.parse().opto().typeCheck(); + assertEquals("return Phi(Region,1,.q,0);", code.print()); + assertEquals("2", Eval2.eval(code, 3)); + } @Test - public void testJig() { + public void testCoRecur() { CodeGen code = new CodeGen( """ -return 0; +struct _int0 { int i; !Test._flt0? !f; }; +struct _flt0 { flt f; !_int0? !i; }; +!_int0 !i0 = new _int0; +i0.i = 17; +!_flt0 !f0 = new _flt0; +f0.f = 3.14; +i0.f = f0; +f0.i = i0; +return f0.i.f.i.i; +"""); + code.parse().opto().typeCheck(); + assertEquals("return 17;", code.print()); + } + + @Test + public void testNullRef0() { + CodeGen code = new CodeGen( +""" +struct _N { !_N? !next; int i; }; +_N n = new _N; +return n.next; """); code.parse().opto(); - assertEquals("return 0;", code.print()); - assertEquals("0", Eval2.eval(code, 0)); + assertEquals("return null;", code.print()); } @Test - public void testFloat() { + public void testNullRef1() { CodeGen code = new CodeGen( """ -flt x = 3.14; +struct M { int m; }; +struct N { !M !next; int i; new N = { !M m -> next = m; }; }; +N n = new N(new M); +return n.next; +"""); + code.parse().opto(); + assertEquals("Stop[ return (const)Test.M; return MEM[ 2:.m=0;]; return Test.M; return MEM[ 2:___ 3:___ 4:___ 5:___ 6:.next=Parm_m(Test.N.N,*Test.M {i64 !m; }); 7:.i=0;]; return MEM[ 2:___ 3:___ 4:___ 5:___ 6:___ 7:.i=0;]; return Test.N; ]", code.print()); + } + + @Test + public void testNullRef2() { + CodeGen code = new CodeGen( +""" +struct M { int m; }; +struct N { !M !next; int i; new N = { !M m -> next = m; }; }; +N n = new N(null); +return n.next; +"""); + try { code.parse().opto().typeCheck(); fail(); } + catch( Exception e ) { assertEquals("Argument #0 isa null, but must be a *Test.M {i64 !m; }",e.getMessage()); } + } + + @Test + public void testNullRef3() { + CodeGen code = new CodeGen( +""" +struct N { !N? !next; int i; }; +!N !n = new N; +n.i = 3.14; +return n.i; +"""); + try { code.parse().opto().typeCheck(); fail(); } + catch( Exception e ) { assertEquals("Type 3.14 is not of declared type i64",e.getMessage()); } + } + + @Test + public void testNullRef4() { + CodeGen code = new CodeGen("return -null-5/null-5;"); + try { code.parse().opto().typeCheck(); fail(); } + catch( Exception e ) { assertEquals("Cannot 5 // null",e.getMessage()); } + } + + @Test public void testNullRef5() { + CodeGen code = new CodeGen("return null+42;"); + try { code.parse().opto().typeCheck(); fail(); } + catch( Exception e ) { assertEquals("Cannot null + 42",e.getMessage()); } + } + + @Test + public void testEmpty() { + CodeGen code = new CodeGen( +""" +struct _S{}; return 0; """); code.parse().opto(); @@ -50,34 +154,128 @@ return 0; } @Test - public void testSquareRoot() { + public void testForwardRef0() { CodeGen code = new CodeGen( """ -flt guess = arg; -while( 1 ) { - flt next = (arg/guess + guess)/2; - if( next == guess ) break; - guess = next; -} -return guess; +struct S1 { S2? s; }; +return new S2; """); - // TODO: One of the problems with <clinit> returning an OS error code, - // is it chops all results to int. - code.parse().opto(); - assertEquals("return Phi(Loop,(flt)arg,((Phi_guess+(ToFloat/Phi_guess))*0.5f));", code.print()); - assertEquals("3.0", Eval2.eval(code, 9)); - assertEquals("1.414213562373095", Eval2.eval(code, 2)); + try { code.parse().opto().typeCheck(); fail(); } + catch( Exception e ) { assertEquals("Unknown struct type 'S2'",e.getMessage()); } } @Test - public void testFPOps() { + public void testForwardRef1() { CodeGen code = new CodeGen( """ -flt x = arg; -return x+1==x; +struct _S1 { !_S2? !s; }; +struct _S2 { int x; }; +return (new _S1).s=new _S2; """); code.parse().opto(); - assertEquals("return ((flt)arg==(ToFloat+1.0f));", code.print()); - assertEquals("0", Eval2.eval(code, 1)); + assertEquals("return Test._S2;", code.print()); + } + + @Test + public void testcheckNull() { + CodeGen code = new CodeGen( +""" +struct I { int i; }; +struct P { !I? !pi; }; +!P !p1 = new P; +!P !p2 = new P; +p2.pi = new I; +p2.pi.i = 2; +if (arg) p1 = new P; +return p1.pi.i + 1; +"""); + try { code.parse().opto().typeCheck(); fail(); } + catch( Exception e ) { assertEquals("Might be null accessing 'i'",e.getMessage()); } + } + + @Test + public void testCoRecur2() { + CodeGen code = new CodeGen( +""" +struct _A { !Test._B? !f0; !Test._C? !f1; }; !_A !a = new _A; +struct _B { !Test._C? !f0; !Test._A? !f1; }; !_B !b = new _B; +struct _C { !Test._A? !f0; !Test._B? !f1; }; !_C !c = new _C; + +a.f0=b; a.f1=c; +b.f0=c; b.f1=a; +c.f0=a; c.f1=b; + +return a.f0.f1.f0.f1.f0; + +"""); + code.parse().opto().typeCheck().GCM().localSched(); + assertEquals("return Test._B;", code.print()); + assertEquals("Test._B{f0=Test._C{f0=Test._A{f0=$cyclic,f1=$cyclic},f1=$cyclic},f1=$cyclic}", Eval2.eval(code, 0)); + } + + @Test + public void testCoRecur3() { + CodeGen code = new CodeGen( +""" +struct _A { !Test._L? !a; !Test._T? !b; !Test._F? !c; }; !_A !a = new _A; +struct _B { !Test._M? !a; !Test._U? !b; !Test._G? !c; }; !_B !b = new _B; +struct _C { !Test._N? !a; !Test._V? !b; !Test._H? !c; }; !_C !c = new _C; +struct _D { !Test._O? !a; !Test._W? !b; !Test._I? !c; }; !_D !d = new _D; +struct _E { !Test._P? !a; !Test._X? !b; !Test._J? !c; }; !_E !e = new _E; +struct _F { !Test._Q? !a; !Test._Y? !b; !Test._K? !c; }; !_F !f = new _F; +struct _G { !Test._R? !a; !Test._Z? !b; !Test._L? !c; }; !_G !g = new _G; +struct _H { !Test._S? !a; !Test._A? !b; !Test._M? !c; }; !_H !h = new _H; +struct _I { !Test._T? !a; !Test._B? !b; !Test._N? !c; }; !_I !i = new _I; +struct _J { !Test._U? !a; !Test._C? !b; !Test._O? !c; }; !_J !j = new _J; +struct _K { !Test._V? !a; !Test._D? !b; !Test._P? !c; }; !_K !k = new _K; +struct _L { !Test._W? !a; !Test._E? !b; !Test._Q? !c; }; !_L !l = new _L; +struct _M { !Test._X? !a; !Test._F? !b; !Test._R? !c; }; !_M !m = new _M; +struct _N { !Test._Y? !a; !Test._G? !b; !Test._S? !c; }; !_N !n = new _N; +struct _O { !Test._Z? !a; !Test._H? !b; !Test._T? !c; }; !_O !o = new _O; +struct _P { !Test._A? !a; !Test._I? !b; !Test._U? !c; }; !_P !p = new _P; +struct _Q { !Test._B? !a; !Test._J? !b; !Test._V? !c; }; !_Q !q = new _Q; +struct _R { !Test._C? !a; !Test._K? !b; !Test._W? !c; }; !_R !r = new _R; +struct _S { !Test._D? !a; !Test._L? !b; !Test._X? !c; }; !_S !s = new _S; +struct _T { !Test._E? !a; !Test._M? !b; !Test._Y? !c; }; !_T !t = new _T; +struct _U { !Test._F? !a; !Test._N? !b; !Test._Z? !c; }; !_U !u = new _U; +struct _V { !Test._G? !a; !Test._O? !b; !Test._A? !c; }; !_V !v = new _V; +struct _W { !Test._H? !a; !Test._P? !b; !Test._B? !c; }; !_W !w = new _W; +struct _X { !Test._I? !a; !Test._Q? !b; !Test._C? !c; }; !_X !x = new _X; +struct _Y { !Test._J? !a; !Test._R? !b; !Test._D? !c; }; !_Y !y = new _Y; +struct _Z { !Test._K? !a; !Test._S? !b; !Test._E? !c; }; !_Z !z = new _Z; + +a.a=l; a.b=t; a.c=f; +b.a=m; b.b=u; b.c=g; +c.a=n; c.b=v; c.c=h; +d.a=o; d.b=w; d.c=i; +e.a=p; e.b=x; e.c=j; +f.a=q; f.b=y; f.c=k; +g.a=r; g.b=z; g.c=l; +h.a=s; h.b=a; h.c=m; +i.a=t; i.b=b; i.c=n; +j.a=u; j.b=c; j.c=o; +k.a=v; k.b=d; k.c=p; +l.a=w; l.b=e; l.c=q; +m.a=x; m.b=f; m.c=r; +n.a=y; n.b=g; n.c=s; +o.a=z; o.b=h; o.c=t; +p.a=a; p.b=i; p.c=u; +q.a=b; q.b=j; q.c=v; +r.a=c; r.b=k; r.c=w; +s.a=d; s.b=l; s.c=x; +t.a=e; t.b=m; t.c=y; +u.a=f; u.b=n; u.c=z; +v.a=g; v.b=o; v.c=a; +w.a=h; w.b=p; w.c=b; +x.a=i; x.b=q; x.c=c; +y.a=j; y.b=r; y.c=d; +z.a=k; z.b=s; z.c=e; + +return a.b.c.a.b.c.a.b.c.a.b.c.a.b.c.a.b.c; + +"""); + code.parse().opto().typeCheck().GCM().localSched(); + assertEquals("return Test._R;", code.print()); + assertEquals("Test._R{a=Test._C{a=Test._N{a=Test._Y{a=Test._J{a=Test._U{a=Test._F{a=Test._Q{a=Test._B{a=Test._M{a=Test._X{a=Test._I{a=Test._T{a=Test._E{a=Test._P{a=Test._A{a=Test._L{a=Test._W{a=Test._H{a=Test._S{a=Test._D{a=Test._O{a=Test._Z{a=Test._K{a=Test._V{a=Test._G{a=$cyclic,b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic}", Eval2.eval(code, 0)); } } diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter13Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter13Test.java index fedd59ce..7cfa5817 100644 --- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter13Test.java +++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter13Test.java @@ -1,281 +1,284 @@ package com.seaofnodes.simple; - import com.seaofnodes.simple.codegen.CodeGen; + + import org.junit.Test; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; public class Chapter13Test { + @Test - public void testJig() { + public void testPrimes() { CodeGen code = new CodeGen( """ -return 3; +if( arg < 2 ) return 0; +int primeCount = 1; +int prime = 3; +while( prime <= arg ) { + int isPrime = 1; + // Check for even case, so the next loop need only check odds + if( (prime/2)*2 == prime ) + continue; + // Check odds up to sqrt of prime + int j = 3; + while( j*j <= prime ) { + if( (prime/j)*j == prime ) { + isPrime = 0; + break; + } + j = j + 2; + } + if( isPrime ) + primeCount = primeCount + 1; + prime = prime + 2; +} +return primeCount; """); code.parse().opto(); - assertEquals("return 3;", code.print()); - assertEquals("3", Eval2.eval(code, 0)); + assertEquals("return Phi(Region,0,Phi(Loop,1,Phi(Region,Phi_primeCount,Phi_primeCount,(Phi_primeCount+1))));", code.print()); + assertEquals("0", Eval2.eval(code, 1)); // No primes 1 or below + assertEquals("1", Eval2.eval(code, 2)); // 2 + assertEquals("2", Eval2.eval(code, 3)); // 2, 3 + assertEquals("2", Eval2.eval(code, 4)); // 2, 3 + assertEquals("3", Eval2.eval(code, 5)); // 2, 3, 5 + assertEquals("4", Eval2.eval(code, 10)); // 2, 3, 5, 7 } @Test - public void testLinkedList0() { + public void testClassField() { CodeGen code = new CodeGen( """ -struct LLI { LLI? next; int i; }; -LLI? !head = null; -while( arg ) { - LLI !x = new LLI; - x.next = head; - x.i = arg; - head = x; - arg = arg-1; -} -return head.next.i; +if( arg < 2 ) return 0; +int primeCount = 1; +while( arg-- ) + primeCount++; +return primeCount; """); - try { code.parse().opto().typeCheck(); fail(); } - catch( Exception e ) { assertEquals("Might be null accessing 'next'",e.getMessage()); } - } + code.driver(CodeGen.Phase.LocalSched); + assertEquals("0", Eval2.eval(code, 1)); // No primes 1 or below + assertEquals("3", Eval2.eval(code, 2)); // +} + @Test - public void testLinkedList1() { + public void testAntiDeps1() { CodeGen code = new CodeGen( """ -struct _LLI { _LLI? next; int q; }; -_LLI? !head = null; -while( arg ) { - _LLI !x = new _LLI; - x.next = head; - x.q = arg; - head = x; - arg = arg-1; -} -if( !head ) return 0; -_LLI? next = head.next; -if( next==null ) return 1; -return next.q; +struct _S { int f; }; +!_S !v=new _S; +v.f = 2; +_S q=new _S; +int i=q.f; +i=v.f; +if (arg) v.f=1; +return i; """); - code.parse().opto().typeCheck(); - assertEquals("return Phi(Region,1,.q,0);", code.print()); - assertEquals("2", Eval2.eval(code, 3)); + code.parse().opto(); + assertEquals("return 2;", code.print()); + assertEquals("2", Eval2.eval(code, 0)); + assertEquals("2", Eval2.eval(code, 1)); } @Test - public void testCoRecur() { + public void testAntiDeps2() { CodeGen code = new CodeGen( """ -struct _int0 { int i; Test._flt0? f; }; -struct _flt0 { flt f; _int0? i; }; -_int0 !i0 = new _int0; -i0.i = 17; -_flt0 !f0 = new _flt0; -f0.f = 3.14; -i0.f = f0; -f0.i = i0; -return f0.i.f.i.i; +struct _S { int f; }; +!_S !v = new _S; +v.f = arg; +!_S !t = new _S; +int i = 0; +if (arg) { + if (arg+1) v = t; + i = v.f; +} else { + v.f = 2; +} +return i; """); - code.parse().opto().typeCheck(); - assertEquals("return 17;", code.print()); + code.parse().opto(); + assertEquals("return Phi(Region,.f,0);", code.print()); } @Test - public void testNullRef0() { + public void testAntiDeps3() { CodeGen code = new CodeGen( """ -struct _N { _N? next; int i; }; -_N n = new _N; -return n.next; +struct _S { int f; }; +!_S !v0 = new _S; +!_S? !v1; +if (arg) v1 = new _S; +if (v1) { + v0.f = v1.f; +} else { + v0.f = 2; +} +return v0.f; """); code.parse().opto(); - assertEquals("return null;", code.print()); + assertEquals("return Phi(Region,.f,2);", code.print()); } + @Test - public void testNullRef1() { + public void testAntiDeps4() { CodeGen code = new CodeGen( """ -struct M { int m; }; -struct N { M next; int i; new N = { M m -> next = m; }; }; -N n = new N(new M); -return n.next; +struct _S { int f; }; +!_S !v = new _S; +v.f = arg; +_S t = new _S; +int i = v.f; +if (arg+1) arg= 0; +while (arg) v.f = 2; +return i; """); code.parse().opto(); - assertEquals("Stop[ return (const)Test.M; return MEM[ 2:.m=0;]; return Test.M; return MEM[ 2:___ 3:___ 4:___ 5:___ 6:.next=Parm_m(Test.N.N,*Test.M {i64 !m; }); 7:.i=0;]; return MEM[ 2:___ 3:___ 4:___ 5:___ 6:___ 7:.i=0;]; return Test.N; ]", code.print()); + assertEquals("return arg;", code.print()); } @Test - public void testNullRef2() { + public void testAntiDeps5() { CodeGen code = new CodeGen( """ -struct M { int m; }; -struct N { M next; int i; new N = { M m -> next = m; }; }; -N n = new N(null); -return n.next; +struct _S { int f; }; +!_S !v = new _S; +while(1) { + while(arg+1) { arg=arg-1; } + if (arg) break; + v.f = 2; +} +return v.f; """); - try { code.parse().opto().typeCheck(); fail(); } - catch( Exception e ) { assertEquals("Argument #0 isa null, but must be a *Test.M {i64 !m; }",e.getMessage()); } + code.parse().opto(); + assertEquals("return Phi(Loop,0,2);", code.print()); + for( int arg : new int[]{-1,0,1,3,100} ) + assertEquals("0",Eval2.eval(code,arg)); } @Test - public void testNullRef3() { + public void testAntiDeps6() { CodeGen code = new CodeGen( """ -struct N { N? next; int i; }; -N !n = new N; -n.i = 3.14; -return n.i; +struct _S { int v; }; +!_S !ptr=new _S; +while( -arg ) + ptr = new _S; +while(1) + arg = arg+ptr.v; """); - try { code.parse().opto().typeCheck(); fail(); } - catch( Exception e ) { assertEquals("Type 3.14 is not of declared type i64",e.getMessage()); } - } - - @Test - public void testNullRef4() { - CodeGen code = new CodeGen("return -null-5/null-5;"); - try { code.parse().opto().typeCheck(); fail(); } - catch( Exception e ) { assertEquals("Cannot 5 // null",e.getMessage()); } - } - - @Test public void testNullRef5() { - CodeGen code = new CodeGen("return null+42;"); - try { code.parse().opto().typeCheck(); fail(); } - catch( Exception e ) { assertEquals("Cannot null + 42",e.getMessage()); } + code.parse().opto(); + assertEquals("return Top;", code.print()); } @Test - public void testEmpty() { + public void testAntiDeps7() { CodeGen code = new CodeGen( """ -struct _S{}; -return 0; +struct _S { int f; }; +!_S !v = new _S; +_S t = new _S; +int i = v.f; +while (arg) { + v.f = arg; + arg = arg-1; +} +return i; """); code.parse().opto(); assertEquals("return 0;", code.print()); - assertEquals("0", Eval2.eval(code, 0)); } @Test - public void testForwardRef0() { + public void testAntiDeps8() { CodeGen code = new CodeGen( """ -struct S1 { S2? s; }; -return new S2; +struct _S { int f; }; +!_S !v = new _S; +_S t = new _S; +while(arg) { + arg=arg-1; + int f = v.f; + v.f = 2; + if (arg) arg = f; + else v.f = 3; +} +return arg; """); - try { code.parse().opto().typeCheck(); fail(); } - catch( Exception e ) { assertEquals("Unknown struct type 'S2'",e.getMessage()); } + code.parse().opto(); + assertEquals("return 0;", code.print()); } @Test - public void testForwardRef1() { + public void testAntiDeps9() { CodeGen code = new CodeGen( """ -struct _S1 { _S2? s; }; -struct _S2 { int x; }; -return (new _S1).s=new _S2; +struct _S { int f; }; +!_S !v = new _S; +_S t = new _S; +if (arg) { + v.f=2; + int i=t.f; + v.f=i; +} +return v.f; """); code.parse().opto(); - assertEquals("return Test._S2;", code.print()); + assertEquals("return 0;", code.print()); } @Test - public void testcheckNull() { + public void testExample2() { CodeGen code = new CodeGen( """ -struct I { int i; }; -struct P { I? pi; }; -P !p1 = new P; -P !p2 = new P; -p2.pi = new I; -p2.pi.i = 2; -if (arg) p1 = new P; -return p1.pi.i + 1; -"""); - try { code.parse().opto().typeCheck(); fail(); } - catch( Exception e ) { assertEquals("Might be null accessing 'i'",e.getMessage()); } +struct _S { int f; }; +!_S !v = new _S; +int i = arg; +while (arg > 0) { + int j = i/3; + if (arg == 5) + v.f = j; + arg = arg - 1; +} +return v; + """); + code.parse().opto(); + //assertEquals("return new _S;", code.print()); } @Test - public void testCoRecur2() { + public void testScheduleUse() { CodeGen code = new CodeGen( """ -struct _A { Test._B? f0; Test._C? f1; }; _A !a = new _A; -struct _B { Test._C? f0; Test._A? f1; }; _B !b = new _B; -struct _C { Test._A? f0; Test._B? f1; }; _C !c = new _C; - -a.f0=b; a.f1=c; -b.f0=c; b.f1=a; -c.f0=a; c.f1=b; - -return a.f0.f1.f0.f1.f0; - +int v0=0; +while(0>=0) { + u1 v1=0; + v1=v0; + if(v1*0) + v0=-v1; +} +return 0; """); - code.parse().opto().typeCheck().GCM().localSched(); - assertEquals("return Test._B;", code.print()); - assertEquals("Test._B{f0=Test._C{f0=Test._A{f0=$cyclic,f1=$cyclic},f1=$cyclic},f1=$cyclic}", Eval2.eval(code, 0)); + code.parse().opto(); + assertEquals("return Top;", code.print()); } @Test - public void testCoRecur3() { + public void testLoopCarriedDep() { CodeGen code = new CodeGen( """ -struct _A { Test._L? a; Test._T? b; Test._F? c; }; _A !a = new _A; -struct _B { Test._M? a; Test._U? b; Test._G? c; }; _B !b = new _B; -struct _C { Test._N? a; Test._V? b; Test._H? c; }; _C !c = new _C; -struct _D { Test._O? a; Test._W? b; Test._I? c; }; _D !d = new _D; -struct _E { Test._P? a; Test._X? b; Test._J? c; }; _E !e = new _E; -struct _F { Test._Q? a; Test._Y? b; Test._K? c; }; _F !f = new _F; -struct _G { Test._R? a; Test._Z? b; Test._L? c; }; _G !g = new _G; -struct _H { Test._S? a; Test._A? b; Test._M? c; }; _H !h = new _H; -struct _I { Test._T? a; Test._B? b; Test._N? c; }; _I !i = new _I; -struct _J { Test._U? a; Test._C? b; Test._O? c; }; _J !j = new _J; -struct _K { Test._V? a; Test._D? b; Test._P? c; }; _K !k = new _K; -struct _L { Test._W? a; Test._E? b; Test._Q? c; }; _L !l = new _L; -struct _M { Test._X? a; Test._F? b; Test._R? c; }; _M !m = new _M; -struct _N { Test._Y? a; Test._G? b; Test._S? c; }; _N !n = new _N; -struct _O { Test._Z? a; Test._H? b; Test._T? c; }; _O !o = new _O; -struct _P { Test._A? a; Test._I? b; Test._U? c; }; _P !p = new _P; -struct _Q { Test._B? a; Test._J? b; Test._V? c; }; _Q !q = new _Q; -struct _R { Test._C? a; Test._K? b; Test._W? c; }; _R !r = new _R; -struct _S { Test._D? a; Test._L? b; Test._X? c; }; _S !s = new _S; -struct _T { Test._E? a; Test._M? b; Test._Y? c; }; _T !t = new _T; -struct _U { Test._F? a; Test._N? b; Test._Z? c; }; _U !u = new _U; -struct _V { Test._G? a; Test._O? b; Test._A? c; }; _V !v = new _V; -struct _W { Test._H? a; Test._P? b; Test._B? c; }; _W !w = new _W; -struct _X { Test._I? a; Test._Q? b; Test._C? c; }; _X !x = new _X; -struct _Y { Test._J? a; Test._R? b; Test._D? c; }; _Y !y = new _Y; -struct _Z { Test._K? a; Test._S? b; Test._E? c; }; _Z !z = new _Z; - -a.a=l; a.b=t; a.c=f; -b.a=m; b.b=u; b.c=g; -c.a=n; c.b=v; c.c=h; -d.a=o; d.b=w; d.c=i; -e.a=p; e.b=x; e.c=j; -f.a=q; f.b=y; f.c=k; -g.a=r; g.b=z; g.c=l; -h.a=s; h.b=a; h.c=m; -i.a=t; i.b=b; i.c=n; -j.a=u; j.b=c; j.c=o; -k.a=v; k.b=d; k.c=p; -l.a=w; l.b=e; l.c=q; -m.a=x; m.b=f; m.c=r; -n.a=y; n.b=g; n.c=s; -o.a=z; o.b=h; o.c=t; -p.a=a; p.b=i; p.c=u; -q.a=b; q.b=j; q.c=v; -r.a=c; r.b=k; r.c=w; -s.a=d; s.b=l; s.c=x; -t.a=e; t.b=m; t.c=y; -u.a=f; u.b=n; u.c=z; -v.a=g; v.b=o; v.c=a; -w.a=h; w.b=p; w.c=b; -x.a=i; x.b=q; x.c=c; -y.a=j; y.b=r; y.c=d; -z.a=k; z.b=s; z.c=e; - -return a.b.c.a.b.c.a.b.c.a.b.c.a.b.c.a.b.c; - +u32 v0=0; +{ + int v1=0; + while(v1) { + v1=1>>>v0!=0; + v0=v1/0; + } + return v1; +} """); - code.parse().opto().typeCheck().GCM().localSched(); - assertEquals("return Test._R;", code.print()); - assertEquals("Test._R{a=Test._C{a=Test._N{a=Test._Y{a=Test._J{a=Test._U{a=Test._F{a=Test._Q{a=Test._B{a=Test._M{a=Test._X{a=Test._I{a=Test._T{a=Test._E{a=Test._P{a=Test._A{a=Test._L{a=Test._W{a=Test._H{a=Test._S{a=Test._D{a=Test._O{a=Test._Z{a=Test._K{a=Test._V{a=Test._G{a=$cyclic,b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic}", Eval2.eval(code, 0)); + code.parse().opto(); + assertEquals("return 0;", code.print()); } + } diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java new file mode 100644 index 00000000..20274581 --- /dev/null +++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java @@ -0,0 +1,83 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import org.junit.Test; +import static org.junit.Assert.assertEquals; + +public class Chapter14FloatTest { + @Test public void testSubZeroFloat() { + var code = new CodeGen("flt x = arg; return 0-x;").parse().opto().typeCheck(); + // Compare strings so +0.0 and -0.0 remain distinct. + assertEquals("0.0", Eval2.eval(code,0)); + assertEquals("-1.0", Eval2.eval(code,1)); + } + + @Test public void testDeadNumericReturns() { + for( String[] test : new String[][] { + {"if(1) return 7; else return 2.5;", "7"}, + {"if(0) return 7; else return 2.5;", "2.5"}, + {"return 7; return 2.5;", "7"}, + {"return 2.5; return 7;", "2.5"} + } ) { + String src = test[0]; + var code = new CodeGen(src).parse().opto().typeCheck(); + assertEquals(src,test[1],Eval2.eval(code,0)); + } + } + + + @Test + public void testJig() { + CodeGen code = new CodeGen( +""" +return 0; +"""); + code.parse().opto(); + assertEquals("return 0;", code.print()); + assertEquals("0", Eval2.eval(code, 0)); + } + + @Test + public void testFloat() { + CodeGen code = new CodeGen( +""" +flt x = 3.14; +return 0; +"""); + code.parse().opto(); + assertEquals("return 0;", code.print()); + assertEquals("0", Eval2.eval(code, 0)); + } + + @Test + public void testSquareRoot() { + CodeGen code = new CodeGen( +""" +flt guess = arg; +while( 1 ) { + flt next = (arg/guess + guess)/2; + if( next == guess ) break; + guess = next; +} +return guess; +"""); + // TODO: One of the problems with <clinit> returning an OS error code, + // is it chops all results to int. + code.parse().opto(); + assertEquals("return Phi(Loop,(flt)arg,((Phi_guess+(ToFloat/Phi_guess))*0.5f));", code.print()); + assertEquals("3.0", Eval2.eval(code, 9)); + assertEquals("1.414213562373095", Eval2.eval(code, 2)); + } + + @Test + public void testFPOps() { + CodeGen code = new CodeGen( +""" +flt x = arg; +return x+1==x; +"""); + code.parse().opto(); + assertEquals("return ((flt)arg==(ToFloat+1.0f));", code.print()); + assertEquals("0", Eval2.eval(code, 1)); + } +} diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter14Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter14Test.java index 807905a8..f0ad6c2d 100644 --- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter14Test.java +++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter14Test.java @@ -93,7 +93,7 @@ return b; CodeGen code = new CodeGen( """ struct _Foo { u1 b; }; -_Foo !f = new _Foo; +!_Foo !f = new _Foo; f.b = 123; return f.b; """); diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter15Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter15Test.java index 24552062..85dc7b47 100644 --- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter15Test.java +++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter15Test.java @@ -25,8 +25,8 @@ return 3.14; public void testCyclic() { CodeGen code = new CodeGen( """ -struct _C { _C? l; }; -_C !c = new _C; +struct _C { !_C? !l; }; +!_C !c = new _C; c.l = c; return c; """); @@ -98,7 +98,7 @@ return a[0]; CodeGen code = new CodeGen( """ struct _A { int i; }; -_A?[] !a = new _A?[2]; +!_A?[] !a = new !_A?[2]; return a; """); code.parse().opto(); @@ -112,16 +112,16 @@ return a; """ struct _S { int x; flt y; }; // A new _S -_S !s = new _S; s.x=99; s.y = 3.14; +!_S !s = new _S; s.x=99; s.y = 3.14; // Double-d array of _Ss. Fill in one row. -_S?[]?[] !iss = new _S?[]?[2]; -iss[0] = new _S?[7]; +!_S?[]?[] !iss = new !_S?[]?[2]; +iss[0] = new !_S?[7]; iss[0][2] = s; // Now pull out the filled-in value, with null checks flt rez; -_S?[]? is = iss[arg]; +!_S?[]? is = iss[arg]; if( !is ) rez = 1.2; else { _S? i = is[2]; @@ -142,11 +142,11 @@ return rez; """ struct _S { int x; flt y; }; // A new _S -_S !s = new _S; s.x=99; s.y = 3.14; +!_S !s = new _S; s.x=99; s.y = 3.14; // Double-d array of _Ss. Fill in one row. -_S?[]?[] !iss = new _S?[]?[2]; -iss[0] = new _S?[7]; +!_S?[]?[] !iss = new !_S?[]?[2]; +iss[0] = new !_S?[7]; iss[0][2] = s; // Now pull out the filled-in value, with null checks @@ -167,9 +167,9 @@ return rez;"""); CodeGen code = new CodeGen( """ // Can we define a forward-reference array? -struct _Tree { _Tree?[]? _kids; }; -_Tree !root = new _Tree; -root._kids = new _Tree?[2]; // NO BANG SO ARRAY IS OF IMMUTABLE TREES???? +struct _Tree { !_Tree?[]? !_kids; }; +!_Tree !root = new _Tree; +root._kids = new !_Tree?[2]; // NO BANG SO ARRAY IS OF IMMUTABLE TREES???? root._kids[0] = new _Tree; return root; """); @@ -182,7 +182,7 @@ return root; public void testNestedStructAddMemProj() { CodeGen code = new CodeGen( """ -struct _S { int a; int[] b; new _S = { int[] ary -> b=ary; }; }; +struct _S { int a; int[] !b; new _S = { int[] ary -> b=ary; }; }; return 0; """); code.parse().opto(); @@ -227,8 +227,8 @@ return ary[1] * 1000 + ary[3]; // 1 * 1000 + 6 CodeGen code = new CodeGen( """ struct _S {int i; flt f;}; -_S !s1 = new _S; -_S !s2 = new _S; +!_S !s1 = new _S; +!_S !s2 = new _S; s2.i = 3; s2.f = 2.0; if (arg) s1 = new _S; diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter16Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter16Test.java index 80b0a2bf..23ec549d 100644 --- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter16Test.java +++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter16Test.java @@ -42,31 +42,6 @@ return x+y; assertEquals("5", Eval2.eval(code, 0)); } -// @Test -// public void testFinal0() { -// CodeGen code = new CodeGen( -//""" -//int !x=2; -//x=3; -//return x; -//"""); -// try { code.parse().opto(); fail(); } -// catch( Exception e ) { assertEquals("Cannot reassign final 'x'",e.getMessage()); } -// } - - @Test - public void testFinal1() { - CodeGen code = new CodeGen( -""" -int x=2, y=3; -if( arg ) { int x = y; x = x*x; y=x; } // Shadow final x -return y; -"""); - code.parse().opto(); - assertEquals("return Phi(Region,9,3);", code.print()); - assertEquals("3", Eval2.eval(code, 0)); - assertEquals("9", Eval2.eval(code, 1)); - } @Test public void testConstruct0() { @@ -81,18 +56,6 @@ return z.x; } @Test - public void testConstruct1() { - CodeGen code = new CodeGen(""" -struct _X { int !x; new _X = { int xx -> x=xx; }; }; -_X z = new _X(3); -return z.x; -"""); - code.parse().opto(); - assertEquals("return 3;", code.print()); - assertEquals("3", Eval2.eval(code, 0)); - } - - @Test public void testConstruct2() { CodeGen code = new CodeGen(""" struct _X { int x=3; new _X = { int xx -> x=xx; }; }; @@ -106,86 +69,11 @@ return z.x; @Test - public void testStructFinal0() { - CodeGen code = new CodeGen(""" -struct _Point { int !x, !y; new _Point = { int xx, int yy -> x=xx; y=yy; }; }; -_Point p = new _Point(3,4); -return p; -"""); - code.parse().opto(); - assertEquals("return (const)Test._Point;", code.print()); - assertEquals("Test._Point{x=3,y=4}", Eval2.eval(code, 0)); - } - - @Test - public void testStructFinal1() { - CodeGen code = new CodeGen(""" -struct _Point { int x=3, y=4; new _Point = { int xx, int yy -> x=xx; y=yy; }; }; -val p = new _Point(5,6); -p.x++; -return p; -"""); - try { code.parse().opto().typeCheck(); fail(); } - catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); } - } - - @Test - public void testStructFinal2() { - CodeGen code = new CodeGen(""" -struct _Point { int x=3, y=4; }; -val p = new _Point; -p.x++; -return p; -"""); - try { code.parse().opto().typeCheck(); fail(); } - catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); } - } - - @Test - public void testStructFinal3() { - CodeGen code = new CodeGen(""" -struct _Point { var x; var y; }; -_Point p = new _Point; -p.x++; -return p; -"""); - try { code.parse().opto(); fail(); } - catch( Exception e ) { assertEquals("'Test._Point' is not fully initialized, field 'x' needs to be set in a constructor",e.getMessage()); } - } - - @Test - public void testStructFinal4() { - CodeGen code = new CodeGen(""" -struct _Point { val x=2; val y=4; }; -_Point p = new _Point; -p.x++; -return p; -"""); - try { code.parse().opto().typeCheck(); fail(); } - catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); } - } - - @Test - public void testStructFinal5() { - CodeGen code = new CodeGen(""" -struct _Point { var x=3; var y=4; }; -_Point !p = new _Point; -p.x++; -return p; -"""); - code.parse().opto(); - assertEquals("return Test._Point;", code.print()); - assertEquals("Test._Point{x=4,y=4}", Eval2.eval(code, 0)); - } - - // Same as the Chapter13 test with the same name, but using the new - // constructor syntax - @Test public void testLinkedList1() { CodeGen code = new CodeGen( """ -struct _LLI { _LLI? next; int i; new _LLI = { _LLI? n, int ii -> next=n; i=ii; }; }; -_LLI? !head = null; +struct _LLI { !_LLI? !next; int i; new _LLI = { !_LLI? !n, int ii -> next=n; i=ii; }; }; +!_LLI? !head = null; while( arg ) { head = new _LLI(head,arg); arg = arg-1; @@ -206,7 +94,7 @@ return next.i; public void testLinkedList2() { CodeGen code = new CodeGen( """ -struct _LLI { _LLI? next; int i; new _LLI = { _LLI? n, int a -> +struct _LLI { !_LLI? !next; int i; new _LLI = { !_LLI? !n, int a -> next=n; int !tmp=a; while( a > 10 ) { @@ -215,7 +103,7 @@ struct _LLI { _LLI? next; int i; new _LLI = { _LLI? n, int a -> } i=tmp; }; }; -_LLI? !head = null; +!_LLI? !head = null; while( arg ) { head = new _LLI(head,arg); arg = arg-1; diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter17aTest.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter17aTest.java new file mode 100644 index 00000000..52acff57 --- /dev/null +++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter17aTest.java @@ -0,0 +1,109 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +public class Chapter17aTest { + + @Test + public void testFinal1() { + CodeGen code = new CodeGen( +""" +int x=2, y=3; +if( arg ) { int x = y; x = x*x; y=x; } // Shadow final x +return y; +"""); + code.parse().opto(); + assertEquals("return Phi(Region,9,3);", code.print()); + assertEquals("3", Eval2.eval(code, 0)); + assertEquals("9", Eval2.eval(code, 1)); + } + + @Test + public void testConstruct1() { + CodeGen code = new CodeGen(""" +struct _X { int ~x; new _X = { int xx -> x=xx; }; }; +_X z = new _X(3); +return z.x; +"""); + code.parse().opto(); + assertEquals("return 3;", code.print()); + assertEquals("3", Eval2.eval(code, 0)); + } + + @Test + public void testStructFinal0() { + CodeGen code = new CodeGen(""" +struct _Point { int ~x, ~y; new _Point = { int xx, int yy -> x=xx; y=yy; }; }; +_Point p = new _Point(3,4); +return p; +"""); + code.parse().opto(); + assertEquals("return (const)Test._Point;", code.print()); + assertEquals("Test._Point{x=3,y=4}", Eval2.eval(code, 0)); + } + + @Test + public void testStructFinal1() { + CodeGen code = new CodeGen(""" +struct _Point { int x=3, y=4; new _Point = { int xx, int yy -> x=xx; y=yy; }; }; +~_Point p = new _Point(5,6); +p.x++; +return p; +"""); + try { code.parse().opto().typeCheck(); fail(); } + catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); } + } + + @Test + public void testStructFinal2() { + CodeGen code = new CodeGen(""" +struct _Point { int x=3, y=4; }; +~_Point p = new _Point; +p.x++; +return p; +"""); + try { code.parse().opto().typeCheck(); fail(); } + catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); } + } + + @Test + public void testStructFinal3() { + CodeGen code = new CodeGen(""" +struct _Point { var x; var y; }; +_Point p = new _Point; +p.x++; +return p; +"""); + try { code.parse().opto(); fail(); } + catch( Exception e ) { assertEquals("'Test._Point' is not fully initialized, field 'x' needs to be set in a constructor",e.getMessage()); } + } + + @Test + public void testStructFinal4() { + CodeGen code = new CodeGen(""" +struct _Point { val x=2; val y=4; }; +_Point p = new _Point; +p.x++; +return p; +"""); + try { code.parse().opto().typeCheck(); fail(); } + catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); } + } + + @Test + public void testStructFinal5() { + CodeGen code = new CodeGen(""" +struct _Point { var x=3; var y=4; }; +!_Point !p = new _Point; +p.x++; +return p; +"""); + code.parse().opto(); + assertEquals("return Test._Point;", code.print()); + assertEquals("Test._Point{x=4,y=4}", Eval2.eval(code, 0)); + } + +} diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter17Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter17bTest.java index 79aecbff..ab1a59e1 100644 --- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter17Test.java +++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter17bTest.java @@ -5,7 +5,7 @@ import org.junit.Test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; -public class Chapter17Test { +public class Chapter17bTest { @Test public void testJig() { @@ -80,7 +80,7 @@ return xs[0]; public void testInc5() { CodeGen code = new CodeGen(""" struct _S { u16 x; }; -_S !s = new _S; +!_S !s = new _S; s.x--; return s.x; """); @@ -156,33 +156,33 @@ return s.x; } @Test public void testVar4() { - CodeGen code = new CodeGen("struct _S{int x;}; _S? s; s=new _S; s.x++; return s.x; // Ok, no initializer so x is mutable "); + CodeGen code = new CodeGen("struct _S{int x;}; !_S? !s; s=new _S; s.x++; return s.x; // Ok, no initializer so x is mutable "); code.parse().opto(); assertEquals("return 1;", code.print()); assertEquals("1", Eval2.eval(code, 0)); } @Test public void testVar5() { - CodeGen code = new CodeGen("struct _S{int x; new _S={ int xx -> x=xx; };}; _S? s; s=new _S(3); s.x++; return s.x; // Ok, no initializer so x is mutable "); + CodeGen code = new CodeGen("struct _S{int x; new _S={ int xx -> x=xx; };}; !_S? !s; s=new _S(3); s.x++; return s.x; // Ok, no initializer so x is mutable "); code.parse().opto(); assertEquals("return 4;", code.print()); assertEquals("4", Eval2.eval(code, 0)); } @Test public void testVar6() { - CodeGen code = new CodeGen("struct S{int x;}; val s = new S; s.x++; return s.x; // Error initializer so x is immutable "); + CodeGen code = new CodeGen("struct S{int x;}; ~S s = new S; s.x++; return s.x; // Error initializer so x is immutable "); try { code.parse().opto().typeCheck(); fail(); } catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); } } @Test public void testVar7() { - CodeGen code = new CodeGen("struct S{int x; new S={ int xx -> x=xx; };}; val s = new S(3); s.x++; return s.x; // Error initializer so x is immutable "); + CodeGen code = new CodeGen("struct S{int x; new S={ int xx -> x=xx; };}; ~S s = new S(3); s.x++; return s.x; // Error initializer so x is immutable "); try { code.parse().opto().typeCheck(); fail(); } catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); } } @Test public void testVar8() { - CodeGen code = new CodeGen("struct _S{int x;}; _S !s=new _S; s.x++; return s.x; // Ok, has '!' so s.x is mutable "); + CodeGen code = new CodeGen("struct _S{int x;}; !_S !s=new _S; s.x++; return s.x; // Ok, has '!' so s.x is mutable "); code.parse().opto(); assertEquals("return 1;", code.print()); assertEquals("1", Eval2.eval(code, 0)); @@ -196,7 +196,7 @@ return s.x; } @Test public void testVar10() { - CodeGen code = new CodeGen("struct S{int x;}; val s=new S; s.x++; return s.x; // Error, has val so x is immutable "); + CodeGen code = new CodeGen("struct S{int x;}; ~S s=new S; s.x++; return s.x; // Error, has val so x is immutable "); try { code.parse().opto().typeCheck(); fail(); } catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); } } @@ -204,15 +204,15 @@ return s.x; @Test public void testVar11() { CodeGen code = new CodeGen(""" struct Bar { int x; }; -Bar !bar = new Bar; +!Bar !bar = new Bar; bar.x = 3; // Ok, bar is mutable -struct Foo { Bar? !bar; int y; new Foo = { Bar? b -> bar = b; }; }; -Foo !foo = new Foo(bar); +struct Foo { !Bar? !bar; int y; new Foo = { !Bar? b -> bar = b; }; }; +!Foo !foo = new Foo(bar); foo.bar = bar; // Ok foo is mutable foo.bar.x++; // Ok foo and foo.bar and foo.bar.x are all mutable -val xfoo = foo; // Throw away mutability +~Foo xfoo = foo; // Throw away mutability xfoo.bar.x++; // Error, cannot mutate through xfoo return xfoo.bar.x; """); @@ -223,15 +223,15 @@ return xfoo.bar.x; @Test public void testVar12() { CodeGen code = new CodeGen(""" struct _Bar { int x; }; -_Bar !bar = new _Bar; +!_Bar !bar = new _Bar; bar.x = 3; // Ok, bar is mutable -struct _Foo { _Bar? !bar; int y; }; -_Foo !foo = new _Foo; +struct _Foo { !_Bar? !bar; int y; }; +!_Foo !foo = new _Foo; foo.bar = bar; // Ok bar is mutable foo.bar.x++; // Ok foo and foo.bar and foo.bar.x are all mutable -val xfoo = foo; // Throw away mutability +~_Foo xfoo = foo; // Throw away mutability int x4 = xfoo.bar.x; // Ok to read through xfoo, gets 4 foo.bar.x++; // Bumps to 5 int x5 = xfoo.bar.x; // Ok to read through xfoo, gets 5 @@ -256,7 +256,7 @@ int i,i++; public void testVar14() { CodeGen code = new CodeGen(""" struct B {}; -struct A { B b; new A = { -> +struct A { !B !b; new A = { -> return b; // read before init b = new B; }; }; @@ -269,10 +269,10 @@ A x = new A; @Test public void testVar15() { for( String fields : new String[] { - "B b; new A = { -> if (arg) b = new B; };", - "B b; new A = { -> if (arg) return 0; b = new B; };", - "B b; new A = { B b -> b = new B; };", // Argument shadows field - "new A = { -> }; B b;", // Field declared later + "!B !b; new A = { -> if (arg) b = new B; };", + "!B !b; new A = { -> if (arg) return 0; b = new B; };", + "!B !b; new A = { !B b -> b = new B; };", // Argument shadows field + "new A = { -> }; !B !b;", // Field declared later } ) { CodeGen code = new CodeGen("struct B {}; struct A { "+fields+" }; return 0;"); try { code.parse(); fail(fields); } @@ -282,16 +282,16 @@ A x = new A; @Test public void testDefaultConstructorFields() { - for( String field : new String[] { "B b;", "B !b;" } ) { + for( String field : new String[] { "!B !b;", "!B !b;" } ) { CodeGen code = new CodeGen("struct B {}; struct A { "+field+" }; return new A;"); try { code.parse(); fail(field); } catch( Parser.ParseException e ) { assertEquals("'Test.A' is not fully initialized, field 'b' needs to be set in a constructor",e.getMessage()); } } for( String fields : new String[] { "B b = new B;", - "B b; b = new B;", + "!B !b; b = new B;", "new A = { -> }; B b = new B;", - "B b; new A = { -> if (arg) { b = new B; return 0; } b = new B; };", + "!B !b; new A = { -> if (arg) { b = new B; return 0; } b = new B; };", } ) { CodeGen code = new CodeGen("struct B {}; struct A { "+fields+" }; return new A.b != null;"); code.parse().opto().typeCheck(); @@ -299,7 +299,7 @@ A x = new A; assertEquals("1",Eval2.eval(code,1)); } // No returning path means no object can escape uninitialized. - new CodeGen("struct B {}; struct A { B b; new A = { -> while (true) {} }; }; return 0;").parse(); + new CodeGen("struct B {}; struct A { !B !b; new A = { -> while (true) {} }; }; return 0;").parse(); } @Test @@ -308,7 +308,7 @@ A x = new A; """ struct S{ new S = { -> x = 2; }; }; val x = 1; -val s = new S; +~S s = new S; return x; """ ); @@ -323,10 +323,10 @@ return x; struct Person { bool focused; }; -val focus = { Person p -> +val focus = { !Person p -> p.focused = true; }; -Person me; // Person !me = new Person; +!Person !me; // !Person !me = new Person; focus(me); return me; """ @@ -361,8 +361,8 @@ return arg ? 0 : arg; public void testShortCircuitGuard() { CodeGen code = new CodeGen(""" struct S { int !x; }; -val get = { S s -> s.x; }; -S? !s = arg ? new S; +val get = { !S s -> s.x; }; +!S? !s = arg ? new S; if( !s || !arg ) return 0; return get(s); """); @@ -400,7 +400,7 @@ return b ? b.x++ + b.x++ : -1; public void testTrinary4() { // This test case will benefit from an unzipping transformation CodeGen code = new CodeGen(""" -struct _Bar { _Bar? next; int x; new _Bar = { _Bar? n, int xx -> next=n; x=xx; }; }; +struct _Bar { !_Bar? !next; int x; new _Bar = { !_Bar? !n, int xx -> next=n; x=xx; }; }; var b = arg ? new _Bar((arg==2) ? new _Bar(null,2), 1); return b ? b.next ? b.next.x : b.x; // parses "b ? (b.next ? b.next.x : b.x) : 0" """); @@ -515,8 +515,8 @@ for(;;arg++;) {} public void testForward0() { CodeGen code = new CodeGen(""" struct _A { - B? f1; - B? f2; + !B? !f1; + !B? !f2; }; return new _A; """); @@ -529,11 +529,11 @@ return new _A; public void testForward1() { CodeGen code = new CodeGen(""" struct _A { - B?[]? nil_array_of_b; - B?[] array_of_b; - new _A = { B?[] bs -> array_of_b = bs; }; + !B?[]? !nil_array_of_b; + !B?[] !array_of_b; + new _A = { !B?[] bs -> array_of_b = bs; }; }; -return new _A(new B?[0]).array_of_b; +return new _A(new !B?[0]).array_of_b; """); code.parse().opto(); assertEquals("return []*B?;", code.print()); @@ -544,8 +544,8 @@ return new _A(new B?[0]).array_of_b; @Test public void testLinkedList2() { CodeGen code = new CodeGen(""" -struct _LLI { _LLI? next; int i; new _LLI = { _LLI? n, int ii -> next=n; i=ii; }; }; -_LLI? !head = null; +struct _LLI { !_LLI? !next; int i; new _LLI = { !_LLI? !n, int ii -> next=n; i=ii; }; }; +!_LLI? !head = null; while( arg-- ) head = new _LLI(head,arg); int sum=0; diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter18Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter18Test.java index 2c61fb44..adc05f6f 100644 --- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter18Test.java +++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter18Test.java @@ -318,7 +318,7 @@ return 2; public void testFcn8() { CodeGen code = new CodeGen( """ -{int -> int}? _i2i = null; +{int -> int}? !_i2i = null; var _id = {{int->int} f-> return f;}; for(;;) { if (_i2i) return _i2i(arg); @@ -336,7 +336,7 @@ for(;;) { public void testFcn9() { CodeGen code = new CodeGen( """ -{int -> int}? i2i = null; +{int -> int}? !i2i = null; for(;;) { if (i2i) return i2i(arg); var x = {int i-> return i;}; @@ -357,13 +357,13 @@ struct _Person { int age; }; -val _fcn = { _Person?[] ps, int x -> +val _fcn = { !_Person?[] !ps, int x -> val tmp = ps[x]; if( ps[x] ) ps[x].age++; }; -var ps = new _Person?[2]; +var ps = new !_Person?[2]; ps[0] = new _Person; ps[1] = new _Person; return _fcn(ps,1); @@ -475,7 +475,7 @@ struct S { { int } f = { -> return x(); }; }; val x = { -> return 1; }; -S? s = null; +!S? !s = null; for(;;) { if (s) return s.f; } @@ -509,7 +509,7 @@ return _f2f(_o)(1); struct _Person { int coffee_count; }; -_Person !p = new _Person; +!_Person !p = new _Person; p.coffee_count += 1; return p.coffee_count; """); diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter19Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter19Test.java index 5ab7ba97..8cd5137c 100644 --- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter19Test.java +++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter19Test.java @@ -46,13 +46,13 @@ return 0; String src = """ struct String { - u8[~] cs; + u8[~] !cs; int _hashCode; new String = { u8[~] s -> cs=s; }; }; // Compare two Strings -val equals = { String self, String s -> +val equals = { !String !self, !String s -> if( self == s ) return true; if( self.cs# != s.cs# ) return false; for( int i=0; i< self.cs#; i++ ) @@ -62,7 +62,7 @@ val equals = { String self, String s -> }; -val _hashCodeString = { String self -> +val _hashCodeString = { !String self -> int hash=0; for( int i=0; i< self.cs#; i++ ) hash = hash*31 + self.cs[i]; @@ -71,7 +71,7 @@ val _hashCodeString = { String self -> }; // Return the String hashCode (cached, and never 0) -val hashCode = { String self -> +val hashCode = { !String self -> self._hashCode ? self._hashCode : (self._hashCode = _hashCodeString(self)); @@ -89,14 +89,14 @@ hashCode(new String("Hello, World!")); String src = """ struct String { - u8[~] cs; + u8[~] !cs; int _hashCode; new String = { u8[~] s -> cs=s; }; }; -val f = { String self -> 7; }; +val f = { !String self -> 7; }; -val hashCode = { String self -> +val hashCode = { !String self -> self._hashCode ? 3 : (self._hashCode = f(self)); @@ -283,7 +283,7 @@ return sum;"""); public void testAlloc1() { CodeGen code = new CodeGen( """ -struct _S { int a; _S? c; }; +struct _S { int a; !_S? !c; }; return new _S;"""); code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); assertEquals("return Test._S;", code.print()); diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter20Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter20Test.java index 245ea745..262b8604 100644 --- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter20Test.java +++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter20Test.java @@ -15,7 +15,7 @@ public class Chapter20Test { // A real integer return merged with a synthetic exit. "if(arg) while(1) {} return arg+1;", // TOP must not widen a non-null pointer or constrain a float register. - "struct S { int x; }; S !s=new S; if(arg) while(1) {} return s;", + "struct S { int x; }; !S !s=new S; if(arg) while(1) {} return s;", "if(arg) while(1) {} return 1.5;", // Several synthetic exits into the same return. "if(arg==1) while(1) {} if(arg==2) while(1) {} return arg+3;", @@ -23,7 +23,7 @@ public class Chapter20Test { "while(1) { arg+=1; }", "if(arg) while(1) {} while(1) {}", // Loop-carried memory and scalar self-conflicts exercise spilling. - "struct S { int x; int y; }; S !a=new S; if(arg) while(1) { a.x+=arg; a.y+=a.x; } return a.x+a.y;" + "struct S { int x; int y; }; !S !a=new S; if(arg) while(1) { a.x+=arg; a.y+=a.x; } return a.x+a.y;" }; for( String target : new String[]{"x86_64_v2","riscv","arm"} ) for( String src : sources ) { @@ -44,7 +44,7 @@ public class Chapter20Test { @Test public void testNarrowStoreMasks() { for( String type : new String[]{"i8","u8","i16","u16"} ) { - CodeGen code = new CodeGen("struct S { "+type+" x; }; S !s=new S; s.x=arg; return 0;") + CodeGen code = new CodeGen("struct S { "+type+" x; }; !S !s=new S; s.x=arg; return 0;") .driver(CodeGen.Phase.RegAlloc,"x86_64_v2","SystemV"); int stores=0; for( var bb : code._cfg ) @@ -60,9 +60,9 @@ public class Chapter20Test { @Test public void testInlinedReturnValue() { String src = """ struct S { int x; }; -val f = { S s -> s.x = g(); }; +val f = { !S s -> s.x = g(); }; val g = { -> 123; }; -S !s = new S; +!S !s = new S; return f(s); """; // The entry of g can disappear before its Return is folded into f. @@ -225,13 +225,13 @@ return ary[1] * 1000 + ary[3]; // 1 * 1000 + 6 String src = """ struct String { - u8[] cs; + u8[] !cs; int _hashCode; new String = { u8[] data -> cs=data; }; }; // Compare two Strings -val equals = { String self, String s -> +val equals = { !String !self, !String s -> if( self == s ) return true; if( self.cs# != s.cs# ) return false; for( int i=0; i< self.cs#; i++ ) @@ -241,13 +241,13 @@ val equals = { String self, String s -> }; // Return the String hashCode (cached, and never 0) -val hashCode = { String self -> +val hashCode = { !String self -> self._hashCode ? self._hashCode : (self._hashCode = _hashCodeString(self)); }; -val _hashCodeString = { String self -> +val _hashCodeString = { !String self -> int hash=0; for( int i=0; i< self.cs#; i++ ) hash = hash*31 + self.cs[i]; diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter21Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter21Test.java index 93a9e63f..bfce93b9 100644 --- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter21Test.java +++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter21Test.java @@ -17,8 +17,8 @@ public class Chapter21Test { @Test public void testReadBeforeConditionalStore() { var code = new CodeGen(""" - struct S { int x; }; S !a=new S; S !b=new S; - a.x=10; b.x=20; S !p=a; if(arg&1) p=b; + struct S { int x; }; !S !a=new S; !S !b=new S; + a.x=10; b.x=20; !S !p=a; if(arg&1) p=b; int sum=0; for(int i=0; i<6; i++) { sum+=a.x; @@ -281,7 +281,7 @@ return new _s0.v1; } @Test public void testInfinite() { - String src = "struct _S { int i; }; _S !s = new _S; while(1) s.i++; return s.i;"; + String src = "struct _S { int i; }; !_S !s = new _S; while(1) s.i++; return s.i;"; testCPU(src,"x86_64_v2", "SystemV",0,"return Top;"); testCPU(src,"riscv" , "SystemV",2,"return Top;"); testCPU(src,"arm" , "SystemV",2,"return Top;"); @@ -311,7 +311,7 @@ return ary[1] * 1000 + ary[3]; // 1 * 1000 + 6 """ struct _S { int f; }; var v0 = new _S; -_S? v1; +!_S? !v1; if (arg) v1 = new _S; if (v1) { v0.f = v1.f; @@ -330,13 +330,13 @@ return v0; String src = """ struct String { - u8[] cs; + u8[] !cs; int _hashCode; new String = { u8[] data -> cs=data; }; }; // Compare two Strings -val equals = { String self, String s -> +val equals = { !String !self, !String s -> if( self == s ) return true; if( self.cs# != s.cs# ) return false; for( int i=0; i< self.cs#; i++ ) @@ -346,13 +346,13 @@ val equals = { String self, String s -> }; // Return the String hashCode (cached, and never 0) -val hashCode = { String self -> +val hashCode = { !String self -> self._hashCode ? self._hashCode : (self._hashCode = _hashCodeString(self)); }; -val _hashCodeString = { String self -> +val _hashCodeString = { !String self -> int hash=0; for( int i=0; i< self.cs#; i++ ) hash = hash*31 + self.cs[i]; @@ -369,13 +369,13 @@ val _hashCodeString = { String self -> String src = """ struct String { - u8[] cs; + u8[] !cs; int _hashCode; new String = { u8[] data -> cs=data; }; }; // Compare two Strings -val equals = { String self, String s -> +val equals = { !String !self, !String s -> if( self == s ) return true; if( self.cs# != s.cs# ) return false; for( int i=0; i< self.cs#; i++ ) @@ -385,13 +385,13 @@ val equals = { String self, String s -> }; // Return the String hashCode (cached, and never 0) -val hashCode = { String self -> +val hashCode = { !String self -> self._hashCode ? self._hashCode : (self._hashCode = _hashCodeString(self)); }; -val _hashCodeString = { String self -> +val _hashCodeString = { !String self -> int hash=0; for( int i=0; i< self.cs#; i++ ) hash = hash*31 + self.cs[i]; @@ -570,7 +570,7 @@ val fib = { int n -> i32 age; }; - val fcn = { Person?[] ps, int x -> + val fcn = { !Person?[] !ps, int x -> if( ps[x] ) ps[x].age++; }; diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter22Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter22Test.java index 82211985..ed678334 100644 --- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter22Test.java +++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter22Test.java @@ -141,7 +141,7 @@ return new s0.v1; @Test public void testSextFail() throws IOException { String src = """ struct _Person { i32 age;}; -_Person !p = new _Person; +!_Person !p = new _Person; p.age = (arg<<17)>>17; return 0; """; @@ -164,7 +164,7 @@ return 0; @Test public void testSextFail2() throws IOException { String src = """ struct _Person { i32 age;}; - _Person !p = new _Person; + !_Person !p = new _Person; p.age = (arg<<48)>>48; return 0; """; @@ -188,7 +188,7 @@ return 0; String src = """ // Should fold away sign extend struct _Person { i8 age;}; -_Person !p = new _Person; +!_Person !p = new _Person; p.age = (arg<<48)>>48; return 0; """; @@ -216,7 +216,7 @@ struct Person { // Exports a field typed singleton+final *class:Person{age:i32} i32 age; }; -val fcn = { Person?[] ps, int x -> // exports a field with a constant fcn ptr; exports the fcn also +val fcn = { !Person?[] !ps, int x -> // exports a field with a constant fcn ptr; exports the fcn also if( ps[x] ) ps[x].age++; }; @@ -270,12 +270,12 @@ val fcn = { Person?[] ps, int x -> // exports a field with a constant fcn ptr; e public void testCoRecur() { String src = """ val x = 5; // aa.az; // Error to self-define forward ref -struct _A { Test._B? b; Test._C? c; i64 ax; val az = x*2; new _A = { i64 x -> ax=x; }; }; -struct _B { Test._A? a; Test._C? c; f32 bx; val bz = x*3; new _B = { f32 x, Test._A? aa -> bx=x; a=aa; }; }; -struct _C { Test._A? a; Test._B? b; f64 cx; val cz = x*x; new _C = { f64 x, Test._A? aa, Test._B? bb -> cx=x; a=aa; b=bb; }; }; -_A !aa = new _A(17); -_B !bb = new _B(3.14f, aa); -_C !cc = new _C(2.73, aa, bb); +struct _A { !Test._B? !b; !Test._C? !c; i64 ax; val az = x*2; new _A = { i64 x -> ax=x; }; }; +struct _B { !Test._A? !a; !Test._C? !c; f32 bx; val bz = x*3; new _B = { f32 x, !Test._A? aa -> bx=x; a=aa; }; }; +struct _C { !Test._A? !a; !Test._B? !b; f64 cx; val cz = x*x; new _C = { f64 x, !Test._A? aa, !Test._B? bb -> cx=x; a=aa; b=bb; }; }; +!_A !aa = new _A(17); +!_B !bb = new _B(3.14f, aa); +!_C !cc = new _C(2.73, aa, bb); aa.b = bb; aa.c = cc; bb.c = cc; diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter23Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter23Test.java index dad1dde4..7c46567a 100644 --- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter23Test.java +++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter23Test.java @@ -142,7 +142,7 @@ return !(!s || !arg) ? 1 : 0; public void testAndPtr() throws IOException { // Todo: have one src here String src = """ -struct S { S? fld; new S = { S? f -> fld=f; }; }; +struct S { !S? !fld; new S = { !S? f -> fld=f; }; }; val ptr = arg == 1 ? null : new S(arg==1 ? null : new S(null)); return ptr && ptr.fld ? "true" : "false"; """; @@ -257,7 +257,7 @@ return (a && sq_noInline(0)) // A hypothetical scanner class struct _Scan { int !x; - u8[~] buf; + u8[~] !buf; new _Scan = { u8[~] b -> buf=b; }; // Skip whitespace val skip = { -> @@ -272,7 +272,7 @@ struct _Scan { return true; }; }; -_Scan !_s = new _Scan(" q"); +!_Scan !_s = new _Scan(" q"); return _s.peek('q'); """; @@ -288,7 +288,7 @@ return _s.peek('q'); // A hypothetical scanner class struct _Scan { int !x; - u8[~] buf; + u8[~] !buf; new _Scan = { u8[~] b -> buf=b; }; // Skip whitespace val skip = { -> @@ -297,14 +297,14 @@ struct _Scan { return self; }; }; -val s = new _Scan(" q"); +~_Scan s = new _Scan(" q"); return s.skip().x; """; try { new CodeGen(src).parse().opto().typeCheck(); fail(); } catch( Exception e ) { // Bad error message, but basically requires a mutable 'x' field. - assertEquals("Argument #0 isa *Test._Scan {i64 x; *[]u8[final] buf; { *Test._Scan -> *Test._Scan {i64 !x; *[]u8 buf; {[ 5]} skip; } #[ 5]} skip; }, but must be a *Test._Scan {i64 !x; *[]u8[final] buf; { *Test._Scan -> *Test._Scan #[ 5]} skip; }",e.getMessage()); + org.junit.Assert.assertTrue(e.getMessage().startsWith("Argument #0 isa")); } }; @@ -315,7 +315,7 @@ return s.skip().x; // A hypothetical scanner class struct _Scan { int !x; - u8[~] buf; + u8[~] !buf; new _Scan = { u8[~] b -> buf=b; }; // Skip whitespace val skip = { -> @@ -327,7 +327,7 @@ struct _Scan { buf[x++]==ch; }; }; -_Scan !_s = new _Scan(" [1,2]"); +!_Scan !_s = new _Scan(" [1,2]"); return _s.require('['); """; diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter25Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter25Test.java index cad16f2b..b06f90d8 100644 --- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter25Test.java +++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter25Test.java @@ -25,10 +25,20 @@ import org.junit.Test; import static org.junit.Assert.*; public class Chapter25Test { + + @Ignore + @Test + public void testJig() { + String src = "int x = arg/17; if( arg == 0 ) return arg + x; return arg + x;"; + CodeGen code = new CodeGen(src).driver(CodeGen.Phase.TypeCheck); + assertEquals("return (arg+3);", code.print()); + assertEquals("3",Eval2.eval(code,0)); + } + @Test public void testNeverReturnPointerRiscV() { new CodeGen(""" struct _S { int x; }; - _S !s = new _S; + !_S !s = new _S; if(arg) while(1) { s.x += arg; } return s; """).driver(CodeGen.Phase.Encoding,"riscv","SystemV"); @@ -119,17 +129,6 @@ public class Chapter25Test { com.seaofnodes.simple.codegen.PrintRegTestSupport.checkConstantPool(); } - - @Ignore - @Test - public void testJig() { - String src = "struct I { int x; }; struct F { I p; }; return new F.p.x;"; - CodeGen code = new CodeGen(src).driver(CodeGen.Phase.TypeCheck); - assertEquals("2",Eval2.eval(code,0)); - } - - - private static final String SYS_BLDDIR = "build/objs/lib_"+TestC.CPU_ABI; private static final File SYS_FILE = new File(SYS_BLDDIR+"/sys.o"); diff --git a/chapter25/src/test/java/com/seaofnodes/simple/CompoundAssignmentTest.java b/chapter25/src/test/java/com/seaofnodes/simple/CompoundAssignmentTest.java index 0d67d3c5..48f739cf 100644 --- a/chapter25/src/test/java/com/seaofnodes/simple/CompoundAssignmentTest.java +++ b/chapter25/src/test/java/com/seaofnodes/simple/CompoundAssignmentTest.java @@ -73,7 +73,7 @@ public class CompoundAssignmentTest { @Test public void testFields() { for (String op : OPS) { long expected = apply(op, -8, 3); - check("struct S { int x; }; S !s=new S; s.x=arg; int r=(s.x " + + check("struct S { int x; }; !S !s=new S; s.x=arg; int r=(s.x " + op + " 3); return s.x*10+r;", -8, expected*11); } } diff --git a/chapter25/src/test/java/com/seaofnodes/simple/MemoryEncodingTest.java b/chapter25/src/test/java/com/seaofnodes/simple/MemoryEncodingTest.java index 2786b403..9406f480 100644 --- a/chapter25/src/test/java/com/seaofnodes/simple/MemoryEncodingTest.java +++ b/chapter25/src/test/java/com/seaofnodes/simple/MemoryEncodingTest.java @@ -22,8 +22,8 @@ public class MemoryEncodingTest { """, // One possibly aliasing arm must reject the unchanged-memory proof. """ - struct S { int x; }; S !a=new S; S !b=new S; - a.x=10; b.x=20; S !p=a; if(arg&1) p=b; + struct S { int x; }; !S !a=new S; !S !b=new S; + a.x=10; b.x=20; !S !p=a; if(arg&1) p=b; int sum=0; for(int i=0; i<6; i++) { if(i==2) p.x=30; @@ -34,8 +34,8 @@ public class MemoryEncodingTest { """, // Unchanged memory does not imply an unchanged loop-carried pointer. """ - struct S { int x; }; S !a=new S; S !b=new S; - a.x=10; b.x=20; S !p=a; int sum=0; + struct S { int x; }; !S !a=new S; !S !b=new S; + a.x=10; b.x=20; !S !p=a; int sum=0; for(int i=0; i<6; i++) { if(i==2) a.x=30; sum+=p.x; @@ -45,7 +45,7 @@ public class MemoryEncodingTest { """, // All arms fold, but their values differ: build a value Phi. """ - struct S { int x; }; S !a=new S; a.x=1; int sum=0; + struct S { int x; }; !S !a=new S; a.x=1; int sum=0; for(int i=0; i<6; i++) { sum+=a.x; if(i&1) a.x=2; @@ -78,9 +78,9 @@ public class MemoryEncodingTest { @Test public void testKeepReadsBeforeWrites() { var code = new CodeGen(""" struct S { int x; int y; }; - S !a = new S; S !b = new S; + !S !a = new S; !S !b = new S; a.x=arg; b.x=arg+1; - S !p=a; S !q=b; + !S !p=a; !S !q=b; if (arg<0) { p=b; q=a; } int v; if (arg>1) { v=p.x; p.x=41; p.y=5; } diff --git a/chapter25/src/test/java/com/seaofnodes/simple/MergeSortTest.java b/chapter25/src/test/java/com/seaofnodes/simple/MergeSortTest.java index ba8fc178..f5d3d146 100644 --- a/chapter25/src/test/java/com/seaofnodes/simple/MergeSortTest.java +++ b/chapter25/src/test/java/com/seaofnodes/simple/MergeSortTest.java @@ -22,7 +22,7 @@ val merge_sort = { int[] a -> _split_merge(a, 0, a#, b); }; -val _split_merge = { int[] b, int begin, int end, int[] a -> +val _split_merge = { int[] !b, int begin, int end, int[] a -> if (end - begin <= 1) return 0; int middle = (end + begin) / 2; @@ -32,7 +32,7 @@ val _split_merge = { int[] b, int begin, int end, int[] a -> return 0; }; -val _merge = { int[] b, int begin, int middle, 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++) { @@ -47,7 +47,7 @@ val _merge = { int[] b, int begin, int middle, int end, int[] a -> } }; -val _copy_array = { int[] a, int begin, int end, int[] b -> +val _copy_array = { int[] !a, int begin, int end, int[] b -> for (int k = begin; k < end; k++) b[k] = a[k]; }; diff --git a/chapter25/src/test/java/com/seaofnodes/simple/MutabilityTest.java b/chapter25/src/test/java/com/seaofnodes/simple/MutabilityTest.java new file mode 100644 index 00000000..60e4c5aa --- /dev/null +++ b/chapter25/src/test/java/com/seaofnodes/simple/MutabilityTest.java @@ -0,0 +1,67 @@ +package com.seaofnodes.simple; + +import org.junit.Test; +import static org.junit.Assert.*; +import com.seaofnodes.simple.codegen.CodeGen; + +public class MutabilityTest { + private static void ok(String src, String result) { + var code = new CodeGen(src).parse().opto().typeCheck(); + assertEquals(result,Eval2.eval(code,0)); + } + private static void bad(String src) { + try { + new CodeGen(src).parse().opto().typeCheck(); + } catch( RuntimeException expected ) { + String msg = expected.getMessage(); + assertTrue("Expected a permission error, got: "+expected, + msg!=null && (msg.contains("final") || msg.contains("not of declared type") || msg.contains("Cannot store") || msg.contains("Argument #"))); + return; + } + fail("Accepted forbidden write: "+src); + } + + @Test public void testIndependentBindingsAndViews() { + ok("struct P { int x; new P = { int v -> x=v; }; }; !P ~p=new P(0); p.x=7; return p.x;", "7"); + ok("struct P { int x; new P = { int v -> x=v; }; }; ~P !p=new P(3); p=new P(7); return p.x;", "7"); + bad("struct P { int x; new P = { int v -> x=v; }; }; !P ~p=new P(0); p=new P(0); return 0;"); + bad("struct P { int x; new P = { int v -> x=v; }; }; ~P !p=new P(0); p.x=7; return 0;"); + bad("int ~x=3; x=7; return x;"); + ok("int !x=3; x=7; return x;", "7"); + } + + @Test public void testDeepViewAndAliases() { + ok("struct P { int x; new P = { int v -> x=v; }; }; !P ~p=new P(0); ~P ~r=p; p.x=7; return r.x;", "7"); + bad("struct P { int x; new P = { int v -> x=v; }; }; struct Box { !P p; new Box = { !P v -> p=v; }; }; !P p=new P(0); Box b=new Box(p); b.p.x=7; return 0;"); + bad("struct P { int x; new P = { int v -> x=v; }; }; P p=new P(0); !P q=p; q.x=7; return 0;"); + bad("struct P { P? next; int x; new P = { P? v -> next=v; }; }; !P p=new P(new P(null)); p.next.x=7; return 0;"); + bad("struct P { !P? next; int x; new P = { !P? v -> next=v; }; }; P p=new P(new P(null)); p.next.x=7; return 0;"); + } + + @Test public void testInferencePreservesAccess() { + ok("struct P { int x; }; val p=new P; p.x=7; return p.x;", "7"); + bad("struct P { int x; }; val p=new P; p=new P; return 0;"); + bad("struct P { int x; }; P p=new P; var q=p; q.x=7; return 0;"); + } + + @Test public void testParameterPermissions() { + ok("struct P { int x; }; val f={ !P p -> p.x=7; return p.x; }; return f(new P);", "7"); + ok("val f={ int ~x -> return x; }; return f(7);", "7"); + bad("val f={ int ~x -> x=7; return x; }; return f(3);"); + bad("struct P { int x; }; val f={ P p -> p.x=7; return p.x; }; return f(new P);"); + bad("struct P { int x; }; val f={ !P p -> p.x=7; return p.x; }; P p=new P; return f(p);"); + bad("struct P { int x; }; val f={ P?[] a -> return a#; }; !P?[] a=new !P?[1]; return f(a);"); + } + + @Test public void testArrayLayers() { + bad("struct P { int x; }; !P?[] rw=new !P?[1]; P?[] ro=rw; rw[0]=new P; ro[0].x=7; return 0;"); + ok("u8[] a=new u8[2]; a[0]=7; return a[0];", "7"); + ok("struct P { int x; new P = { int v -> x=v; }; }; P?[] a=new P?[1]; a[0]=new P(7); return a[0].x;", "7"); + bad("struct P { int x; new P = { int v -> x=v; }; }; P?[] a=new P?[1]; a[0]=new P(0); a[0].x=7; return 0;"); + ok("struct P { int x; new P = { int v -> x=v; }; }; !P?[] a=new !P?[1]; a[0]=new P(0); a[0].x=7; return a[0].x;", "7"); + bad("u8[~] a=new u8[1]; a[0]=7; return 0;"); + ok("u8[~]?[] rows=new u8[~]?[1]; rows[0]=new u8[1]; return rows#;", "1"); + bad("u8[~]?[] rows=new u8[~]?[1]; rows[0]=new u8[1]; rows[0][0]=7; return 0;"); + bad("u8[]?[~] rows=new u8[]?[1]; rows[0]=new u8[1]; return 0;"); + } +} diff --git a/chapter25/src/test/java/com/seaofnodes/simple/TypeTest.java b/chapter25/src/test/java/com/seaofnodes/simple/TypeTest.java index b9cba5e8..f6099607 100644 --- a/chapter25/src/test/java/com/seaofnodes/simple/TypeTest.java +++ b/chapter25/src/test/java/com/seaofnodes/simple/TypeTest.java @@ -90,7 +90,7 @@ public class TypeTest { Assert.assertNotEquals(ptr1, ptr2); Assert.assertEquals(ptr1, ptr1.dual().dual()); - Assert.assertEquals(TypeMemPtr.makeNullable(TypeStruct.BOT), ptr1.meet(ptr2nil)); + Assert.assertEquals(TypeMemPtr.makeNullable(TypeStruct.BOT).withAccess(false), ptr1.meet(ptr2nil)); TypeMemPtr TOP = TypeMemPtr.TOP; TypeMemPtr BOT = TypeMemPtr.makeNullable(TypeStruct.BOT); @@ -99,7 +99,7 @@ public class TypeTest { Type PTR_meet_NULL = NULL.meet(PTR); Assert.assertEquals(BOT, PTR_meet_NULL); Type ptr1_meet_ptr2 = ptr1.meet(ptr2); - Assert.assertEquals(PTR, ptr1_meet_ptr2); + Assert.assertEquals(PTR.withAccess(false), ptr1_meet_ptr2); Type NULL_join_ptr1 = NULL.join(ptr1); Assert.assertEquals(TypePtr.XNPTR, NULL_join_ptr1); Type NULL_join_PTR = PTR.join(NULL); diff --git a/chapter25/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java b/chapter25/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java index 7ecde7c0..c5e94047 100644 --- a/chapter25/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java +++ b/chapter25/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java @@ -445,7 +445,7 @@ public class ScriptGenerator { for (int i=0; i<fields.length; i++) { var fieldName = getRandomName(); var type = getType(true); - printIndentation().append(type.name).append(" ").append(fieldName).append(";\n"); + printIndentation().append(declType(type)).append(" !").append(fieldName).append(";\n"); fields[i] = new TypeStruct.Field(fieldName.toString(), type, struct); } indentation -= INDENTATION; @@ -642,15 +642,17 @@ public class ScriptGenerator { return 0; } - /** - * Generate a declaration statement. - * @return 0 - */ + // This generator mutates references and fields; request both permissions. + private static String declType(Type type) { + return (type instanceof TypeStruct || type instanceof TypeNullable ? "!" : "") + type.name; + } + + /** Generate a declaration statement. */ public int genDecl() { var type = getType(); var name = getVarName(); // Always make them mutable - sb.append(generateInvalid() ? getRandomName() : type.name).append(" !").append(name); + sb.append(generateInvalid() ? getRandomName() : declType(type)).append(" !").append(name); if (!(type instanceof TypeNullable) || random.nextBoolean()) { sb.append("="); genExpression(type, true, type==TYPE_BOOL); diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter20Test-testString.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter20Test-testString.smp index 8c56636f..f66ad621 100644 --- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter20Test-testString.smp +++ b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter20Test-testString.smp @@ -4,7 +4,7 @@ struct String { int _hashCode; }; -val equals = { String self, String s -> +val equals = { !String !self, !String s -> if( self == s ) return true; if( self.cs# != s.cs# ) return false; for( int i=0; i< self.cs#; i++ ) @@ -13,13 +13,13 @@ val equals = { String self, String s -> return true; }; -val hashCode = { String self -> +val hashCode = { !String self -> self._hashCode ? self._hashCode : (self._hashCode = _hashCodeString(self)); }; -val _hashCodeString = { String self -> +val _hashCodeString = { !String self -> int hash=0; if( self.cs ) { for( int i=0; i< self.cs#; i++ ) @@ -29,7 +29,7 @@ val _hashCodeString = { String self -> return hash; }; -String !s = new String(new u8[17]); +!String !s = new String(new u8[17]); s.cs[0] = 67; // C s.cs[1] = 108; // l hashCode(s); diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21AllocTest-testString.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21AllocTest-testString.smp index 86220b7a..586ccb85 100644 --- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21AllocTest-testString.smp +++ b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21AllocTest-testString.smp @@ -6,7 +6,7 @@ struct String { }; // Compare two Strings -val equals = { String self, String s -> +val equals = { !String !self, !String s -> if( self == s ) return true; if( self.cs# != s.cs# ) return false; for( int i=0; i< self.cs#; i++ ) @@ -16,13 +16,13 @@ val equals = { String self, String s -> }; // Return the String hashCode (cached, and never 0) -val hashCode = { String self -> +val hashCode = { !String self -> self._hashCode ? self._hashCode : (self._hashCode = _hashCodeString(self)); }; -val _hashCodeString = { String self -> +val _hashCodeString = { !String self -> int hash=0; if( self.cs ) for( int i=0; i< self.cs#; i++ ) diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testAntiDeps1.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testAntiDeps1.smp index baf77c92..950e282d 100644 --- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testAntiDeps1.smp +++ b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testAntiDeps1.smp @@ -1,6 +1,6 @@ struct S { int f; }; var v0 = new S; -S? v1; +!S? !v1; if (arg) v1 = new S; if (v1) { v0.f = v1.f; diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testInfinite.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testInfinite.smp index 1e4557d3..5e517b61 100644 --- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testInfinite.smp +++ b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testInfinite.smp @@ -1 +1 @@ -struct S { int i; }; S !s = new S; while(1) s.i++; +struct S { int i; }; !S !s = new S; while(1) s.i++; diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testPerson.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testPerson.smp index 06c2861e..09285034 100644 --- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testPerson.smp +++ b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testPerson.smp @@ -3,7 +3,7 @@ struct Person { int age; }; -val fcn = { Person?[] ps, int x -> +val fcn = { !Person?[] ps, int x -> if( ps[x] ) ps[x].age++; }; diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testStringExport.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testStringExport.smp index 3a696d66..f3d34b0a 100644 --- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testStringExport.smp +++ b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testStringExport.smp @@ -5,7 +5,7 @@ struct String { }; // Compare two Strings -val equals = { String self, String s -> +val equals = { !String !self, !String s -> if( self == s ) return true; if( self.cs# != s.cs# ) return false; for( int i=0; i< self.cs#; i++ ) @@ -15,13 +15,13 @@ val equals = { String self, String s -> }; // Return the String hashCode (cached, and never 0) -val hashCode = { String self -> +val hashCode = { !String self -> self._hashCode ? self._hashCode : (self._hashCode = _hashCodeString(self)); }; -val _hashCodeString = { String self -> +val _hashCodeString = { !String self -> int hash=0; for( int i=0; i< self.cs#; i++ ) hash = hash*31 + self.cs[i]; diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testInfiniteReturn.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testInfiniteReturn.smp index 523fc6f9..f9866025 100644 --- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testInfiniteReturn.smp +++ b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testInfiniteReturn.smp @@ -1 +1 @@ -struct S { int i; }; S !s = new S; while(1) s.i++; return s.i; +struct S { int i; }; !S !s = new S; while(1) s.i++; return s.i; diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testPerson.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testPerson.smp index a3cafc9a..f860091d 100644 --- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testPerson.smp +++ b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testPerson.smp @@ -2,7 +2,7 @@ struct Person { i32 age; }; -val fcn = { Person?[] ps, int x -> +val fcn = { !Person?[] ps, int x -> if( ps[x] ) ps[x].age++; }; diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testSextFail.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testSextFail.smp index 2d431705..9558bca4 100644 --- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testSextFail.smp +++ b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testSextFail.smp @@ -1,4 +1,4 @@ struct Person { i32 age;}; -Person !p = new Person; +!Person !p = new Person; p.age = (arg<<17)>>17; return 0; diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testSextFail2.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testSextFail2.smp index 01e070c8..d3a27319 100644 --- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testSextFail2.smp +++ b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testSextFail2.smp @@ -1,5 +1,5 @@ struct Person { i32 age;}; - Person !p = new Person; + !Person !p = new Person; p.age = (arg<<48)>>48; return 0; diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testSextSuccess.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testSextSuccess.smp index 6d11a431..fdf4a7e9 100644 --- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testSextSuccess.smp +++ b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testSextSuccess.smp @@ -1,5 +1,5 @@ // Should fold away sign extend struct Person { i8 age;}; -Person !p = new Person; +!Person !p = new Person; p.age = (arg<<48)>>48; return 0; diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23AllocTest-testString.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23AllocTest-testString.smp index 1a4d55c1..4ef375e9 100644 --- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23AllocTest-testString.smp +++ b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23AllocTest-testString.smp @@ -6,7 +6,7 @@ struct String { }; // Compare two Strings -val equals = { String self, String s -> +val equals = { !String !self, !String s -> if( self == s ) return true; if( self.cs# != s.cs# ) return false; for( int i=0; i< self.cs#; i++ ) @@ -16,13 +16,13 @@ val equals = { String self, String s -> }; // Return the String hashCode (cached, and never 0) -val hashCode = { String self -> +val hashCode = { !String self -> self._hashCode ? self._hashCode : (self._hashCode = _hashCodeString(self)); }; -val _hashCodeString = { String self -> +val _hashCodeString = { !String self -> int hash=0; for( int i=0; i< self.cs#; i++ ) hash = hash*31 + self.cs[i]; diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testAndPtr.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testAndPtr.smp index 5d300ede..556a1375 100644 --- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testAndPtr.smp +++ b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testAndPtr.smp @@ -1,6 +1,6 @@ {i32 i64 u32 -> u32} write="C"; val _p={u8[~] str -> i64 ptr=str; return write(1,ptr,str#);}; -struct S { S? fld; new S={ S? f -> fld=f; }; }; +struct S { !S? !fld; new S={ !S? f -> fld=f; }; }; val ptr = arg == 1 ? null : new S(arg==1 ? null : new S(null)); if( ptr && ptr.fld ) { diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testFRefFields.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testFRefFields.smp index 8eb86522..00fcb589 100644 --- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testFRefFields.smp +++ b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testFRefFields.smp @@ -16,5 +16,5 @@ struct Scan { return true; }; }; -Scan !s = new Scan(" q"); +!Scan !s = new Scan(" q"); return s.peek('q'); diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testMethod.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testMethod.smp index 3ba33216..27a19988 100644 --- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testMethod.smp +++ b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testMethod.smp @@ -13,5 +13,5 @@ struct Scan { buf[x++]==ch; }; }; -Scan !s = new Scan(" [1,2]"); +!Scan !s = new Scan(" [1,2]"); return s.require('['); diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testOrRhsLoopLocal.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testOrRhsLoopLocal.smp index a4c8a9f5..cf6d93ab 100644 --- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testOrRhsLoopLocal.smp +++ b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testOrRhsLoopLocal.smp @@ -2,7 +2,7 @@ struct S { int f; }; int x=0; int result=arg&1; if(!result) { - S !s=new S; + !S !s=new S; while(x<3) x++; s.f=++x; result=!!s.f; diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testOrRhsLoopMemory.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testOrRhsLoopMemory.smp index 775b92b5..370bfb8d 100644 --- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testOrRhsLoopMemory.smp +++ b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testOrRhsLoopMemory.smp @@ -1,8 +1,8 @@ struct S { int f; }; -S !s=new S; +!S !s=new S; int result=arg&1; if(!result) { - S !t=new S; + !S !t=new S; while(s.f<3) s.f++; s.f++; t.f=s.f; diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24AllocTest-testBubbleSort.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24AllocTest-testBubbleSort.smp index a61ca332..7aeeaa35 100644 --- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24AllocTest-testBubbleSort.smp +++ b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24AllocTest-testBubbleSort.smp @@ -137,7 +137,7 @@ struct aryi64 { }; }; val parseAryI64 = { u8[~] str -> - Scan !s = new Scan(str); + !Scan !s = new Scan(str); if( !s.match('[') ) return null; // Array of parsed results var nums = new aryi64(new i64[1]); @@ -185,7 +185,7 @@ struct aryi64 { // // Bubble sort, with reading (parsing) and writing (formatting) the data. -val sort = { aryi64 nums -> +val sort = { !aryi64 nums -> var done = false; while( !done ) { done = true; |
