From 018379b0cf4e286ceacb1e39f34dd80f770e381e Mon Sep 17 00:00:00 2001 From: Cliff Click Date: Mon, 21 Apr 2025 14:18:23 -0700 Subject: Chapter 22: A Hello, World! MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: godalming123 <68993177+godalming123@users.noreply.github.com> Co-authored-by: Tamás Hadházy <85063808+Hels15@users.noreply.github.com> --- src/main/java/com/seaofnodes/simple/Parser.java | 235 +++++++++++++++------ src/main/java/com/seaofnodes/simple/Var.java | 12 +- .../com/seaofnodes/simple/codegen/CodeGen.java | 59 ++++-- .../com/seaofnodes/simple/codegen/ElfFile.java | 9 +- .../com/seaofnodes/simple/codegen/Encoding.java | 120 ++++++++--- .../simple/codegen/GlobalCodeMotion.java | 17 +- .../com/seaofnodes/simple/codegen/RegAlloc.java | 26 ++- .../java/com/seaofnodes/simple/node/AddNode.java | 47 +---- .../java/com/seaofnodes/simple/node/AndNode.java | 29 +-- .../java/com/seaofnodes/simple/node/ArithNode.java | 98 +++++++++ .../java/com/seaofnodes/simple/node/BoolNode.java | 21 +- .../java/com/seaofnodes/simple/node/CFGNode.java | 14 +- .../com/seaofnodes/simple/node/CallEndMach.java | 36 ++++ .../com/seaofnodes/simple/node/CallEndNode.java | 35 +-- .../java/com/seaofnodes/simple/node/CallNode.java | 10 +- .../java/com/seaofnodes/simple/node/CastMach.java | 19 ++ .../java/com/seaofnodes/simple/node/CastNode.java | 5 + .../java/com/seaofnodes/simple/node/DivNode.java | 28 +-- .../com/seaofnodes/simple/node/ExternNode.java | 23 ++ .../java/com/seaofnodes/simple/node/FunNode.java | 15 +- .../java/com/seaofnodes/simple/node/LoadNode.java | 6 + .../com/seaofnodes/simple/node/LogicalNode.java | 29 --- .../java/com/seaofnodes/simple/node/LoopNode.java | 2 +- .../com/seaofnodes/simple/node/MemMergeNode.java | 41 ++-- .../java/com/seaofnodes/simple/node/MemOpNode.java | 3 +- .../java/com/seaofnodes/simple/node/MinusNode.java | 2 +- .../java/com/seaofnodes/simple/node/MulNode.java | 34 +-- src/main/java/com/seaofnodes/simple/node/Node.java | 12 +- .../java/com/seaofnodes/simple/node/OrNode.java | 20 +- .../java/com/seaofnodes/simple/node/ParmNode.java | 4 + .../java/com/seaofnodes/simple/node/PhiNode.java | 9 +- .../com/seaofnodes/simple/node/ReadOnlyMach.java | 19 ++ .../com/seaofnodes/simple/node/ReadOnlyNode.java | 2 +- .../com/seaofnodes/simple/node/ReturnNode.java | 1 - .../java/com/seaofnodes/simple/node/SarNode.java | 26 +-- .../java/com/seaofnodes/simple/node/ScopeNode.java | 44 ++-- .../java/com/seaofnodes/simple/node/ShlNode.java | 27 ++- .../java/com/seaofnodes/simple/node/ShrNode.java | 22 +- .../java/com/seaofnodes/simple/node/StopNode.java | 20 +- .../java/com/seaofnodes/simple/node/StoreNode.java | 29 ++- .../java/com/seaofnodes/simple/node/SubNode.java | 36 +--- .../java/com/seaofnodes/simple/node/XorNode.java | 18 +- .../seaofnodes/simple/node/cpus/arm/CallARM.java | 9 +- .../simple/node/cpus/arm/CallEndARM.java | 8 - .../seaofnodes/simple/node/cpus/arm/CastARM.java | 18 -- .../seaofnodes/simple/node/cpus/arm/FloatARM.java | 2 - .../seaofnodes/simple/node/cpus/arm/TFPARM.java | 14 +- .../seaofnodes/simple/node/cpus/arm/TMPARM.java | 53 +++++ .../com/seaofnodes/simple/node/cpus/arm/arm.java | 90 ++++---- .../simple/node/cpus/riscv/CallEndRISC.java | 8 - .../simple/node/cpus/riscv/CallRISC.java | 20 +- .../simple/node/cpus/riscv/CastRISC.java | 18 -- .../seaofnodes/simple/node/cpus/riscv/FltRISC.java | 3 +- .../seaofnodes/simple/node/cpus/riscv/ImmRISC.java | 3 +- .../simple/node/cpus/riscv/Int8RISC.java | 47 +++++ .../com/seaofnodes/simple/node/cpus/riscv/LUI.java | 2 +- .../simple/node/cpus/riscv/MemOpRISC.java | 12 +- .../simple/node/cpus/riscv/SraIRISC.java | 4 +- .../seaofnodes/simple/node/cpus/riscv/TFPRISC.java | 11 +- .../seaofnodes/simple/node/cpus/riscv/TMPRISC.java | 41 ++++ .../simple/node/cpus/riscv/XorIRISC.java | 1 + .../seaofnodes/simple/node/cpus/riscv/riscv.java | 16 +- .../simple/node/cpus/x86_64_v2/AddIX86.java | 11 + .../simple/node/cpus/x86_64_v2/CallEndX86.java | 8 - .../simple/node/cpus/x86_64_v2/CallX86.java | 11 +- .../simple/node/cpus/x86_64_v2/CastX86.java | 16 -- .../simple/node/cpus/x86_64_v2/CmpMemX86.java | 2 +- .../simple/node/cpus/x86_64_v2/CmpX86.java | 6 + .../simple/node/cpus/x86_64_v2/DivX86.java | 2 +- .../simple/node/cpus/x86_64_v2/ImmX86.java | 3 +- .../simple/node/cpus/x86_64_v2/MemAddX86.java | 5 +- .../simple/node/cpus/x86_64_v2/MulIX86.java | 33 ++- .../simple/node/cpus/x86_64_v2/RegX86.java | 1 + .../simple/node/cpus/x86_64_v2/SetX86.java | 4 +- .../simple/node/cpus/x86_64_v2/TFPX86.java | 1 + .../simple/node/cpus/x86_64_v2/TMPX86.java | 35 +++ .../simple/node/cpus/x86_64_v2/x86_64_v2.java | 28 +-- .../com/seaofnodes/simple/print/ASMPrinter.java | 87 +++++--- src/main/java/com/seaofnodes/simple/sys.java | 25 +++ src/main/java/com/seaofnodes/simple/sys/sys.smp | 58 +++++ .../java/com/seaofnodes/simple/type/Field.java | 8 +- src/main/java/com/seaofnodes/simple/type/Type.java | 14 +- .../com/seaofnodes/simple/type/TypeConAry.java | 69 ++++++ .../com/seaofnodes/simple/type/TypeConAryB.java | 21 ++ .../com/seaofnodes/simple/type/TypeConAryI.java | 15 ++ .../java/com/seaofnodes/simple/type/TypeFloat.java | 2 +- .../com/seaofnodes/simple/type/TypeFunPtr.java | 2 +- .../com/seaofnodes/simple/type/TypeInteger.java | 10 +- .../java/com/seaofnodes/simple/type/TypeMem.java | 3 +- .../com/seaofnodes/simple/type/TypeMemPtr.java | 25 ++- .../java/com/seaofnodes/simple/type/TypeNil.java | 2 +- .../java/com/seaofnodes/simple/type/TypePtr.java | 2 +- .../com/seaofnodes/simple/type/TypeStruct.java | 145 ++++++++++--- .../java/com/seaofnodes/simple/type/TypeTuple.java | 15 +- 94 files changed, 1539 insertions(+), 774 deletions(-) create mode 100644 src/main/java/com/seaofnodes/simple/node/ArithNode.java create mode 100644 src/main/java/com/seaofnodes/simple/node/CallEndMach.java create mode 100644 src/main/java/com/seaofnodes/simple/node/CastMach.java create mode 100644 src/main/java/com/seaofnodes/simple/node/ExternNode.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/LogicalNode.java create mode 100644 src/main/java/com/seaofnodes/simple/node/ReadOnlyMach.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/CallEndARM.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/CastARM.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/TMPARM.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallEndRISC.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/CastRISC.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/Int8RISC.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/TMPRISC.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallEndX86.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CastX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TMPX86.java create mode 100644 src/main/java/com/seaofnodes/simple/sys.java create mode 100644 src/main/java/com/seaofnodes/simple/sys/sys.smp create mode 100644 src/main/java/com/seaofnodes/simple/type/TypeConAry.java create mode 100644 src/main/java/com/seaofnodes/simple/type/TypeConAryB.java create mode 100644 src/main/java/com/seaofnodes/simple/type/TypeConAryI.java (limited to 'src/main/java') diff --git a/src/main/java/com/seaofnodes/simple/Parser.java b/src/main/java/com/seaofnodes/simple/Parser.java index 219c3c44..23e9ddee 100644 --- a/src/main/java/com/seaofnodes/simple/Parser.java +++ b/src/main/java/com/seaofnodes/simple/Parser.java @@ -4,7 +4,7 @@ import com.seaofnodes.simple.codegen.CodeGen; import com.seaofnodes.simple.node.*; import com.seaofnodes.simple.print.GraphVisualizer; import com.seaofnodes.simple.type.*; -import java.text.ParseException; + import java.util.*; /** @@ -24,7 +24,7 @@ public class Parser { public final CodeGen _code; // The Lexer. Thin wrapper over a byte[] buffer with a cursor. - private final Lexer _lexer; + private Lexer _lexer; /** * Current ScopeNode - ScopeNodes change as we parse code, but at any point of time @@ -84,7 +84,7 @@ public class Parser { // Mapping from a type name to a Type. The string name matches // `type.str()` call. No TypeMemPtrs are in here, because Simple does not // have C-style '*ptr' references. - public static HashMap TYPES; + public static HashMap TYPES = defaultTypes(); // Mapping from a type name to the constructor for a Type. public final HashMap INITS; @@ -92,7 +92,6 @@ public class Parser { public Parser(CodeGen code, TypeInteger arg) { _code = code; - _lexer = new Lexer(code._src); _scope = new ScopeNode(); _continueScope = _breakScope = null; ZERO = con(TypeInteger.ZERO).keep(); @@ -132,9 +131,8 @@ public class Parser { private Node ctrl() { return _scope.ctrl(); } private N ctrl(N n) { return _scope.ctrl(n); } - public void parse() { parse(false); } - public void parse(boolean show) { - _xScopes.push(_scope); + public void parse() { + _scope.define(ScopeNode.CTRL, Type.CONTROL , false, null, _lexer); _scope.define(ScopeNode.MEM0, TypeMem.BOT , false, null, _lexer); _scope.define(ScopeNode.ARG0, TypeInteger.BOT, false, null, _lexer); @@ -142,6 +140,17 @@ public class Parser { ctrl(XCTRL); _scope.mem(new MemMergeNode(false)); + // Parse the sys import + _lexer = new Lexer(com.seaofnodes.simple.sys.SYS); + while( !_lexer.isEOF() ) { + parseStatement(); + _lexer.skipWhiteSpace(); + } + + // Reset lexer for program text + _lexer = new Lexer(_code._src); + _xScopes.push(_scope); + // Parse whole program, as-if function header "{ int arg -> body }" parseFunctionBody(_code._main,loc(),"arg"); @@ -150,9 +159,10 @@ public class Parser { // default main). FunNode main = _code.link(_code._main); StopNode stop = _code._stop; - if( main.ret().expr() instanceof ConstantNode && main.ret().mem().in(0)==main && stop.nIns() > 1 ) { + if( main.ret().expr()._type==Type.TOP && main.uctrl()==null ) { // Kill an empty default main; so it does not attempt to put a // "main" in any final ELF file + main.setDef(1,XCTRL); // Delete default start input stop.delDef(stop._inputs.find(main.ret())); } else { // We have a non-empty default main. @@ -173,7 +183,6 @@ public class Parser { init.unkeep().kill(); INITS.clear(); stop.peephole(); - if( show ) showGraph(); } /** @@ -187,7 +196,7 @@ public class Parser { ScopeNode breakScope = _breakScope; _breakScope = null; ScopeNode continueScope = _continueScope; _continueScope = null; - FunNode fun = _fun = (FunNode)peep(new FunNode(loc(),sig,null,_code._start)); + FunNode fun = _fun = (FunNode)peep(new FunNode(loc(),sig,_nestedType,null,_code._start)); // Once the function header is available, install in linker table - // allowing recursive functions. Linker matches on declared args and // exact fidx, and ignores the return (because the fidx will only match @@ -227,8 +236,8 @@ public class Parser { while (!peek('}') && !_lexer.isEOF()) last = parseStatement(); - // Last expression is the return - if( ctrl()._type==Type.CONTROL ) + // Last expression is the return except for the top-level main + if( ctrl()._type==Type.CONTROL && fun.sig() != _code._main ) fun.addReturn(ctrl(), _scope.mem().merge(), last); // Pop off the inProgress node on the multi-exit Region merge @@ -287,8 +296,7 @@ public class Parser { * @return a {@link Node} or {@code null} */ private Node parseStatement() { - if( false ) return null; - else if (matchx("return") ) return parseReturn(); + if( matchx("return") ) return parseReturn(); else if (matchx("if") ) return parseIf(); else if (matchx("while") ) return parseWhile(); else if (matchx("for") ) return parseFor(); @@ -403,7 +411,7 @@ public class Parser { // Our current scope is the body Scope ctrl(ifT.unkeep()); // set ctrl token to ifTrue projection _scope.addGuards(ifT,pred.unkeep(),false); // Up-cast predicate - parseStatement(); // Parse loop body + parseStatement().isKill(); // Parse loop body _scope.removeGuards(ifT); // Merge the loop bottom into other continue statements @@ -440,8 +448,11 @@ public class Parser { // At exit the false control is the current control, and // the scope is the exit scope after the exit test. - _xScopes.pop(); - _xScopes.push(exit); + // During sys parsing, there is no xscope here. + if( !_xScopes.isEmpty() ) { + _xScopes.pop(); + _xScopes.push( exit ); + } _scope = exit; return ZERO; } @@ -628,11 +639,11 @@ public class Parser { // outside the constructor need to check the final bit. if( _scope.in(def._idx)._type!=Type.TOP && def._final && // Inside a constructor, final assign is OK, outside nope - !(_scope.inCon() && def._idx >= _scope._lexSize.last()) ) + !(_scope.inConstructor() && def._idx >= _scope._lexSize.last()) ) throw error("Cannot reassign final '"+name+"'"); // Lift expression, based on type - Node lift = liftExpr(expr.keep(), def.type(), def._final); + Node lift = liftExpr(expr.keep(), def.type(), def._final, true); // Update _scope.update(name,lift); // Return un-lifted expr @@ -641,20 +652,29 @@ public class Parser { // Make finals deep; widen ints to floats; narrow wide int types. // Early error if types do not match variable. - private Node liftExpr( Node expr, Type t, boolean xfinal ) { - assert !(expr._type instanceof TypeMemPtr tmp) || !tmp.isFRef(); + private Node liftExpr( Node expr, Type t, boolean xfinal, boolean isLoad ) { + if( expr._type instanceof TypeMemPtr tmp && tmp.isFRef() ) + throw error("Must define forward ref "+tmp._obj._name); // Final is deep on ptrs if( xfinal && t instanceof TypeMemPtr tmp ) { t = tmp.makeRO(); expr = peep(new ReadOnlyNode(expr)); } + // Auto-widen array to i64 (cast ptr to raw int bits) + if( t == TypeInteger.BOT && expr._type instanceof TypeMemPtr tmp && tmp._obj.isAry() ) + expr = peep(new AddNode(peep(new CastNode(t,ctrl(),expr)),con(tmp._obj.aryBase()))); // Auto-widen int to float expr = widenInt( expr, t ); - // Auto-narrow wide ints to narrow ints - expr = zsMask(expr,t); + // Auto-narrow wide ints to narrow ints. For loads, emit code to force + // the loaded value to match the declared sign/zero bits. For stores, + // just force the type, acting "as if" the store silently truncates. + Type et = expr._type; + if( isLoad ) { expr = zsMask(expr,t); et = expr._type; } + else if( et instanceof TypeInteger && t instanceof TypeInteger ) et=t; + // Type is sane - if( expr._type!=Type.BOTTOM && !expr._type.shallowISA(t) ) - throw error("Type " + expr._type.str() + " is not of declared type " + t.str()); + if( et!=Type.BOTTOM && !et.shallowISA(t) ) + throw error("Type " + et.str() + " is not of declared type " + t.str()); return expr; } @@ -670,7 +690,10 @@ public class Parser { * exprAsgn = var '=' exprAsgn | expr */ private Node parseDeclarationStatement() { + int old = pos(); Type t = type(); + if( peek('.') ) // Ambiguity static vars: "type.var", parse as expression + { pos(old); t=null; } if( t == null ) return require(parseAsgn(),";"); @@ -694,7 +717,9 @@ public class Parser { boolean xfinal = false; Node expr; if( match("=") ) { - expr = parseAsgn(); + expr = isExternDecl() + ? externDecl(name,t) + : parseAsgn(); // TOP means val and val is always final xfinal = (t==Type.TOP) || // BOTTOM is var and var is always not-final @@ -710,16 +735,19 @@ public class Parser { if( expr._type==Type.NIL ) throw error("a not-null/non-zero expression"); t = expr._type; - if( !xfinal ) t = t.glb(); // Widen if not final + if( !xfinal ) t = t.glb(false); // Widen if not final + } + // expr is a constant function + if( t instanceof TypeFunPtr && expr._type instanceof TypeFunPtr tfp && tfp.isConstant() ) { + if( expr instanceof ExternNode ) t = expr._type; // Upgrade declared type to exact function + else _code.link(tfp).setName(name); // Assign debug name to Simple function } - if( t instanceof TypeFunPtr && expr._type instanceof TypeFunPtr tfp && tfp.isConstant() ) - _code.link(tfp)._name = name; } else { // Need an expression to infer the type. // Also, if not-null then need an initializing expression. // Allowed in a class def, because type/init will happen in the constructor. - if( (inferType || (t instanceof TypeNil tn && !tn.nullable() )) && !_scope.inCon() ) + if( (inferType || (t instanceof TypeNil tn && !tn.nullable() )) && !_scope.inConstructor() ) throw errorSyntax("=expression"); // Initial value for uninitialized struct fields. expr = switch( t ) { @@ -735,7 +763,7 @@ public class Parser { } // Lift expression, based on type - Node lift = liftExpr(expr, t, xfinal); + Node lift = liftExpr(expr, t, xfinal, true); if( xfinal && t instanceof TypeMemPtr tmp ) t = tmp.makeRO(); @@ -754,23 +782,23 @@ public class Parser { /** * Parse a struct declaration, and return the following statement. - * Only allowed in top level scope. - * Structs cannot be redefined. + * Structs cannot be redefined, but can be nested. * - * @return The statement following the struct + * @return zero */ + private String _nestedType; private Node parseStruct() { - if (_xScopes.size() > 1) throw error("struct declarations can only appear in top level scope"); + // "struct" already parsed, so expect the struct name next. + String old = _nestedType; String typeName = requireId(); - Type t = TYPES.get(typeName); - if( t!=null && !(t instanceof TypeMemPtr tmp && tmp.isFRef() ) ) - throw error("struct '" + typeName + "' cannot be redefined"); + if( old!=null ) typeName = old+"."+typeName; + _nestedType = typeName; // A Block scope parse, and inspect the scope afterward for fields. _scope.push(ScopeNode.Kind.Constructor); require("{"); while (!peek('}') && !_lexer.isEOF()) - parseStatement(); + parseStatement().isKill(); // Grab the declarations and build fields and a Struct int lexlen = _scope._lexSize.last(); @@ -778,15 +806,19 @@ public class Parser { StructNode s = new StructNode(); Ary fs = new Ary<>(Field.class); for( int i=lexlen; i")) ) - return posT(old1); - pos(old2); // Reset lexer to reparse - if( id==null ) + if( !(peek(',') || peek(';') || match("->")) || id==null ) return posT(old1); // Reset lexer to reparse + pos(old2); // Reset lexer to reparse // Yes a forward ref, so declare it TYPES.put(tname,t1); + // Return the (array, final) type return t2; } @@ -854,8 +902,7 @@ public class Parser { String tname = "["+t.str()+"]"; Type ta = TYPES.get(tname); if( ta != null ) return (TypeMemPtr)ta; - // Need make an array type. - TypeStruct ts = TypeStruct.makeAry(TypeInteger.U32,_code.getALIAS(),t,_code.getALIAS()); + TypeStruct ts = TypeStruct.makeAry(tname,TypeInteger.U32,_code.getALIAS(),t,_code.getALIAS()); assert ts.str().equals(tname); TypeMemPtr tary = TypeMemPtr.make(ts); TYPES.put(tname,tary); @@ -1069,12 +1116,28 @@ public class Parser { */ private Node parsePrimary() { if( _lexer.isNumber(_lexer.peek()) ) return parseLiteral(); + if( _lexer.peek('"') ) return newString(parseString()); if( matchx("true" ) ) return con(1); if( matchx("false") ) return ZERO; if( matchx("null" ) ) return NIL; + if( match ("'" ) ) return parseChar(); if( match ("(" ) ) return require(parseAsgn(), ")"); if( matchx("new" ) ) return alloc(); if( match ("{" ) ) return require(func(),"}"); + + // Parse static variable lookup: `type.fld` in the type namespace + Type t = type(); + if( t!=null && match(".") ) { + String fld = requireId(); + TypeMemPtr tmp = ((TypeMemPtr)t); + String tname = tmp._obj._name; + StructNode init = INITS.get(tname); + int idx = tmp._obj.find(fld); + if( idx == -1 ) + throw error("Accessing unknown STATIC field"); + return init.in(idx); + } + // Expect an identifier now Var n = requireLookupId(); Node rvalue = _scope.in(n); @@ -1117,12 +1180,13 @@ public class Parser { }; // Convert to float ops, or narrow int types; error if not declared type. // Also, if postfix LHS is still keep() - return liftExpr(peep(op.widen()),t,false); + return liftExpr(peep(op.widen()),t,false,true); } // Expect an ID here. If not found, assume a forward reference function Var requireLookupId() { + // Try in the normal variable namespace String id = _lexer.matchId(); if( id == null || KEYWORDS.contains(id) ) throw errorSyntax("an identifier or expression"); @@ -1239,6 +1303,18 @@ public class Parser { ALTMP.clear(); ALTMP.add(len.unkeep()); ALTMP.add(con(ary._fields[1]._type.makeZero())); return newStruct(ary,size,0,ALTMP); } + private Node newString(String s) { + TypeMemPtr tmp = typeAry(TypeInteger.U8); + int lenAlias = tmp._obj.field("#" )._alias; + int elemAlias = tmp._obj.field("[]")._alias; + TypeConAryB con = TypeConAryB.make(s); + Type elem = con.elem(); + // Make a TMP, not-null (byte)2, singleton (true) (requires text + // strings are hash-interned), with a TypeStruct having a constant + // array body. + TypeMemPtr str = TypeMemPtr.make((byte)2,TypeStruct.makeAry("[u8]", TypeInteger.U32, lenAlias, elem, elemAlias, true, con),true); + return con(str); + } // We set up memory aliases by inserting special vars in the scope these // variables are prefixed by $ so they cannot be referenced in Simple code. @@ -1272,14 +1348,12 @@ public class Parser { throw error("Accessing unknown field '" + name + "' from 'null'"); // Sanity check expr for being a reference - if( !(expr._type instanceof TypeMemPtr ptr) ) { + if( !(expr._type instanceof TypeMemPtr ptr) ) throw error( "Expected "+(name=="#" || name=="[]" ? "array" : "reference")+" but found " + expr._type.str() ); - } - // Sanity check field name for existing - TypeMemPtr tmp = (TypeMemPtr)TYPES.get(ptr._obj._name); - if( tmp == null ) throw error("Accessing unknown field '" + name + "' from '" + ptr + "'"); - TypeStruct base = tmp._obj; + // Find the field from the Type. Lookup in the base object field names. + TypeStruct base = ptr._obj; + assert !base.isFRef(); int fidx = base.find(name); if( fidx == -1 ) throw error("Accessing unknown field '" + name + "' from '" + ptr.str() + "'"); @@ -1299,7 +1373,7 @@ public class Parser { // Disambiguate "obj.fld==x" boolean test from "obj.fld=x" field assignment if( matchOpx('=','=') ) { Node val = parseAsgn().keep(); - Node lift = liftExpr( val, tf, f._final ); + Node lift = liftExpr( val, tf, f._final, false ); Node st = new StoreNode(loc(), name, f._alias, tf, memAlias(f._alias), expr, off.unkeep(), lift, false); // Arrays include control, as a proxy for a safety range check. @@ -1393,10 +1467,10 @@ public class Parser { if( expr._type == Type.NIL ) throw error("Calling a null function pointer"); if( !(expr instanceof FRefNode) && !expr._type.isa(TypeFunPtr.BOT) ) - throw error("Expected a function but got "+expr._type.glb().str()); + throw error("Expected a function but got "+expr._type.glb(false).str()); expr.keep(); // Keep while parsing args - Ary args = new Ary(Node.class); + Ary args = new Ary<>( Node.class ); args.push(null); // Space for ctrl,mem args.push(null); while( !peek(')') ) { @@ -1437,6 +1511,21 @@ public class Parser { return new ProjNode(cend,2,"#2").peephole(); } + // Just after parsing "type foo = " can parse `"C"` + private boolean isExternDecl( ) { + int old = pos(); + String s = parseString(); + if( "C".equals(s) ) return true; + pos(old); + return false; + } + // External linked constant + private Node externDecl( String ex, Type t ) { + if( t instanceof TypeFunPtr tfp ) // Generic TFP from type parse + t = _code.makeFun(tfp._sig,tfp._ret); // Get a FIDX, becomes a constant + return new ExternNode(t,ex).peephole(); + } + /** * Parse integer literal * @@ -1453,6 +1542,22 @@ public class Parser { return _scope.upcastGuard(n.peephole()); } + // Parse a string or null + private String parseString() { + if( !peek('"') ) return null; + _lexer.inc(); + int start = pos(); + while( !_lexer.isEOF() && _lexer.nextChar()!= '"' ) ; + if( _lexer.isEOF() ) + throw error("Unclosed string"); + return new String(_lexer._input,start,pos()-start-1); + } + + // Already parsed "'" + private Node parseChar() { + return require(con(TypeInteger.constant(_lexer.nextChar())),"'"); + } + ////////////////////////////////// // Utilities for lexical analysis @@ -1596,7 +1701,15 @@ public class Parser { _input[_position ] == '/' && _input[_position+1] == '/') { inc(); inc(); - while( !isEOF() && _input[_position] != '\n' ) inc(); + while( !isEOF() && _input[_position] != '\n' ) + inc(); + } else if( _position+2 < _input.length && + _input[_position ] == '/' && + _input[_position+1] == '*') { + // Skip /*comment*/ + while( !isEOF() && !(_input[_position-1] == '*' && _input[_position] == '/')) + inc(); + inc(); } else break; } } diff --git a/src/main/java/com/seaofnodes/simple/Var.java b/src/main/java/com/seaofnodes/simple/Var.java index e71d7ef2..cf549cd6 100644 --- a/src/main/java/com/seaofnodes/simple/Var.java +++ b/src/main/java/com/seaofnodes/simple/Var.java @@ -9,11 +9,13 @@ import com.seaofnodes.simple.type.*; public class Var { public final String _name; // Declared name + // These fields are not final for forward reference late updates or + // promotions. public int _idx; // index in containing scope private Type _type; // Declared type public boolean _final; // Final field - public Parser.Lexer _loc; // Source location public boolean _fref; // Forward ref + public Parser.Lexer _loc; // Source location public Var(int idx, String name, Type type, boolean xfinal, Parser.Lexer loc ) { this(idx,name,type,xfinal,loc,false); @@ -23,8 +25,8 @@ public class Var { _name = name; _type = type; _final = xfinal; - _loc = loc; _fref = fref; + _loc = loc; } public Type type() { if( !_type.isFRef() ) return _type; @@ -32,10 +34,6 @@ public class Var { Type def = Parser.TYPES.get(((TypeMemPtr)_type)._obj._name); return (_type=_type.meet(def)); } - public Type lazyGLB() { - Type t = type(); - return t instanceof TypeMemPtr ? t : t.glb(); - } // Forward reference variables (not types) must be BOTTOM and // distinct from inferred variables @@ -45,8 +43,8 @@ public class Var { assert isFRef() && xfinal; _type = type; _final = true; - _loc = loc; _fref = false; + _loc = loc; } @Override public String toString() { diff --git a/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java b/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java index 9ec3bc7b..adb71413 100644 --- a/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java +++ b/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java @@ -21,7 +21,7 @@ public class CodeGen { Opto, // Run ideal optimizations TypeCheck, // Last check for bad programs LoopTree, // Build a loop tree; break infinite loops - InstSelect, // Convert to target hardware nodes + Select, // Convert to target hardware nodes Schedule, // Global schedule (code motion) nodes LocalSched, // Local schedule RegAlloc, // Register allocation @@ -35,6 +35,7 @@ public class CodeGen { public final String _src; // Compile-time known initial argument type public final TypeInteger _arg; + // --------------------------- public CodeGen( String src ) { this(src, TypeInteger.BOT, 123L ); } public CodeGen( String src, TypeInteger arg, long workListSeed ) { @@ -54,14 +55,15 @@ public class CodeGen { public CodeGen driver( Phase phase ) { return driver(phase,null,null); } public CodeGen driver( Phase phase, String cpu, String callingConv ) { if( _phase==null ) parse(); - if( _phase.ordinal() < phase.ordinal() ) opto(); - if( _phase.ordinal() < phase.ordinal() ) typeCheck(); - if( _phase.ordinal() < phase.ordinal() ) loopTree(); - if( _phase.ordinal() < phase.ordinal() && cpu != null ) instSelect(cpu,callingConv); - if( _phase.ordinal() < phase.ordinal() ) GCM(); - if( _phase.ordinal() < phase.ordinal() ) localSched(); - if( _phase.ordinal() < phase.ordinal() ) regAlloc(); - if( _phase.ordinal() < phase.ordinal() ) encode(); + int p1 = phase.ordinal(); + if( _phase.ordinal() < p1 && _phase.ordinal() < Phase.Opto .ordinal() ) opto(); + if( _phase.ordinal() < p1 && _phase.ordinal() < Phase.TypeCheck .ordinal() ) typeCheck(); + if( _phase.ordinal() < p1 && _phase.ordinal() < Phase.LoopTree .ordinal() ) loopTree(); + if( _phase.ordinal() < p1 && _phase.ordinal() < Phase.Select .ordinal() && cpu != null ) instSelect(cpu,callingConv); + if( _phase.ordinal() < p1 && _phase.ordinal() < Phase.Schedule .ordinal() ) GCM(); + if( _phase.ordinal() < p1 && _phase.ordinal() < Phase.LocalSched.ordinal() ) localSched(); + if( _phase.ordinal() < p1 && _phase.ordinal() < Phase.RegAlloc .ordinal() ) regAlloc(); + if( _phase.ordinal() < p1 && _phase.ordinal() < Phase.Encoding .ordinal() ) encode(); return this; } @@ -128,7 +130,7 @@ public class CodeGen { return TypeFunPtr.make((byte)2,sig,ret, 1L<(); _instSelect( _stop, map ); _stop = ( StopNode)map.get(_stop ); - _start = (StartNode)map.get(_start); + StartNode start = (StartNode)map.get(_start); + _start = start==null ? new StartNode(_start) : start; _instOuts(_stop,visit()); _visit.clear(); _tInsSel = (int)(System.currentTimeMillis() - t0); @@ -317,8 +338,6 @@ public class CodeGen { if( x instanceof MachNode mach ) mach.postSelect(this); // Post selection action - // Updates forward edges only. - n._outputs.clear(); return x; } @@ -342,7 +361,7 @@ public class CodeGen { // Global schedule (code motion) nodes public CodeGen GCM() { return GCM(false); } public CodeGen GCM( boolean show) { - assert _phase.ordinal() <= Phase.InstSelect.ordinal(); + assert _phase.ordinal() <= Phase.Select.ordinal(); _phase = Phase.Schedule; long t0 = System.currentTimeMillis(); @@ -442,8 +461,10 @@ public class CodeGen { String printCFG() { if( _cfg==null ) return "no CFG"; SB sb = new SB(); - for( CFGNode cfg : _cfg ) - IRPrinter.printLine(cfg,sb); + for( CFGNode cfg : _cfg ) { + sb.fix(8,""+cfg._idepth); + IRPrinter.printLine( cfg, sb ); + } return sb.toString(); } diff --git a/src/main/java/com/seaofnodes/simple/codegen/ElfFile.java b/src/main/java/com/seaofnodes/simple/codegen/ElfFile.java index 746ecfed..0eaaae7b 100644 --- a/src/main/java/com/seaofnodes/simple/codegen/ElfFile.java +++ b/src/main/java/com/seaofnodes/simple/codegen/ElfFile.java @@ -273,8 +273,6 @@ public class ElfFile { int start_global = num+1; // Add one to skip the final .rela.text local symbol for( Symbol a: symbols._symbols ) a._index = start_global++; - int bigConIdx = start_global; - start_global += enc._bigCons.size(); // create .text relocations DataSection text_rela = new DataSection(".rela.text", 4 /* SHT_RELA */); @@ -298,10 +296,10 @@ public class ElfFile { // Write relocations for the constant pool for( Encoding.Relo relo : enc._bigCons.values() ) { - Symbol glob = new Symbol("GLOB$"+bigConIdx, rdata._index, SYM_BIND_GLOBAL, SYM_TYPE_FUNC); + Symbol glob = new Symbol("CPOOL$"+start_global, rdata._index, SYM_BIND_GLOBAL, SYM_TYPE_FUNC); glob._value = relo._target; - glob._size = 1 << relo._t.log_size(); - glob._index = bigConIdx++; + glob._size = relo._t.size(); + glob._index = start_global++; symbols.push(glob); write8(text_rela._contents, relo._opStart+relo._off); write8(text_rela._contents, ((long)glob._index << 32L) | relo._elf ); @@ -313,6 +311,7 @@ public class ElfFile { text_rela._info = text._index; pushSection(text_rela); + // Final .rela.text symbol Symbol sym = new Symbol(text_rela._name, num++, SYM_BIND_LOCAL, SYM_TYPE_SECTION); sym._name_pos = text_rela._name_pos; sym._size = text_rela.size(); diff --git a/src/main/java/com/seaofnodes/simple/codegen/Encoding.java b/src/main/java/com/seaofnodes/simple/codegen/Encoding.java index c35ea6a4..db799b98 100644 --- a/src/main/java/com/seaofnodes/simple/codegen/Encoding.java +++ b/src/main/java/com/seaofnodes/simple/codegen/Encoding.java @@ -51,7 +51,7 @@ public class Encoding { public final Type _t; // Constant type public final byte _off; // Offset from start of opcode public final byte _elf; // ELF relocation type, e.g. 2/PC32 - public int _target; // Where constant is finally placede + public int _target; // Where constant is finally placed public int _opStart; // Opcode start Relo( Node op, Type t, byte off, byte elf ) { _op=op; _t=t; _off=off; _elf=elf; @@ -140,8 +140,8 @@ public class Encoding { final HashMap _externals = new HashMap<>(); - public Encoding external( Node call, String extern ) { - _externals.put(call,extern); + public Encoding external( Node n, String extern ) { + _externals.put(n,extern); return this; } @@ -185,12 +185,10 @@ public class Encoding { Ary rpo = new Ary<>(CFGNode.class); rpos.put(_code._start.loop(),rpo); BitSet visit = _code.visit(); - //IdentityHashMap looptail = new IdentityHashMap<>(); rpo.add(_code._stop); for( Node n : _code._start._outputs ) if( n instanceof FunNode fun ) { int x = rpo._len; - //_rpo_cfg2(fun, visit, rpo, looptail); _rpo_cfg(fun, visit, rpos ); assert rpo.at(x) instanceof ReturnNode; } @@ -301,7 +299,7 @@ public class Encoding { private static boolean forwardsEmptyScan( CFGNode c, int bld ) { if( c.nOuts()!=1 || c.loopDepth()!=bld ) return false; return c.uctrl() instanceof RegionNode cfg && - (cfg instanceof LoopNode || forwardsEmptyScan(cfg,bld)); + ((cfg instanceof LoopNode && cfg._ltree==c._ltree) || forwardsEmptyScan(cfg,bld)); } // Is the CFG from "next" to the end empty? This means jumping to "next" @@ -361,11 +359,6 @@ public class Encoding { slide = 0; for( int i=0; i 0 ) { // If no short-form ops, nothing to do here @@ -424,31 +434,71 @@ public class Encoding { void writeConstantPool( BAOS bits, boolean patch ) { padN(16,bits); + // radix sort the big constants by alignment + Ary[] raligns = new Ary[5]; + for( Node op : _bigCons.keySet() ) { + Relo relo = _bigCons.get(op); + int align = relo._t.alignment(); + Ary relos = raligns[align]==null ? (raligns[align]=new Ary<>(Relo.class)) : raligns[align]; + relos.add(relo); + } + + + // Types can be used more than once; collapse the dups HashMap targets = new HashMap<>(); - // By log size - for( int log = 3; log >= 0; log-- ) { - // Write the 8-byte constants - for( Node op : _bigCons.keySet() ) { - Relo relo = _bigCons.get(op); - if( relo._t.log_size()==log ) { - // Map from relo to constant start and patch - Integer target = targets.get(relo._t); - if( target==null ) { - targets.put(relo._t,target = bits.size()); - // Put constant into code space. - if( relo._t instanceof TypeTuple tt ) // Constant tuples put all entries - for( Type tx : tt._types ) - addN(log,tx,bits); - else - addN(log,relo._t,bits); + // By alignment + for( int align = 4; align >= 0; align-- ) { + Ary relos = raligns[align]; + if( relos == null ) continue; + for( Relo relo : relos ) { + // Map from relo to constant start and patch + Integer target = targets.get(relo._t); + if( target==null ) { + targets.put(relo._t,target = bits.size()); + // Write constant into constant pool + switch( relo._t ) { + case TypeTuple tt -> cpool(align,bits,tt); + case TypeStruct ts -> cpool(bits,ts); + // Simple primitive (e.g. larger int, float) + default -> addN(align,relo._t,bits); } - relo._target = target; - relo._opStart= _opStart[op._nid]; - // Go ahead and locally patch in-memory - if( patch ) - ((RIPRelSize)op).patch(this, relo._opStart, _opLen[op._nid], relo._target - relo._opStart); } + // Record target address and opcode start + relo._target = target; + relo._opStart= _opStart[relo._op._nid]; + // Go ahead and locally patch in-memory + if( patch ) + ((RIPRelSize)relo._op).patch(this, relo._opStart, _opLen[relo._op._nid], relo._target - relo._opStart); + } + } + } + + // Constant tuples put all entries at same alignment + private void cpool(int align, BAOS bits, TypeTuple tt) { + for( Type tx : tt._types ) + addN(align,tx,bits); + } + + // Structs use internal field layout + private void cpool( BAOS bits, TypeStruct ts ) { + // Field order by offset + int[] layout = ts.layout(); + int off=0; // offset in the struct + for( int fn=0; fn SENTINAL_CALLOC; + case "calloc" -> SENTINEL_CALLOC; + case "write" -> SENTINEL_WRITE ; default -> throw Utils.TODO(); }; ((RIPRelSize)src).patch(this, start, _opLen[src._nid], target - start); diff --git a/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java b/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java index 40f827ec..04a119c2 100644 --- a/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java +++ b/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java @@ -32,7 +32,6 @@ public abstract class GlobalCodeMotion { // Post-Order of CFG private static void _rpo_cfg(CFGNode def, Node use, BitSet visit, Ary rpo) { - if( use instanceof CallNode call ) call.unlink_all(); if( !(use instanceof CFGNode cfg) || visit.get(cfg._nid) ) return; // Been there, done that if( def instanceof ReturnNode && use instanceof CallEndNode ) @@ -178,14 +177,22 @@ public abstract class GlobalCodeMotion { } // Walk all inputs and put on worklist, as their last-use might now be done - for( Node def : n._inputs ) - if( def!=null && late[def._nid]==null ) { + for( Node def : n._inputs ) { + if( def!=null && late[def._nid]==null ) work.push(def); - // if the def has a load use, maybe the load can fire + // if the def has a load use, maybe the load can fire + if( def!=null ) for( Node out : def._outputs ) if( out instanceof MemOpNode ld && ld._isLoad && late[ld._nid]==null ) work.push(ld); + } + + if( n instanceof LoopNode loop ) { + for( Node phi : loop._outputs ) { + if( phi instanceof PhiNode && late[phi._nid]==null ) + work.push(phi); } + } } } @@ -274,7 +281,7 @@ public abstract class GlobalCodeMotion { for( Node mem : load.mem()._outputs ) { switch( mem ) { case MemOpNode st: - if( !st._isLoad ) { + if( !st._isLoad && load._alias == st._alias ) { assert late[mem._nid] != null; lca = anti_dep( load, late[mem._nid], mem.cfg0(), lca, st ); } diff --git a/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java b/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java index bd4cbf35..57d0d90c 100644 --- a/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java +++ b/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java @@ -163,7 +163,7 @@ public class RegAlloc { } // Cache reg masks for New and Call for( CFGNode bb : _code._cfg ) { - if( bb instanceof CallEndNode cend ) cend.cacheRegs(_code); + if( bb instanceof CallEndMach cend ) cend.cacheRegs(_code); for( Node n : bb._outputs ) if( n instanceof NewNode nnn ) nnn.cacheRegs(_code); } @@ -292,20 +292,21 @@ public class RegAlloc { Ary ns = new Ary<>(Node.class); for( RegMask rmask : rclass ) { ns.addAll(def._outputs); - Node split = makeSplit(def,"popular",round,lrg); + Node split = makeSplit(def,"popular",round,lrg,rmask); split.insertAfter( def ); if( split.nIns()>1 ) split.setDef(1,def); // all uses by class to split - for( Node use : ns ) { + for( int j=0; j < def._outputs._len; j++ ) { + Node use = def._outputs.at(j); if( use instanceof MachNode mach && use!=split ) { // Check all use inputs for n, in case there's several for( int i = 1; i < use.nIns(); i++ ) // Find a def input, and check register class - if( use.in( i ) == def ) { + if( use.in( i ) == def && mach.regmap( i ).overlap( rmask ) ) { RegMask m = mach.regmap( i ); if( m!=null && mach.regmap( i ).overlap( rmask ) ) // Modify use to use the split version specialized to this rclass - use.setDefOrdered( i, split ); + { use.setDefOrdered( i, split ); j--; break; } } } } @@ -501,15 +502,17 @@ public class RegAlloc { Node def = n.in(i); // Effective block for use CFGNode cfg = n instanceof PhiNode phi ? phi.region().cfg(i) : n.cfg0(); + // Use-side RegMask, if available + RegMask umask = n instanceof MachNode mach ? mach.regmap(i) : null; // Def is a split ? if( skip && def instanceof SplitNode ) { - boolean singleReg = n instanceof MachNode mach && mach.regmap(i).size1(); + boolean singleReg = umask!=null && umask.size1(); // Same block, multiple registers, split is only used by n, // assume this is good enough and do not split again. if( cfg==def.cfg0() && def.nOuts()==1 && !singleReg ) return; } - makeSplit(def,kind,round,lrg).insertBefore(n, i); + makeSplit(def,kind,round,lrg,umask).insertBefore(n, i); // Skip split-of-split same block if( skip && def instanceof SplitNode && cfg==def.cfg0() ) n.in(i).setDefOrdered(1,def.in(1)); @@ -536,8 +539,9 @@ public class RegAlloc { } } - private Node makeSplit( Node def, String kind, byte round, LRG lrg ) { - Node split = def instanceof MachNode mach && mach.isClone() + private Node makeSplit( Node def, String kind, byte round, LRG lrg, RegMask umask ) { + // Clone simple constants if possible + Node split = def instanceof MachNode mach && mach.isClone() && (umask==null || mach.outregmap().overlap(umask)) ? mach.copy() : _code._mach.split(kind,round,lrg); _lrgs.put(split,lrg); @@ -555,13 +559,13 @@ public class RegAlloc { private void postColor() { int maxReg = -1; for( CFGNode bb : _code._cfg ) { // For all ops - if( bb instanceof FunNode fun ) + if( bb instanceof FunNode ) maxReg = -1; // Reset for new function // Compute frame size, based on arguments and largest reg seen if( bb instanceof ReturnNode ret ) ret.fun().computeFrameAdjust(_code,maxReg); // Raise frame size by max stack args passed, even if ignored - if( bb instanceof CallEndNode cend ) + if( bb instanceof CallEndMach cend ) maxReg = Math.max(maxReg,cend._xslot); for( int j=0; j= 2 && !lhs.inProgress() && + lhs.region()==rhs.region() ) { + // Profit check: only 1 instance of `this` will remain, all the + // others will fold to constants. + int cnt=0; + for( int i=1; i=nIns() || in(i)==null ) + setDefX(i,mem.in(i)); + } + } + setDef(1,mem.in(1)); + return this; + } + + return progress ? this : null; } public Node in( Var v ) { return in(v._idx); } public Node alias( int alias ) { - return in(alias _outputs; + public Ary _outputs; /** @@ -66,7 +66,7 @@ public abstract class Node implements Cloneable { // Make a Node using the existing arrays of nodes. // Used by any pass rewriting all Node classes but not the edges. Node( Node n ) { - assert CodeGen.CODE._phase.ordinal() >= CodeGen.Phase.InstSelect.ordinal(); + assert CodeGen.CODE._phase.ordinal() >= CodeGen.Phase.Select.ordinal(); _nid = CODE.getUID(); // allocate unique dense ID _inputs = new Ary<>(n==null ? new Node[0] : n._inputs.asAry()); _outputs = new Ary<>(Node.class); @@ -239,7 +239,7 @@ public abstract class Node implements Cloneable { // Shortcut for "popping" until n nodes. A "pop" is basically a // setDef(last,null) followed by lowering the nIns() count. - void popUntil(int n) { + public void popUntil(int n) { unlock(); while( nIns() > n ) { Node old_def = _inputs.pop(); @@ -255,9 +255,9 @@ public abstract class Node implements Cloneable { * code elimination. */ public void kill( ) { + assert isUnused(); // Has no uses, so it is dead unlock(); moveDepsToWorklist(); - assert isUnused(); // Has no uses, so it is dead _type=null; // Flag as dead while( nIns()>0 ) { // Set all inputs to null, recursively killing unused Nodes Node old_def = _inputs.removeLast(); @@ -295,6 +295,10 @@ public abstract class Node implements Cloneable { if( unkeep().isUnused() ) kill(); } + public void isKill() { + if( isUnused() ) + kill(); + } // Replace self with nnn in the graph, making 'this' go dead diff --git a/src/main/java/com/seaofnodes/simple/node/OrNode.java b/src/main/java/com/seaofnodes/simple/node/OrNode.java index 9b03dc5d..ac0d8454 100644 --- a/src/main/java/com/seaofnodes/simple/node/OrNode.java +++ b/src/main/java/com/seaofnodes/simple/node/OrNode.java @@ -4,26 +4,16 @@ import com.seaofnodes.simple.Parser; import com.seaofnodes.simple.type.Type; import com.seaofnodes.simple.type.TypeInteger; -import java.util.BitSet; - -public class OrNode extends LogicalNode { +public class OrNode extends ArithNode { public OrNode(Parser.Lexer loc, Node lhs, Node rhs) { super(loc, lhs, rhs); } @Override public String label() { return "Or"; } @Override public String op() { return "|"; } - @Override public String glabel() { return "|"; } - @Override - public Type compute() { - Type t1 = in(1)._type, t2 = in(2)._type; - if( t1.isHigh() || t2.isHigh() ) - return TypeInteger.TOP; - if( t1 instanceof TypeInteger i0 && - t2 instanceof TypeInteger i1 ) { - if( i0.isConstant() && i1.isConstant() ) - return TypeInteger.constant(i0.value()|i1.value()); - } + + @Override long doOp( long x, long y ) { return x | y; } + @Override TypeInteger doOp(TypeInteger x, TypeInteger y) { return TypeInteger.BOT; } @@ -49,7 +39,7 @@ public class OrNode extends LogicalNode { Node phicon = AddNode.phiCon(this,true); if( phicon!=null ) return phicon; - return null; + return super.idealize(); } @Override Node copy(Node lhs, Node rhs) { return new OrNode(_loc,lhs,rhs); } } diff --git a/src/main/java/com/seaofnodes/simple/node/ParmNode.java b/src/main/java/com/seaofnodes/simple/node/ParmNode.java index 9e11a129..32e41f54 100644 --- a/src/main/java/com/seaofnodes/simple/node/ParmNode.java +++ b/src/main/java/com/seaofnodes/simple/node/ParmNode.java @@ -45,4 +45,8 @@ public class ParmNode extends PhiNode { // Always in-progress until we run out of unknown callers @Override public boolean inProgress() { return fun().inProgress(); } + + @Override public boolean eq( Node n ) { + return ((ParmNode)n)._idx==_idx && super.eq(n); + } } diff --git a/src/main/java/com/seaofnodes/simple/node/PhiNode.java b/src/main/java/com/seaofnodes/simple/node/PhiNode.java index 0579ecd0..971b52ae 100644 --- a/src/main/java/com/seaofnodes/simple/node/PhiNode.java +++ b/src/main/java/com/seaofnodes/simple/node/PhiNode.java @@ -54,7 +54,7 @@ public class PhiNode extends Node { // During parsing Phis have to be computed type pessimistically. if( r.inProgress() ) return _declaredType; // Set type to local top of the starting type - Type t = _declaredType.glb().dual();//Type.TOP; + Type t = _declaredType.glb(false).dual();//Type.TOP; for (int i = 1; i < nIns(); i++) // If the region's control input is live, add this as a dependency // to the control because we can be peeped should it become dead. @@ -121,12 +121,17 @@ public class PhiNode extends Node { Node op = in(i); if( in(1).getClass() != op.getClass() || op.in(0)!=null || in(1).nIns() != op.nIns() ) return false; // Wrong class or CFG bound or mismatched inputs + if( in(1) instanceof MemOpNode mem && mem._alias != ((MemOpNode)op)._alias ) + return false; if( op.nOuts() > 1 ) { // Too many users, but addDep in case lose users for( Node out : op._outputs ) if( out!=null && out!=this ) addDep(out); return false; } + for( int j=1; j>"; } - @Override public String glabel() { return ">>"; } - @Override - public Type compute() { - Type t1 = in(1)._type, t2 = in(2)._type; - if( t1.isHigh() || t2.isHigh() ) - return TypeInteger.TOP; - if (t1 instanceof TypeInteger i0 && - t2 instanceof TypeInteger i1) { - if( i0 == TypeInteger.ZERO ) - return TypeInteger.ZERO; - if( i0.isConstant() && i1.isConstant() ) - return TypeInteger.constant(i0.value()>>i1.value()); - if( i1.isConstant() ) { - int log = (int)i1.value(); - return TypeInteger.make(-1L<<(63-log),(1L<<(63-log))-1); - } + @Override long doOp( long x, long y ) { return x >> y; } + @Override TypeInteger doOp( TypeInteger x, TypeInteger y ) { + if( y.isConstant() ) { + int log = (int)y.value(); + return TypeInteger.make(-1L<<(63-log),(1L<<(63-log))-1); } return TypeInteger.BOT; } @@ -44,7 +32,7 @@ public class SarNode extends LogicalNode { // TODO: x >> 3 >> (y ? 1 : 2) ==> x >> (y ? 4 : 5) - return null; + return super.idealize(); } @Override Node copy(Node lhs, Node rhs) { return new SarNode(_loc,lhs,rhs); } } diff --git a/src/main/java/com/seaofnodes/simple/node/ScopeNode.java b/src/main/java/com/seaofnodes/simple/node/ScopeNode.java index 38a5d968..3127e491 100644 --- a/src/main/java/com/seaofnodes/simple/node/ScopeNode.java +++ b/src/main/java/com/seaofnodes/simple/node/ScopeNode.java @@ -25,7 +25,10 @@ public class ScopeNode extends MemMergeNode { // Size of each nested lexical scope public final Ary _lexSize; - // Lexical scope is one of normal Block, constructor or function + // Lexical scope is typed: + // - Block scope - basic block scoping + // - Constructor - struct type is defined, and here we give values to all fields + // - Function - out-of-scope lookups restricted to final constants public enum Kind { Block, Constructor, Function }; public final Ary _kinds; @@ -49,7 +52,7 @@ public class ScopeNode extends MemMergeNode { int j=1; for( int i=0; i N ctrl(N n) { return setDef(0,n); } public Node mem(Node n) { return setDef(1,n); } - public void push(Kind kind) { + public void push( Kind kind ) { assert _lexSize._len==_kinds._len; _lexSize.push(_vars.size()); _kinds .push(kind); @@ -94,11 +98,11 @@ public class ScopeNode extends MemMergeNode { // Pop a lexical scope public void pop() { assert _lexSize._len==_kinds._len; - promote(); + promote(); // Promote forward references to the next outer scope int n = _lexSize.pop(); _kinds.pop(); - popUntil(n); - _vars.setLen(n); + popUntil(n); // Pop off inputs going out of scope + _vars.setLen(n); // Pop off variables going out of scope } @@ -107,7 +111,7 @@ public class ScopeNode extends MemMergeNode { public void promote() { int n = _lexSize.last(); for( int i=n; i=0; i-- ) - if( _vars.at(i)._name.equals(name) ) + if( var(i)._name.equals(name) ) return i; return -1; } @@ -148,7 +152,7 @@ public class ScopeNode extends MemMergeNode { assert _lexSize.isEmpty() || name.charAt(0)!='$' ; // Later scopes do not define memory if( _lexSize._len > 0 ) for( int i=_vars.size()-1; i>=_lexSize.last(); i-- ) { - Var n = _vars.at(i); + Var n = var(i); if( n._name.equals(name) ) { if( !n.isFRef() ) return false; // Double define FRefNode fref = (FRefNode)in(n._idx); // Get forward ref @@ -181,11 +185,11 @@ public class ScopeNode extends MemMergeNode { public Var lookup( String name ) { int idx = find(name); // -1 is missed in all scopes, not found - return idx == -1 ? null : update(_vars.at(idx),null); + return idx == -1 ? null : update(var(idx),null); } /** - * If the name is present in any scope, then redefine else null + * Redefine an existing name * * @param name Name being redefined * @param n The node to bind to the name @@ -193,7 +197,7 @@ public class ScopeNode extends MemMergeNode { public void update( String name, Node n ) { int idx = find(name); assert idx>=0; - update(_vars.at(idx),n); + update(var(idx),n); } public Var update( Var v, Node st ) { @@ -207,7 +211,7 @@ public class ScopeNode extends MemMergeNode { // Set real Phi in the loop head // The phi takes its one input (no backedge yet) from a recursive // lookup, which might have insert a Phi in every loop nest. - : loop.setDef(v._idx,new PhiNode(v._name, v.lazyGLB(), loop.ctrl(), loop.in(loop.update(v,null)._idx),null).peephole()); + : loop.setDef(v._idx,new PhiNode(v._name, v.type(), loop.ctrl(), loop.in(loop.update(v,null)._idx),null).peephole()); setDef(v._idx,old); } assert !v._final || st==null; @@ -260,7 +264,7 @@ public class ScopeNode extends MemMergeNode { for( int i=2; i>shf) != x._min || + ((x._max<>shf) != x._max ) ) + return TypeInteger.make(x._min< x << (y ? 4 : 5) - return null; + return super.idealize(); } @Override Node copy(Node lhs, Node rhs) { return new ShlNode(_loc,lhs,rhs); } } diff --git a/src/main/java/com/seaofnodes/simple/node/ShrNode.java b/src/main/java/com/seaofnodes/simple/node/ShrNode.java index 789fb8a8..269f6b7f 100644 --- a/src/main/java/com/seaofnodes/simple/node/ShrNode.java +++ b/src/main/java/com/seaofnodes/simple/node/ShrNode.java @@ -3,29 +3,17 @@ package com.seaofnodes.simple.node; import com.seaofnodes.simple.Parser; import com.seaofnodes.simple.type.Type; import com.seaofnodes.simple.type.TypeInteger; -import java.util.BitSet; -public class ShrNode extends LogicalNode { +public class ShrNode extends ArithNode { public ShrNode(Parser.Lexer loc, Node lhs, Node rhs) { super(loc, lhs, rhs); } @Override public String label() { return "Shr"; } @Override public String op() { return ">>>"; } - @Override public String glabel() { return ">>>"; } - @Override - public Type compute() { - Type t1 = in(1)._type, t2 = in(2)._type; - if( t1.isHigh() || t2.isHigh() ) - return TypeInteger.TOP; - if (t1 instanceof TypeInteger i0 && - t2 instanceof TypeInteger i1 ) { - if( i0 == TypeInteger.ZERO ) - return TypeInteger.ZERO; - if( i0.isConstant() && i1.isConstant() ) - return TypeInteger.constant(i0.value()>>>i1.value()); - } - return TypeInteger.BOT; + @Override long doOp( long x, long y ) { return x >>> y; } + @Override TypeInteger doOp( TypeInteger x, TypeInteger y ) { + return x == TypeInteger.ZERO ? x : TypeInteger.BOT; } @Override @@ -40,7 +28,7 @@ public class ShrNode extends LogicalNode { // TODO: x >>> 3 >>> (y ? 1 : 2) ==> x >>> (y ? 4 : 5) - return null; + return super.idealize(); } @Override Node copy(Node lhs, Node rhs) { return new ShrNode(_loc,lhs,rhs); } } diff --git a/src/main/java/com/seaofnodes/simple/node/StopNode.java b/src/main/java/com/seaofnodes/simple/node/StopNode.java index 9be08ad9..b65535f1 100644 --- a/src/main/java/com/seaofnodes/simple/node/StopNode.java +++ b/src/main/java/com/seaofnodes/simple/node/StopNode.java @@ -23,11 +23,15 @@ public class StopNode extends CFGNode { @Override public StringBuilder _print1(StringBuilder sb, BitSet visited) { // For the sake of many old tests, and single value prints as "return val" - if( ret()!=null ) return ret()._print0(sb,visited); + ReturnNode ret1 = ret(); + if( ret1!=null ) return ret1._print0(sb,visited); sb.append("Stop[ "); for( Node ret : _inputs ) - if( ret!=null ) - ret._print0(sb, visited).append(" "); + if( ret!=null ) { + String name = ((ReturnNode)ret).fun()._name; + if( name== null || !name.startsWith("sys.") ) + ret._print0(sb, visited).append(" "); + } return sb.append("]"); } @@ -37,7 +41,13 @@ public class StopNode extends CFGNode { // If a single Return, return it. // Otherwise, null because ambiguous. public ReturnNode ret() { - return nIns()==1 && in(0) instanceof ReturnNode ret ? ret : null; + Node ret1 = this; + for( Node ret : _inputs ) { + String name = ((ReturnNode)ret).fun()._name; + if( name==null || !name.startsWith("sys.") ) + ret1 = ret1==this ? ((ReturnNode)ret) : null; + } + return ret1==this ? null : (ReturnNode)ret1; } @Override @@ -49,7 +59,7 @@ public class StopNode extends CFGNode { public Node idealize() { int len = nIns(); for( int i=0; i= store_size && not_affected_bits >= store_size) { + setDef(4, shl.in(1)); + return this; + } + } return null; } @@ -111,8 +128,6 @@ public class StoreNode extends MemOpNode { TypeMemPtr tmp = (TypeMemPtr)ptr()._type; if( tmp._obj.field(_name)._final && !_init ) return Parser.error("Cannot modify final field '"+_name+"'",_loc); - //Type t = val()._type; - //return _init || t.isa(_declaredType) ? null : Parser.error("Cannot store "+t+" into field "+_declaredType+" "+_name,_loc); return null; } } diff --git a/src/main/java/com/seaofnodes/simple/node/SubNode.java b/src/main/java/com/seaofnodes/simple/node/SubNode.java index 4290ff9b..8e4f6f79 100644 --- a/src/main/java/com/seaofnodes/simple/node/SubNode.java +++ b/src/main/java/com/seaofnodes/simple/node/SubNode.java @@ -5,38 +5,22 @@ import com.seaofnodes.simple.type.Type; import com.seaofnodes.simple.type.TypeInteger; import java.util.BitSet; -public class SubNode extends Node { +public class SubNode extends ArithNode { public SubNode(Node lhs, Node rhs) { super(null, lhs, rhs); } @Override public String label() { return "Sub"; } + @Override public String op() { return "-"; } - @Override public String glabel() { return "-"; } - - @Override - public StringBuilder _print1(StringBuilder sb, BitSet visited) { - in(1)._print0(sb.append("("), visited); - in(2)._print0(sb.append("-"), visited); - return sb.append(")"); - } - - @Override - public Type compute() { - Type t1 = in(1)._type, t2 = in(2)._type; - if( t1.isHigh() || t2.isHigh() ) - return TypeInteger.TOP; + @Override long doOp( long x, long y ) { return x - y; } + @Override TypeInteger doOp(TypeInteger x, TypeInteger y) { // Sub of same is 0 if( in(1)==in(2) ) return TypeInteger.ZERO; - if( t1 instanceof TypeInteger i1 && - t2 instanceof TypeInteger i2 ) { - if (i1.isConstant() && i2.isConstant()) - return TypeInteger.constant(i1.value()-i2.value()); - // Fold ranges like {2-3} - {0-1} into {1-3}. - if( !AddNode.overflow(i1._min,-i2._max) && - !AddNode.overflow(i1._max,-i2._min) && - i2._min != Long.MIN_VALUE ) - return TypeInteger.make(i1._min-i2._max,i1._max-i2._min); - } + // Fold ranges like {2-3} - {0-1} into {1-3}. + if( !AddNode.overflow(x._min,-y._max) && + !AddNode.overflow(x._max,-y._min) && + y._min != Long.MIN_VALUE ) + return TypeInteger.make(x._min-y._max,x._max-y._min); return TypeInteger.BOT; } @@ -51,7 +35,7 @@ public class SubNode extends Node { if( in(1) instanceof MinusNode minus ) return new MinusNode(new AddNode(minus.in(1),in(2)).peephole()); - return null; + return super.idealize(); } @Override Node copy(Node lhs, Node rhs) { return new SubNode(lhs,rhs); } diff --git a/src/main/java/com/seaofnodes/simple/node/XorNode.java b/src/main/java/com/seaofnodes/simple/node/XorNode.java index 37d7147d..fdb4863e 100644 --- a/src/main/java/com/seaofnodes/simple/node/XorNode.java +++ b/src/main/java/com/seaofnodes/simple/node/XorNode.java @@ -3,26 +3,16 @@ package com.seaofnodes.simple.node; import com.seaofnodes.simple.Parser; import com.seaofnodes.simple.type.Type; import com.seaofnodes.simple.type.TypeInteger; -import java.util.BitSet; -public class XorNode extends LogicalNode { +public class XorNode extends ArithNode { public XorNode(Parser.Lexer loc, Node lhs, Node rhs) { super(loc, lhs, rhs); } @Override public String label() { return "Xor"; } @Override public String op() { return "^"; } - @Override public String glabel() { return "^"; } - @Override - public Type compute() { - Type t1 = in(1)._type, t2 = in(2)._type; - if( t1.isHigh() || t2.isHigh() ) - return TypeInteger.TOP; - if( t1 instanceof TypeInteger i0 && - t2 instanceof TypeInteger i1 ) { - if( i0.isConstant() && i1.isConstant() ) - return TypeInteger.constant(i0.value()^i1.value()); - } + @Override long doOp( long x, long y ) { return x ^ y; } + @Override TypeInteger doOp( TypeInteger x, TypeInteger y ) { return TypeInteger.BOT; } @@ -48,7 +38,7 @@ public class XorNode extends LogicalNode { Node phicon = AddNode.phiCon(this,true); if( phicon!=null ) return phicon; - return null; + return super.idealize(); } @Override Node copy(Node lhs, Node rhs) { return new XorNode(_loc,lhs,rhs); } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallARM.java index 7f6fe93c..989bd366 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallARM.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallARM.java @@ -8,13 +8,13 @@ import com.seaofnodes.simple.type.TypeFunPtr; public class CallARM extends CallNode implements MachNode, RIPRelSize { final TypeFunPtr _tfp; final String _name; - CallARM(CallNode call, TypeFunPtr tfp) { super(call); _inputs.pop(); // Pop constant target assert tfp.isConstant(); _tfp = tfp; - _name = CodeGen.CODE.link(tfp)._name; + FunNode fun = CodeGen.CODE.link(tfp); + _name = fun==null ? ((ExternNode)call.fptr())._extern : fun._name; // Can be null for extern calls } @Override public String op() { return "call"; } @@ -23,9 +23,12 @@ public class CallARM extends CallNode implements MachNode, RIPRelSize { @Override public TypeFunPtr tfp() { return _tfp; } @Override public RegMask regmap(int i) { return arm.callInMask(_tfp,i,fun()._maxArgSlot); } @Override public RegMask outregmap() { return null; } + @Override public int nargs() { return nIns()-2; } // Minus control, memory, fptr @Override public void encoding( Encoding enc ) { - enc.relo(this); + FunNode fun = CodeGen.CODE.link(_tfp); + if( fun==null ) enc.external(this,_name); + else enc.relo(this); // BL enc.add4(arm.b(arm.OP_CALL,0)); // Target patched at link time } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallEndARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallEndARM.java deleted file mode 100644 index b3c2f6c2..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallEndARM.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.seaofnodes.simple.node.cpus.arm; - -import com.seaofnodes.simple.node.CallEndNode; -import com.seaofnodes.simple.node.MachNode; - -public class CallEndARM extends CallEndNode implements MachNode { - CallEndARM( CallEndNode cend ) { super(cend); } -} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/CastARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/CastARM.java deleted file mode 100644 index a5a4e112..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/CastARM.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.seaofnodes.simple.node.cpus.arm; - -import com.seaofnodes.simple.SB; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.Encoding; -import com.seaofnodes.simple.codegen.RegMask; -import com.seaofnodes.simple.node.CastNode; -import com.seaofnodes.simple.node.MachNode; - -public class CastARM extends CastNode implements MachNode { - public CastARM( CastNode cast ) { super(cast); } - @Override public String op() { return label(); } - @Override public RegMask regmap(int i) { assert i==1; return RegMask.FULL; } - @Override public RegMask outregmap() { return RegMask.FULL; } - @Override public int twoAddress( ) { return 1; } - @Override public void encoding( Encoding enc ) { } - @Override public void asm(CodeGen code, SB sb) { _t.print(sb.p(code.reg(in(1))).p(" isa ")); } -} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/FloatARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/FloatARM.java index 6de65b03..63c57770 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/FloatARM.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/FloatARM.java @@ -20,8 +20,6 @@ public class FloatARM extends ConstantNode implements MachNode, RIPRelSize { @Override public void encoding( Encoding enc ) { enc.largeConstant(this,_con,0,-1/*TODO: ARM-style ELF patching*/); short dst = (short)(enc.reg(this) - arm.D_OFFSET); - double d = ((TypeFloat)_con).value(); - long x = Double.doubleToRawLongBits(d); enc.add4(arm.load_pc(arm.OPF_ARM, 0, dst)); } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/TFPARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/TFPARM.java index 16b56553..606b03f0 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/TFPARM.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/TFPARM.java @@ -4,7 +4,7 @@ import com.seaofnodes.simple.*; import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.ConstantNode; import com.seaofnodes.simple.node.MachNode; -import com.seaofnodes.simple.node.cpus.riscv.riscv; +import com.seaofnodes.simple.node.Node; import com.seaofnodes.simple.type.TypeFunPtr; public class TFPARM extends ConstantNode implements MachNode, RIPRelSize { @@ -25,21 +25,22 @@ public class TFPARM extends ConstantNode implements MachNode, RIPRelSize { } @Override public byte encSize(int delta) { - if( -(1L<<12) <= delta && delta < (1L<<12) ) return 4; - throw Utils.TODO(); + return 8; } // Delta is from opcode start @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { short rpc = enc.reg(this); if(opLen == 8 ) { - // opstart of add - int next = opStart + opLen; + // ARM encoding delta is from PC & 0xFFF + int target = opStart+delta; + int base = opStart & ~0xFFF; + delta = target-base; int adrp_delta = delta >> 12; // patch upper 20 bits via adrp enc.patch4(opStart, arm.adrp(1, adrp_delta & 0b11, 0b10000, adrp_delta >> 2, rpc)); // low 12 bits via add - enc.patch4(next, arm.imm_inst_l(arm.OPI_ADD, delta & 0xfff, rpc)); + enc.patch4(opStart+4, arm.imm_inst_l(arm.OPI_ADD, delta & 0xfff, rpc)); } else { // should not happen as one instruction is 4 byte, and TFP arm encodes 2. throw Utils.TODO(); @@ -50,4 +51,5 @@ public class TFPARM extends ConstantNode implements MachNode, RIPRelSize { String reg = code.reg(this); _con.print(sb.p(reg).p(" #")); } + @Override public boolean eq(Node n) { return this==n; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/TMPARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/TMPARM.java new file mode 100644 index 00000000..68e32c78 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/TMPARM.java @@ -0,0 +1,53 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.ConstantNode; +import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.node.Node; +import com.seaofnodes.simple.type.TypeMemPtr; + +public class TMPARM extends ConstantNode implements MachNode, RIPRelSize { + TMPARM( ConstantNode con ) { super(con); } + @Override public String op() { return "ldp"; } + @Override public RegMask regmap(int i) { return null; } + @Override public RegMask outregmap() { return arm.WMASK; } + @Override public boolean isClone() { return true; } + @Override public TMPARM copy() { return new TMPARM(this); } + @Override public void encoding( Encoding enc ) { + enc.largeConstant(this,((TypeMemPtr)_con)._obj,0,-1); + short dst = enc.reg(this); + // adrp x0, 0 + enc.add4(arm.adrp(1,0, arm.OP_ADRP, 0,dst)); + // add x0, x0, 0 + arm.imm_inst(enc,arm.OPI_ADD,0, dst); + } + + @Override public byte encSize(int delta) { + return 8; + } + + // Delta is from opcode start + @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { + short dst = enc.reg(this); + if(opLen == 8 ) { + // ARM encoding delta is from PC & 0xFFF + int target = opStart+delta; + int base = opStart & ~0xFFF; + delta = target-base; + int adrp_delta = delta >> 12; + // patch upper 20 bits via adrp + enc.patch4(opStart, arm.adrp(1, adrp_delta & 0b11, 0b10000, adrp_delta >> 2, dst)); + // low 12 bits via add + enc.patch4(opStart+4, arm.imm_inst_l(arm.OPI_ADD, delta & 0xfff, dst)); + } else { + throw Utils.TODO(); + } + } + + @Override public void asm(CodeGen code, SB sb) { + String reg = code.reg(this); + _con.print(sb.p(reg).p(" #")); + } + @Override public boolean eq(Node n) { return this==n; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java index 3e39adc0..e6f64158 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java @@ -568,6 +568,8 @@ public class arm extends Machine { if( idx==0 ) return CodeGen.CODE._rpcMask; if( idx==1 ) return null; // Count floats in signature up to index + if( idx-2 >= tfp.nargs() ) return null; // Anti-dependence + // Count floats in signature up to index int fcnt=0; for( int i=2; i new AddFARM(addf); - case AddNode add -> add(add); - case AndNode and -> and(and); - case BoolNode bool -> cmp(bool); - case CallNode call -> call(call); - case CastNode cast -> new CastARM(cast); - case CallEndNode cend -> new CallEndARM(cend); - case CProjNode c -> new CProjNode(c); - case ConstantNode con -> con(con); - case DivFNode divf -> new DivFARM(divf); - case DivNode div -> new DivARM(div); - case FunNode fun -> new FunARM(fun); - case IfNode iff -> jmp(iff); - case LoadNode ld -> ld(ld); - case MemMergeNode mem -> new MemMergeNode(mem); - case MinusNode neg -> new NegARM(neg); - case MulFNode mulf -> new MulFARM(mulf); - case MulNode mul -> new MulARM(mul); - case NewNode nnn -> new NewARM(nnn); - case NotNode not -> new NotARM(not); - case OrNode or -> or(or); - case ParmNode parm -> new ParmARM(parm); - case PhiNode phi -> new PhiNode(phi); - case ProjNode prj -> new ProjARM(prj); - case ReadOnlyNode read -> new ReadOnlyNode(read); - case ReturnNode ret -> new RetARM(ret,ret.fun()); - case SarNode sar -> asr(sar); - case ShlNode shl -> lsl(shl); - case ShrNode shr -> lsr(shr); + case AddFNode addf -> new AddFARM(addf); + case AddNode add -> add(add); + case AndNode and -> and(and); + case BoolNode bool -> cmp(bool); + case CallNode call -> call(call); + case CastNode cast -> new CastMach(cast); + case CallEndNode cend-> new CallEndMach(cend); + case CProjNode c -> new CProjNode(c); + case ConstantNode con-> con(con); + case DivFNode divf -> new DivFARM(divf); + case DivNode div -> new DivARM(div); + case FunNode fun -> new FunARM(fun); + case IfNode iff -> jmp(iff); + case LoadNode ld -> ld(ld); + case MemMergeNode mem-> new MemMergeNode(mem); + case MinusNode neg -> new NegARM(neg); + case MulFNode mulf -> new MulFARM(mulf); + case MulNode mul -> new MulARM(mul); + case NewNode nnn -> new NewARM(nnn); + case NotNode not -> new NotARM(not); + case OrNode or -> or(or); + case ParmNode parm -> new ParmARM(parm); + case PhiNode phi -> new PhiNode(phi); + case ProjNode prj -> new ProjARM(prj); + case ReadOnlyNode read -> new ReadOnlyMach(read); + case ReturnNode ret -> new RetARM(ret,ret.fun()); + case SarNode sar -> asr(sar); + case ShlNode shl -> lsl(shl); + case ShrNode shr -> lsr(shr); case StartNode start -> new StartNode(start); - case StopNode stop -> new StopNode(stop); - case StoreNode st -> st(st); - case SubFNode subf -> new SubFARM(subf); - case SubNode sub -> sub(sub); - case ToFloatNode tfn-> new I2F8ARM(tfn); - case XorNode xor -> xor(xor); - - case LoopNode loop -> new LoopNode(loop); + case StopNode stop -> new StopNode(stop); + case StoreNode st -> st(st); + case SubFNode subf -> new SubFARM(subf); + case SubNode sub -> sub(sub); + case ToFloatNode tfn -> new I2F8ARM(tfn); + case XorNode xor -> xor(xor); + + case LoopNode loop -> new LoopNode(loop); case RegionNode region-> new RegionNode(region); default -> throw Utils.TODO(); }; @@ -729,13 +731,13 @@ public class arm extends Machine { private Node con( ConstantNode con ) { if( !con._con.isConstant() ) return new ConstantNode( con ); // Default unknown caller inputs return switch( con._con ) { - case TypeInteger ti -> new IntARM(con); - case TypeFloat tf -> new FloatARM(con); - case TypeFunPtr tfp -> new TFPARM(con); - case TypeMemPtr tmp -> new ConstantNode(con); - case TypeNil tn -> throw Utils.TODO(); - // TOP, BOTTOM, XCtrl, Ctrl, etc. Never any executable code. - case Type t -> t==Type.NIL ? new IntARM(con) : new ConstantNode(con); + case TypeInteger ti -> new IntARM(con); + case TypeFloat tf -> new FloatARM(con); + case TypeFunPtr tfp -> new TFPARM(con); + case TypeMemPtr tmp -> new TMPARM(con); + case TypeNil tn -> throw Utils.TODO(); + // TOP, BOTTOM, XCtrl, Ctrl, etc. Never any executable code. + case Type t -> t==Type.NIL ? new IntARM(con) : new ConstantNode(con); }; } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallEndRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallEndRISC.java deleted file mode 100644 index 7df68e77..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallEndRISC.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.seaofnodes.simple.node.cpus.riscv; - -import com.seaofnodes.simple.node.CallEndNode; -import com.seaofnodes.simple.node.MachNode; - -public class CallEndRISC extends CallEndNode implements MachNode { - CallEndRISC( CallEndNode cend ) { super(cend); } -} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRISC.java index 7a0f3313..89b2c865 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRISC.java @@ -13,23 +13,26 @@ public class CallRISC extends CallNode implements MachNode, RIPRelSize { assert tfp.isConstant(); _inputs.pop(); // Pop constant target _tfp = tfp; - _name = CodeGen.CODE.link(tfp)._name; + FunNode fun = CodeGen.CODE.link(tfp); + _name = fun==null ? ((ExternNode)call.fptr())._extern : fun._name; // Can be null for extern calls } @Override public String op() { return "call"; } @Override public String label() { return op(); } - @Override public RegMask regmap(int i) { - return riscv.callInMask(_tfp,i,fun()._maxArgSlot); - } - @Override public RegMask outregmap() { return riscv.RPC_MASK; } @Override public String name() { return _name; } @Override public TypeFunPtr tfp() { return _tfp; } + @Override public RegMask regmap(int i) { return riscv.callInMask(_tfp,i,fun()._maxArgSlot); } + @Override public RegMask outregmap() { return riscv.RPC_MASK; } + @Override public int nargs() { return nIns()-2; } // Minus control, memory, fptr @Override public void encoding( Encoding enc ) { // Short form +/-4K: beq r0,r0,imm12 // Long form: auipc rX,imm20/32; jal r0,[rX+imm12/32] - enc.relo(this); - enc.add4(riscv.j_type(riscv.OP_JAL, riscv.RPC, 0)); + FunNode fun = CodeGen.CODE.link(_tfp); + if( fun==null ) enc.external(this,_name); + else enc.relo(this); + short rpc = enc.reg(this); + enc.add4(riscv.j_type(riscv.OP_JAL, rpc, 0)); } // Delta is from opcode start @@ -41,8 +44,9 @@ public class CallRISC extends CallNode implements MachNode, RIPRelSize { // Delta is from opcode start @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { + short rpc = enc.reg(this); if( opLen==4 ) { - enc.patch4(opStart,riscv.j_type(riscv.OP_JAL, riscv.RPC, delta)); + enc.patch4(opStart,riscv.j_type(riscv.OP_JAL, rpc, delta)); } else { throw Utils.TODO(); } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CastRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CastRISC.java deleted file mode 100644 index c404aaa5..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CastRISC.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.seaofnodes.simple.node.cpus.riscv; - -import com.seaofnodes.simple.SB; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.Encoding; -import com.seaofnodes.simple.codegen.RegMask; -import com.seaofnodes.simple.node.CastNode; -import com.seaofnodes.simple.node.MachNode; - -public class CastRISC extends CastNode implements MachNode { - public CastRISC( CastNode cast ) { super(cast); } - @Override public String op() { return label(); } - @Override public RegMask regmap(int i) { assert i==1; return RegMask.FULL; } - @Override public RegMask outregmap() { return RegMask.FULL; } - @Override public int twoAddress( ) { return 1; } - @Override public void encoding( Encoding enc ) { } - @Override public void asm(CodeGen code, SB sb) { _t.print(sb.p(code.reg(in(1))).p(" isa ")); } -} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FltRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FltRISC.java index 35de7e04..be699280 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FltRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FltRISC.java @@ -16,7 +16,7 @@ public class FltRISC extends ConstantNode implements MachNode, RIPRelSize { @Override public FltRISC copy() { return new FltRISC(this); } @Override public void encoding( Encoding enc ) { - enc.largeConstant(this,_con, 0, -1/*TODO: RISC5 style patching*/); + enc.largeConstant(this,_con, 0, -1); short dst = (short)(enc.reg(this) - riscv.F_OFFSET); short tmp = (short)riscv.T6; // AUIPC dst,#hi20_constant_pool @@ -27,7 +27,6 @@ public class FltRISC extends ConstantNode implements MachNode, RIPRelSize { @Override public RegMask killmap() { return new RegMask(riscv.T6); } // Delta is from opcode start. - // TODO: always size 8? @Override public byte encSize(int delta) { return 8; } // Delta is from opcode start diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ImmRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ImmRISC.java index 9303e566..9e7f456a 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ImmRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ImmRISC.java @@ -40,8 +40,7 @@ abstract public class ImmRISC extends MachConcreteNode implements MachNode { @Override public void encoding( Encoding enc ) { short dst = enc.reg(this ); short src = enc.reg(in(1)); - int body = riscv.i_type(opcode(), dst, func3(), src, _imm12 & 0xFFF); - enc.add4(body); + enc.add4(riscv.i_type(opcode(), dst, func3(), src, _imm12 & 0xFFF)); } // General form: "addi rd = rs1 + imm" diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/Int8RISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/Int8RISC.java new file mode 100644 index 00000000..f9b7daee --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/Int8RISC.java @@ -0,0 +1,47 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.node.ConstantNode; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.MachNode; + +// Special instruction for loading 8 byte constants from the constant pool. + +public class Int8RISC extends ConstantNode implements MachNode, RIPRelSize { + public Int8RISC(ConstantNode con) { super(con); } + + @Override public String op() { return "ld8"; } + @Override public RegMask regmap(int i) { return null; } + @Override public RegMask outregmap() { return riscv.WMASK; } + @Override public boolean isClone() { return true; } + @Override public Int8RISC copy() { return new Int8RISC(this); } + + @Override public byte encSize(int delta) { return 8; } + + @Override public void encoding( Encoding enc ) { + short dst = enc.reg(this); + short tmp = (short)riscv.T6; + enc.largeConstant(this,_con, 0, -1); + // AUIPC dst,#hi20_constant_pool + enc.add4(riscv.u_type(riscv.OP_AUIPC, tmp, 0)); + // Load dst,[dst+#low12_constant_pool] + enc.add4(riscv.i_type(riscv.OP_LOAD, dst, 0b11, tmp, 0)); + } + @Override public RegMask killmap() { return new RegMask(riscv.T6); } + + // Delta is from opcode start + @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { + short dst = enc.reg(this); + short tmp = (short)riscv.T6; + // AUIPC dst,#hi20_constant_pool + enc.patch4(opStart , riscv.u_type(riscv.OP_AUIPC, tmp, delta>>12)); + // Load dst,[dst+#low12_constant_pool] + enc.patch4(opStart+4, riscv.i_type(riscv.OP_LOAD, dst, 0b11, tmp, delta & 0xFFF)); + } + + @Override public void asm(CodeGen code, SB sb) { + _con.print(sb.p(code.reg(this)).p(" = #")); + } + +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/LUI.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/LUI.java index 75ad6496..b65afc79 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/LUI.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/LUI.java @@ -10,7 +10,7 @@ import com.seaofnodes.simple.type.TypeInteger; public class LUI extends ConstantNode implements MachNode { public LUI( int imm20 ) { super(TypeInteger.constant(imm20)); - assert riscv.imm20Exact((TypeInteger)_con); + assert (imm20 & 0xFFF)==0; } @Override public RegMask regmap(int i) { return null; } @Override public RegMask outregmap() { return riscv.WMASK; } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MemOpRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MemOpRISC.java index b8eb7644..8b84a2b9 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MemOpRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MemOpRISC.java @@ -35,8 +35,9 @@ public abstract class MemOpRISC extends MemOpNode implements MachNode { if(this instanceof StoreRISC) { if( _declaredType == TypeInteger. I8 || _declaredType == TypeInteger.U8 || _declaredType == TypeInteger.BOOL) func3=0; // SB if( _declaredType == TypeInteger.I16 || _declaredType == TypeInteger.U16 ) func3=1; // SH - if( _declaredType == TypeInteger.I32 || _declaredType == TypeInteger.U32 || _declaredType instanceof TypeMemPtr) func3=2; // SW - if( _declaredType == TypeInteger.BOT ) func3=3; // SD + if( _declaredType == TypeInteger.I32 || _declaredType == TypeInteger.U32 ) func3=2; // SW + if( _declaredType instanceof TypeMemPtr) func3=3; // SD + if( _declaredType == TypeInteger.BOT ) func3=3; // SD if( func3 == -1 ) throw Utils.TODO(); return func3; } @@ -50,9 +51,10 @@ public abstract class MemOpRISC extends MemOpNode implements MachNode { if( _declaredType == TypeInteger.U32 ) func3=6; // LWU // float - if(_declaredType == TypeFloat.F32) func3 = 2; // fLW fSW - if(_declaredType == TypeFloat.F64) func3 = 3; // fLD fSD - if( _declaredType instanceof TypeMemPtr ) func3=3; // 8 byte pointers + if( _declaredType == TypeFloat.F32) func3 = 2; // fLW fSW + if( _declaredType == TypeFloat.F64) func3 = 3; // fLD fSD + + if( _declaredType instanceof TypeMemPtr) func3 = 3; // 8 byte pointers (pick ld) if( func3 == -1 ) throw Utils.TODO(); return func3; } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraIRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraIRISC.java index 26404e95..1533003c 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraIRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraIRISC.java @@ -4,7 +4,9 @@ import com.seaofnodes.simple.Utils; import com.seaofnodes.simple.node.Node; public class SraIRISC extends ImmRISC { - public SraIRISC( Node and, int imm) { super(and,imm); } + public SraIRISC( Node and, int imm) { + super(and,imm | (0x20 << 5)); + } @Override int opcode() { return riscv.OP_IMM; } @Override int func3() { return 5;} @Override public String glabel() { return ">>"; } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/TFPRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/TFPRISC.java index 17613009..15be82bc 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/TFPRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/TFPRISC.java @@ -4,6 +4,7 @@ import com.seaofnodes.simple.*; import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.ConstantNode; import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.node.Node; import com.seaofnodes.simple.type.TypeFunPtr; public class TFPRISC extends ConstantNode implements MachNode, RIPRelSize { @@ -19,11 +20,9 @@ public class TFPRISC extends ConstantNode implements MachNode, RIPRelSize { short dst = enc.reg(this); TypeFunPtr tfp = (TypeFunPtr)_con; // auipc t0,0 - int auipc = riscv.u_type(riscv.OP_AUIPC, dst, 0); + enc.add4(riscv.u_type(riscv.OP_AUIPC, dst, 0)); // addi t1,t0 + #0 - int addi = riscv.i_type(riscv.OP_IMM, dst, 0, dst, 0); - enc.add4(auipc); - enc.add4(addi); + enc.add4(riscv.i_type(riscv.OP_IMM, dst, 0, dst, 0)); } @Override public byte encSize(int delta) { @@ -38,7 +37,7 @@ public class TFPRISC extends ConstantNode implements MachNode, RIPRelSize { // AUIPC (upper 20 bits) // opstart of add int next = opStart + opLen; - enc.patch4(opStart,riscv.u_type(riscv.OP_AUIPC, rpc, delta)); + enc.patch4(opStart,riscv.u_type(riscv.OP_AUIPC, rpc, delta>>12)); // addi(low 12 bits) enc.patch4(next,riscv.i_type(riscv.OP_IMM, rpc, 0, rpc, delta & 0xFFF)); // addi @@ -51,5 +50,5 @@ public class TFPRISC extends ConstantNode implements MachNode, RIPRelSize { @Override public void asm(CodeGen code, SB sb) { _con.print(sb.p(code.reg(this)).p(" #")); } - + @Override public boolean eq(Node n) { return this==n; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/TMPRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/TMPRISC.java new file mode 100644 index 00000000..860e5eaf --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/TMPRISC.java @@ -0,0 +1,41 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.ConstantNode; +import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.node.Node; +import com.seaofnodes.simple.type.TypeMemPtr; + +public class TMPRISC extends ConstantNode implements MachNode, RIPRelSize { + TMPRISC(ConstantNode con) { super(con); } + @Override public String op() { return "ldp"; } + @Override public RegMask regmap(int i) { return null; } + @Override public RegMask outregmap() { return riscv.WMASK; } + @Override public boolean isClone() { return true; } + @Override public TMPRISC copy() { return new TMPRISC(this); } + @Override public void encoding( Encoding enc ) { + enc.largeConstant(this,((TypeMemPtr)_con)._obj,0,-1); + short dst = enc.reg(this); + // AUIPC dst,#hi20_constant_pool + enc.add4(riscv.u_type(riscv.OP_AUIPC, dst, 0)); + // addi dst,[dst+#low12_constant_pool] + enc.add4(riscv.i_type(riscv.OP_IMM, dst, 0, dst, 0)); + } + + @Override public byte encSize(int delta) { return 8; } + + // Delta is from opcode start + @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { + short dst = enc.reg(this); + // AUIPC dst,#hi20_constant_pool + enc.patch4(opStart , riscv.u_type(riscv.OP_AUIPC, dst, delta>>12)); + // Load dst,[dst+#low12_constant_pool] + enc.patch4(opStart+4, riscv.i_type(riscv.OP_IMM, dst, 0, dst, delta & 0xFFF)); + } + + @Override public void asm(CodeGen code, SB sb) { + _con.print(sb.p(code.reg(this)).p(" #")); + } + @Override public boolean eq(Node n) { return this==n; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorIRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorIRISC.java index 61f25be2..27098033 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorIRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorIRISC.java @@ -5,6 +5,7 @@ import com.seaofnodes.simple.node.Node; public class XorIRISC extends ImmRISC { public XorIRISC( Node and, int imm) { super(and,imm); } + public XorIRISC( Node and, int imm, boolean pop) { super(and,imm,pop); } @Override int opcode() { return riscv.OP_IMM; } @Override int func3() { return 4; } @Override public String glabel() { return "^"; } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java index 54c6b828..9c5bfa10 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java @@ -188,7 +188,6 @@ public class riscv extends Machine { return (imm12 << 20) | (rs1 << 15) | (func3 << 12) | (rd << 7) | opcode; } - // S-type instructions(store) public static int s_type(int opcode, int func3, int rs1, int rs2, int imm12) { assert 0 <= rs1 && rs1 < 32; @@ -257,6 +256,7 @@ public class riscv extends Machine { static RegMask callInMask( TypeFunPtr tfp, int idx, int maxArgSlot ) { if( idx==0 ) return RPC_MASK; if( idx==1 ) return null; + if( idx-2 >= tfp.nargs() ) return null; // Anti-dependence // Count floats in signature up to index int fcnt=0; for( int i=2; i>>44)<<12) == ti.value(); + return ti.isConstant() && (((ti.value()<<32)>>44)<<12) == ti.value(); } @Override public Node instSelect( Node n ) { @@ -328,8 +328,8 @@ public class riscv extends Machine { case AndNode and -> and(and); case BoolNode bool -> cmp(bool); case CallNode call -> call(call); - case CastNode cast -> new CastRISC(cast); - case CallEndNode cend -> new CallEndRISC(cend); + case CastNode cast -> new CastMach(cast); + case CallEndNode cend -> new CallEndMach(cend); case CProjNode c -> new CProjNode(c); case ConstantNode con -> con(con); case DivFNode divf -> new DivFRISC(divf); @@ -347,7 +347,7 @@ public class riscv extends Machine { case ParmNode parm -> new ParmRISC(parm); case PhiNode phi -> new PhiNode(phi); case ProjNode prj -> prj(prj); - case ReadOnlyNode read-> new ReadOnlyNode(read); + case ReadOnlyNode read-> new ReadOnlyMach(read); case ReturnNode ret -> new RetRISC(ret, ret.fun()); case SarNode sar -> sra(sar); case ShlNode shl -> sll(shl); @@ -420,7 +420,7 @@ public class riscv extends Machine { ? new SetIRISC(bool, (int)ti.value(),false) : new SetRISC(bool, false); // x <= y - flip and negate; !(y < x); `slt tmp=y,x; xori dst=tmp,#1` - case "<=" -> new XorIRISC(new SetRISC(bool.swap12(), false),1); + case "<=" -> new XorIRISC(new SetRISC(bool.swap12(), false),1,false); // x == y - sub and vs0 == `sub tmp=x-y; sltu dst=tmp,#1` case "==" -> new SetIRISC(new SubRISC(bool),1,true); default -> throw Utils.TODO(); @@ -443,12 +443,12 @@ public class riscv extends Machine { } // Need more complex sequence for larger constants... or a load // from a constant pool, which does not need an extra register - throw Utils.TODO(); + yield new Int8RISC(con); } // Load from constant pool case TypeFloat tf -> new FltRISC(con); case TypeFunPtr tfp -> new TFPRISC(con); - case TypeMemPtr tmp -> throw Utils.TODO(); + case TypeMemPtr tmp -> new TMPRISC(con); case TypeNil tn -> throw Utils.TODO(); // TOP, BOTTOM, XCtrl, Ctrl, etc. Never any executable code. case Type t -> t==Type.NIL ? new IntRISC(con) : new ConstantNode(con); diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddIX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddIX86.java index 569be754..050451f1 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddIX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddIX86.java @@ -1,5 +1,8 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; +import com.seaofnodes.simple.SB; +import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.codegen.Encoding; import com.seaofnodes.simple.node.Node; public class AddIX86 extends ImmX86 { @@ -10,4 +13,12 @@ public class AddIX86 extends ImmX86 { @Override public String glabel() { return "+"; } @Override int opcode() { return 0x81; } @Override int mod() { return 0; } + @Override public final void encoding( Encoding enc ) { + if( _imm== 1 || _imm== -1 ) { + short dst = enc.reg(this); // Also src1 + enc.add1( x86_64_v2.rex(0,dst,0)); + enc.add1(0xFF); + enc.add1( x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, _imm==1 ? 0 : 1, dst) ); + } else super.encoding(enc); + } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallEndX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallEndX86.java deleted file mode 100644 index ce8b993f..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallEndX86.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.seaofnodes.simple.node.cpus.x86_64_v2; - -import com.seaofnodes.simple.node.CallEndNode; -import com.seaofnodes.simple.node.MachNode; - -public class CallEndX86 extends CallEndNode implements MachNode { - CallEndX86( CallEndNode cend ) { super(cend); } -} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallX86.java index c97037e2..4696c859 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallX86.java @@ -2,8 +2,7 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; import com.seaofnodes.simple.SB; import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.CallNode; -import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.node.*; import com.seaofnodes.simple.type.TypeFunPtr; public class CallX86 extends CallNode implements MachNode, RIPRelSize { @@ -14,7 +13,8 @@ public class CallX86 extends CallNode implements MachNode, RIPRelSize { _inputs.pop(); // Pop constant target assert tfp.isConstant(); _tfp = tfp; - _name = CodeGen.CODE.link(tfp)._name; + FunNode fun = CodeGen.CODE.link(tfp); + _name = fun==null ? ((ExternNode)call.fptr())._extern : fun._name; // Can be null for extern calls } @Override public String op() { return "call"; } @Override public String label() { return op(); } @@ -25,7 +25,10 @@ public class CallX86 extends CallNode implements MachNode, RIPRelSize { @Override public int nargs() { return nIns()-2; } // Minus control, memory, fptr @Override public void encoding( Encoding enc ) { - enc.relo(this).add1(0xe8).add4(0); + FunNode fun = CodeGen.CODE.link(_tfp); + if( fun==null ) enc.external(this,_name); + else enc.relo(this); + enc.add1(0xe8).add4(0); } // Delta is from opcode start, but X86 measures from the end of the 5-byte encoding diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CastX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CastX86.java deleted file mode 100644 index 9537cfb3..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CastX86.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.seaofnodes.simple.node.cpus.x86_64_v2; - -import com.seaofnodes.simple.SB; -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.CastNode; -import com.seaofnodes.simple.node.MachNode; - -public class CastX86 extends CastNode implements MachNode { - CastX86( CastNode cast ) { super(cast); } - @Override public String op() { return label(); } - @Override public RegMask regmap(int i) { assert i==1; return RegMask.FULL; } - @Override public RegMask outregmap() { return RegMask.FULL; } - @Override public int twoAddress( ) { return 1; } - @Override public void encoding( Encoding enc ) { } - @Override public void asm(CodeGen code, SB sb) { _t.print(sb.p(code.reg(in(1))).p(" isa ")); } -} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpMemX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpMemX86.java index 489853eb..a1845363 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpMemX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpMemX86.java @@ -11,7 +11,7 @@ public class CmpMemX86 extends MemOpX86 { CmpMemX86( BoolNode bool, LoadNode ld, Node base, Node idx, int off, int scale, int imm, Node val, boolean swap ) { super(bool,ld, base, idx, off, scale, imm, val ); _swap = swap; - assert !_swap || val() == null || bool.op()=="==" || bool.op()=="!="; // Cannot swap with immediate + assert !_swap || val() != null || bool.op()=="==" || bool.op()=="!="; // Cannot swap with immediate } @Override public String op() { return ((val()==null && _imm==0) ? "test" : "cmp") + _sz; } @Override public RegMask outregmap() { return x86_64_v2.FLAGS_MASK; } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpX86.java index 06a36d99..3e4200c0 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpX86.java @@ -9,6 +9,12 @@ public class CmpX86 extends MachConcreteNode implements MachNode { @Override public String op() { return "cmp"; } @Override public RegMask regmap(int i) { return x86_64_v2.RMASK; } @Override public RegMask outregmap() { return x86_64_v2.FLAGS_MASK; } + // This one is marginal: cloning a Cmp[reg,reg] means stretching the + // lifetimes of 2 normal registers in exchange for shortening a flags + // register lifetime. Spilling flags is (probably) relatively expensive + // compared to some normal registers - and those normal registers might not + // be spilling! + @Override public boolean isClone() { return true; } @Override public void encoding( Encoding enc ) { short dst = enc.reg(in(1)); diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivX86.java index 87b8b0c3..06f001b0 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivX86.java @@ -8,7 +8,7 @@ public class DivX86 extends MachConcreteNode implements MachNode { DivX86( Node div ) { super(div); } @Override public String op() { return "div"; } @Override public RegMask regmap(int i) { - return (i==1) ? x86_64_v2.RAX_MASK : x86_64_v2.RMASK; + return (i==1) ? x86_64_v2.RAX_MASK : x86_64_v2.DIV_MASK; } @Override public RegMask outregmap() { return x86_64_v2.RAX_MASK; } @Override public RegMask killmap() { return x86_64_v2.RDX_MASK; } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ImmX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ImmX86.java index 760d6805..95f617e7 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ImmX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ImmX86.java @@ -15,11 +15,12 @@ public abstract class ImmX86 extends MachConcreteNode implements MachNode { @Override public RegMask regmap(int i) { return x86_64_v2.RMASK; } @Override public RegMask outregmap() { return x86_64_v2.WMASK; } @Override public int twoAddress() { return 1; } + @Override public RegMask killmap() { return x86_64_v2.FLAGS_MASK; } abstract int opcode(); abstract int mod(); - @Override public final void encoding( Encoding enc ) { + @Override public void encoding( Encoding enc ) { short dst = enc.reg(this); // Also src1 enc.add1(x86_64_v2.rex(0, dst, 0)); // opcode; 0x81 or 0x83; 0x69 or 0x6B diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemAddX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemAddX86.java index a97f3d9f..f0d01b3d 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemAddX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemAddX86.java @@ -13,6 +13,7 @@ public class MemAddX86 extends MemOpX86 { } // Register mask allowed as a result. 0 for no register. @Override public RegMask outregmap() { return null; } + @Override public RegMask killmap() { return x86_64_v2.FLAGS_MASK; } @Override public void encoding( Encoding enc ) { // add something to memory // REX.W + 01 /r | REX.W + 81 /0 id @@ -23,11 +24,11 @@ public class MemAddX86 extends MemOpX86 { enc.add1(x86_64_v2.rex(src, ptr, idx)); // opcode - enc.add1( src == -1 ? 0x81: 0x01); + enc.add1( src == -1 ? (_imm==1 ? 0xFF : 0x81): 0x01); // includes modrm x86_64_v2.indirectAdr(_scale, idx, ptr, _off, src == -1 ? 0 : src, enc); - if( src == -1 ) enc.add4(_imm); + if( src == -1 && _imm!=1 ) enc.add4(_imm); } // General form: "add [base + idx<<2 + 12] += src" @Override public void asm(CodeGen code, SB sb) { diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulIX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulIX86.java index a9a83fb7..0dc16ab1 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulIX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulIX86.java @@ -1,11 +1,34 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; -import com.seaofnodes.simple.node.Node; +import com.seaofnodes.simple.SB; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.codegen.*; -public class MulIX86 extends ImmX86 { - MulIX86( Node add, int imm ) { super(add,imm); } +public class MulIX86 extends MachConcreteNode implements MachNode { + final int _imm; + MulIX86( Node add, int imm ) { + super(add); + _inputs.pop(); // Pop the constant input + _imm = imm; // Record the constant + } @Override public String op() { return "muli"; } @Override public String glabel() { return "*"; } - @Override int opcode() { return 0x69; } - @Override int mod() { return 0; } + @Override public RegMask regmap(int i) { return x86_64_v2.RMASK; } + @Override public RegMask outregmap() { return x86_64_v2.WMASK; } + // Rare 3-address form + @Override public final void encoding( Encoding enc ) { + short dst = enc.reg(this ); // Also src1 + short src = enc.reg(in(1)); + enc.add1(x86_64_v2.rex(src, dst, 0)); + // opcode; 0x69 or 0x6B + enc.add1( 0x69 + (x86_64_v2.imm8(_imm) ? 2 : 0) ); + enc.add1( x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, src) ); + // immediate(4 bytes) 32 bits or (1 byte)8 bits + if( x86_64_v2.imm8(_imm) ) enc.add1(_imm); + else enc.add4(_imm); + } + // General form: "addi dst += #imm" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" * #").p(_imm); + } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RegX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RegX86.java index 0997644d..a4a6a814 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RegX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RegX86.java @@ -8,6 +8,7 @@ public abstract class RegX86 extends MachConcreteNode { RegX86( Node add ) { super(add); } @Override public RegMask regmap(int i) { return x86_64_v2.RMASK; } @Override public RegMask outregmap() { return x86_64_v2.WMASK; } + @Override public RegMask killmap() { return x86_64_v2.FLAGS_MASK; } @Override public int twoAddress() { return 1; } abstract int opcode(); @Override public void encoding( Encoding enc ) { diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SetX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SetX86.java index b2902086..775ea8eb 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SetX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SetX86.java @@ -27,7 +27,7 @@ public class SetX86 extends MachConcreteNode implements MachNode { short dst = enc.reg(this ); // Optional rex, for dst - if( dst >= 8 ) enc.add1(x86_64_v2.rex(0, dst, 0)); + if( dst >= 4 ) enc.add1(x86_64_v2.rex(0, dst, 0, false)); enc.add1(0x0F); // opcode enc.add1(switch (_bop) { case "==" -> 0x94; // SETE @@ -39,7 +39,7 @@ public class SetX86 extends MachConcreteNode implements MachNode { enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, 0, dst)); // low 8 bites are set, now zero extend for next instruction - if( dst >= 8 ) enc.add1(x86_64_v2.rex(0, dst, 0)); + if( dst >= 4 ) enc.add1(x86_64_v2.rex(dst, dst, 0, false)); enc.add1(0x0F); // opcode enc.add1(0xB6); // opcode enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, dst)); diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TFPX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TFPX86.java index be4d1a83..d7566f9e 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TFPX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TFPX86.java @@ -63,4 +63,5 @@ public class TFPX86 extends ConstantNode implements MachNode, RIPRelSize { else _con.print(sb.p(reg).p(" = #")); } + @Override public boolean eq(Node n) { return this==n; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TMPX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TMPX86.java new file mode 100644 index 00000000..c44cf2f9 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TMPX86.java @@ -0,0 +1,35 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.SB; +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.TypeMemPtr; + +public class TMPX86 extends ConstantNode implements MachNode, RIPRelSize{ + TMPX86(ConstantNode con ) { super(con); } + @Override public String op() { return "ldp"; } + @Override public RegMask regmap(int i) { return null; } + @Override public RegMask outregmap() { return x86_64_v2.WMASK; } + @Override public boolean isClone() { return true; } + @Override public Node copy() { return new TMPX86(this); } + + @Override public void encoding( Encoding enc ) { + short dst = enc.reg(this); + x86_64_v2.rexF(dst, 0, 0, true, enc); + enc.add1(0x8D).add1(x86_64_v2.modrm(x86_64_v2.MOD.INDIRECT, dst, 0b101)).add4(0); + enc.largeConstant(this,((TypeMemPtr)_con)._obj,7-4,2/*ELF encoding PC32*/); + } + + // Delta is from opcode start + @Override public byte encSize(int delta) { return (byte)7; } + + // Delta is from opcode start + @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { + enc.patch4(opStart+opLen-4,delta-opLen); + } + + @Override public void asm(CodeGen code, SB sb) { + _con.print(sb.p(code.reg(this)).p(" = #")); + } + @Override public boolean eq(Node n) { return this==n; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java index 2b7fddb1..6706635f 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java @@ -43,6 +43,8 @@ public class x86_64_v2 extends Machine { static RegMask XMASK = new RegMask(FP_BITS); static RegMask FLAGS_MASK = new RegMask(FLAGS); static RegMask RPC_MASK = new RegMask(RPC); + // Divide mask: exclude RDX:RAX + static RegMask DIV_MASK = new RegMask(RD_BITS & ~(1L<= tfp.nargs() ) return null; // Anti-dependence return switch( CodeGen.CODE._callingConv ) { case "SystemV" -> callSys5 (tfp,idx,maxArgSlot); case "Win64" -> callWin64(tfp,idx,maxArgSlot); @@ -343,9 +346,9 @@ public class x86_64_v2 extends Machine { case AndNode and -> and(and); case BoolNode bool -> cmp(bool); case CProjNode c -> new CProjNode(c); - case CallEndNode cend -> new CallEndX86(cend); + case CallEndNode cend -> new CallEndMach(cend); case CallNode call -> call(call); - case CastNode cast -> new CastX86(cast); + case CastNode cast -> new CastMach(cast); case ConstantNode con -> con(con); case DivFNode divf -> new DivFX86(divf); case DivNode div -> new DivX86(div); @@ -362,7 +365,7 @@ public class x86_64_v2 extends Machine { case ParmNode parm -> new ParmX86(parm); case PhiNode phi -> new PhiNode(phi); case ProjNode prj -> prj(prj); - case ReadOnlyNode read-> new ReadOnlyNode(read); + case ReadOnlyNode read-> new ReadOnlyMach(read); case ReturnNode ret -> new RetX86(ret, ret.fun()); case SarNode sar -> sar(sar); case ShlNode shl -> shl(shl); @@ -499,15 +502,16 @@ public class x86_64_v2 extends Machine { } private Node con( ConstantNode con ) { - if(!con._con.isConstant()) return new ConstantNode(con); // Default unknown caller inputs + if( !con._con.isConstant() ) + return new ConstantNode(con); // Default unknown caller inputs return switch (con._con) { - case TypeInteger ti -> new IntX86(con); - case TypeFloat tf -> new FltX86(con); - case TypeFunPtr tfp -> new TFPX86(con); - case TypeMemPtr tmp -> throw Utils.TODO(); - case TypeNil tn -> throw Utils.TODO(); - // TOP, BOTTOM, XCtrl, Ctrl, etc. Never any executable code. - case Type t -> t == Type.NIL ? new IntX86(con) : new ConstantNode(con); + case TypeInteger ti -> new IntX86(con); + case TypeFloat tf -> new FltX86(con); + case TypeFunPtr tfp -> new TFPX86(con); + case TypeMemPtr tmp -> new TMPX86(con); + case TypeNil tn -> throw Utils.TODO(); + // TOP, BOTTOM, XCtrl, Ctrl, etc. Never any executable code. + case Type t -> t == Type.NIL ? new IntX86(con) : new ConstantNode(con); }; } diff --git a/src/main/java/com/seaofnodes/simple/print/ASMPrinter.java b/src/main/java/com/seaofnodes/simple/print/ASMPrinter.java index da269d31..366ae376 100644 --- a/src/main/java/com/seaofnodes/simple/print/ASMPrinter.java +++ b/src/main/java/com/seaofnodes/simple/print/ASMPrinter.java @@ -1,9 +1,11 @@ package com.seaofnodes.simple.print; +import com.seaofnodes.simple.Ary; import com.seaofnodes.simple.SB; import com.seaofnodes.simple.Utils; import com.seaofnodes.simple.codegen.CodeGen; import com.seaofnodes.simple.codegen.Encoding; +import com.seaofnodes.simple.codegen.Encoding.Relo; import com.seaofnodes.simple.node.*; import com.seaofnodes.simple.type.*; import java.util.HashSet; @@ -20,54 +22,71 @@ public abstract class ASMPrinter { if( code._cfg.at(i) instanceof FunNode fun ) iadr = print(iadr,sb,code,fun,i); - // Skip padding - while( ((iadr+7) & -8) > iadr ) - iadr++; - // constant pool + iadr = (iadr+15)&-16; // pad to 16 Encoding enc = code._encoding; - if( enc!=null && !enc._bigCons.isEmpty() ) { - iadr = (iadr+15)&-16; // pad to 16 + if( enc!=null && !enc._bigCons.isEmpty() && iadr < enc._bits.size() ) { + // radix sort the big constants by alignment + Ary[] raligns = new Ary[5]; + for( Node op : enc._bigCons.keySet() ) { + Relo relo = enc._bigCons.get(op); + int align = relo._t.alignment(); + Ary relos = raligns[align]==null ? (raligns[align]=new Ary<>( Relo.class)) : raligns[align]; + relos.add(relo); + } + HashSet targets = new HashSet<>(); sb.p("--- Constant Pool ------").nl(); - // By log size - for( int log = 3; log >= 0; log-- ) { - for( Node op : enc._bigCons.keySet() ) { - Encoding.Relo relo = enc._bigCons.get(op); + + // By alignment + for( int align = 4; align >= 0; align-- ) { + Ary relos = raligns[align]; + if( relos == null ) continue; + for( Relo relo : relos ) { if( targets.contains(relo._t) ) continue; targets.add(relo._t); - if( relo._t.log_size()==log ) { - sb.hex2(iadr).p(" "); - if( relo._t instanceof TypeTuple tt ) { - for( Type tx : tt._types ) { - switch( log ) { - case 0: sb.hex1(enc.read1(iadr)); break; - case 1: sb.hex2(enc.read2(iadr)); break; - case 2: sb.hex4(enc.read4(iadr)); break; - case 3: sb.hex8(enc.read8(iadr)); break; - } - iadr += (1< { + for( Type tx : tt._types ) { + pN(enc,sb,iadr,align).p(" "); + iadr += (1< { + int sz = ts.size(); + int log = 1< { + pN(enc,sb,iadr,align).fix(9-(1<} for C or {@code import java.lang.*} for Java. + */ +public abstract class sys { + + public static final String SYS; + + static { + try { + SYS = Files.readString(Path.of("src/main/smp/sys.smp")); + } + catch( Exception e ) { + throw new RuntimeException(e); + } + } + +} diff --git a/src/main/java/com/seaofnodes/simple/sys/sys.smp b/src/main/java/com/seaofnodes/simple/sys/sys.smp new file mode 100644 index 00000000..4ac925db --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/sys/sys.smp @@ -0,0 +1,58 @@ +// -*- mode: java; -*- + +// top-level default import +struct sys { + // libc bindings + /** https://www.man7.org/linux/man-pages/man2/.2.html */ + struct libc { + i32 errno = "C"; + // fd buf len -> len + { i32 i64 u32 -> u32 } write = "C"; + // fd buf len -> len + { i32 i64 u32 -> u32 } read = "C"; + // status + { i32 -> i32 } _exit = "C"; + // addr len prot flags fd off -> void* + { i64 i64 i32 i32 i32 i32 -> i64 } mmap = "C"; + // mmap flags + i32 PROT_EXEC = 4; + i32 PROT_READ = 1; + i32 PROT_WRITE= 2; + i32 PROT_NONE = 0; + i32 MAP_PRIVATE = 2; + i32 MAP_ANON = 32; + }; + struct ary { + val copy = { u8[] dst, int doff, u8[~] src, int soff, int len -> + for( int i=0; i + i64 ptr = str; // cast array base to i64 + return sys.libc.write(1,ptr,str#); + }; + + // Return stdin as a u8[] + val stdin = { -> + var buf = new u8[8*1024]; + i64 ptr = buf; + int len = 0; + while( true ) { + int rez = sys.libc.read(0,ptr,buf#); + if( rez < 0 ) sys.libc._exit(sys.libc.errno); + len += rez; + if( rez < buf# ) + break; + sys.libc._exit(-2); // Need to implement + } + // Compress + var dst = new u8[len]; + return sys.ary.copy(dst,0,buf,0,len); + }; + + }; +}; diff --git a/src/main/java/com/seaofnodes/simple/type/Field.java b/src/main/java/com/seaofnodes/simple/type/Field.java index ffa9d2a6..ad96369b 100644 --- a/src/main/java/com/seaofnodes/simple/type/Field.java +++ b/src/main/java/com/seaofnodes/simple/type/Field.java @@ -51,8 +51,10 @@ public class Field extends Type { @Override public Field dual() { return make(_fname,_type.dual(),_alias,!_final); } - @Override public Field glb() { - Type glb = _type.glb(); + @Override public boolean isConstant() { return _type.isConstant(); } + + @Override public Field glb(boolean mem) { + Type glb = _type.glb(mem); return (glb==_type && _final) ? this : make(_fname,glb,_alias,true); } @@ -70,5 +72,5 @@ public class Field extends Type { return _type.print(sb.p(_final?"":"!").p(_fname).p(":").p(_alias).p(" : ")); } - @Override public String str() { return _fname; } + @Override public String str() { return (_final?"":"!")+_fname; } } diff --git a/src/main/java/com/seaofnodes/simple/type/Type.java b/src/main/java/com/seaofnodes/simple/type/Type.java index 6df4e7ab..a44cdd61 100644 --- a/src/main/java/com/seaofnodes/simple/type/Type.java +++ b/src/main/java/com/seaofnodes/simple/type/Type.java @@ -44,7 +44,8 @@ public class Type { static final byte TMEM =13; // All memory (alias 0) or A slice of memory - with specific alias static final byte TSTRUCT =14; // Structs; tuples with named fields static final byte TFLD =15; // Named fields in structs - static final byte TRPC =16; // Return Program Control (Return PC or RPC) + static final byte TCONARY =16; // Constant array + static final byte TRPC =17; // Return Program Control (Return PC or RPC) public final byte _type; @@ -75,6 +76,7 @@ public class Type { TypeStruct.gather(ts); TypeTuple.gather(ts); TypeRPC.gather(ts); + TypeConAry.gather(ts); int sz = ts.size(); for( int i = 0; i < sz; i++ ) ts.add(ts.get(i).dual()); @@ -160,6 +162,7 @@ public class Type { // RHS TypeNil vs NIL/XNIL if( _type== TNIL ) return t instanceof TypeNil ptr ? ptr.meet0() : (t._type==TXNIL ? TypePtr.PTR : BOTTOM); if( _type== TXNIL ) return t instanceof TypeNil ptr ? ptr.meetX() : (t._type== TNIL ? TypePtr.PTR : BOTTOM); + // 'this' is only {TCTRL,TXCTRL} // Other non-simple RHS things bottom out if( !t.is_simple() ) return BOTTOM; @@ -200,8 +203,9 @@ public class Type { // Make a zero version of this type, 0 for integers and null for pointers. public Type makeZero() { return Type.NIL; } - /** Compute greatest lower bound in the lattice */ - public Type glb() { return Type.BOTTOM; } + /** Compute greatest lower bound in the lattice. If values are in memory, + * ints and floats cannot widen. */ + public Type glb(boolean mem) { return Type.BOTTOM; } // Is forward-reference public boolean isFRef() { return false; } @@ -218,7 +222,9 @@ public class Type { // Sizes are expected to be between 1 and 64 bits. // Size 0 means this either takes no space (such as a known-zero field) // or isn't a scalar to be stored in memory. - public int log_size() { return 3; } + public int log_size () { return 3; } // log-size of a type; log_size for a struct is usually undefined + public int size() { return 1< extends Type { + boolean _any; + // One of byte,short,int,long,float,double array + public final A _ary; + + TypeConAry( boolean any, A ary ) { super(TCONARY); _any = any; _ary = ary; } + private static final TypeConAry TOP = new TypeConAry(true ,null).intern(); + public static final TypeConAry BOT = new TypeConAry(false,null).intern(); + public static void gather(ArrayList ts) { + ts.add(BOT); + TypeConAryB.gather(ts); + TypeConAryI.gather(ts); + } + + @Override public String str() { return (_any?"~":"") + "[]"; } + @Override public boolean isConstant() { return true; } + @Override public TypeConAry dual() { + if( this== BOT ) return TOP; + if( this== TOP ) return BOT; + return this; + } + + @Override Type xmeet(Type t) { + TypeConAry ary = (TypeConAry)t; // Invariant + if( this==TOP ) return ary ; + if( ary ==TOP ) return this; + assert _ary!=ary._ary; // Already interned and this!=t + return BOT; + } + + // Meet-over-elements type + public Type elem() { + if( _ary==null ) + return _any ? TOP : BOTTOM; + int len = len(); + long min = Long.MAX_VALUE; + long max = Long.MIN_VALUE; + for( int i=0; i { + private TypeConAryB(byte[] bs) { super(false,bs); } + public static TypeConAryB make( byte[] bs ) { return new TypeConAryB(bs).intern(); } + public static TypeConAryB make( String s ) { return make(s.getBytes()); } + static final TypeConAryB ABC = make("abc"); + static final TypeConAryB ABCD = make("abcd"); + public static void gather(ArrayList ts) { ts.add(ABC); ts.add(ABCD); } + @Override public long at(int idx) { return _ary[idx]; } + @Override public int len() { return _ary.length; } + @Override public int alignment() { return 0; } + @Override public String str() { return "[\""+new String(_ary)+"\"]"; } + @Override boolean eq(Type t) { return Arrays.equals(_ary,((TypeConAryB)t)._ary); } + @Override int hash() { return Arrays.hashCode(_ary); } + @Override public void write( ByteArrayOutputStream baos ) { baos.write(_ary,0,_ary.length); } +} diff --git a/src/main/java/com/seaofnodes/simple/type/TypeConAryI.java b/src/main/java/com/seaofnodes/simple/type/TypeConAryI.java new file mode 100644 index 00000000..f888a93d --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/type/TypeConAryI.java @@ -0,0 +1,15 @@ +package com.seaofnodes.simple.type; + +import java.util.ArrayList; +import java.util.Arrays; + +public class TypeConAryI extends TypeConAry { + private TypeConAryI(int[] bs) { super(false,bs); } + public static TypeConAryI make( int[] bs ) { return new TypeConAryI(bs).intern(); } + private static final TypeConAryI I123 = make(new int[]{1,2,3}); + public static void gather(ArrayList ts) { ts.add(I123); } + @Override public String str() { return "[i32]"; } + @Override public int alignment() { return 2; } + @Override boolean eq(Type t) { return Arrays.equals(_ary,((TypeConAryI)t)._ary); } + @Override int hash() { return Arrays.hashCode(_ary); } +} diff --git a/src/main/java/com/seaofnodes/simple/type/TypeFloat.java b/src/main/java/com/seaofnodes/simple/type/TypeFloat.java index aa205149..a0940f8b 100644 --- a/src/main/java/com/seaofnodes/simple/type/TypeFloat.java +++ b/src/main/java/com/seaofnodes/simple/type/TypeFloat.java @@ -81,7 +81,7 @@ public class TypeFloat extends Type { } @Override public Type makeZero() { return FZERO; } - @Override public Type glb() { return F64; } + @Override public Type glb(boolean mem) { return mem ? (isHigh() ? dual() : this) : F64; } @Override int hash() { return (int)(Double.hashCode(_con) ^ _sz ^ (1<<17)); } diff --git a/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java b/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java index bd64bd81..abfcf1e3 100644 --- a/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java +++ b/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java @@ -59,7 +59,7 @@ public class TypeFunPtr extends TypeNil { // RHS is NIL; do not deep-dual when crossing the centerline @Override public Type meet0() { return _nil==3 ? this : make((byte)3,_sig,_ret,_fidxs); } - @Override public TypeFunPtr glb() { return make((byte)3,_sig,_ret,-1L); } + @Override public TypeFunPtr glb(boolean mem) { return make((byte)3,_sig,_ret,-1L); } @Override public boolean isHigh () { return _nil <= 1 || (_nil==2 && _fidxs==0); } @Override public boolean isConstant() { return (_nil==2 && Long.bitCount(_fidxs)==1) || (_nil==3 && _fidxs==0); } diff --git a/src/main/java/com/seaofnodes/simple/type/TypeInteger.java b/src/main/java/com/seaofnodes/simple/type/TypeInteger.java index cccb85a5..59378ef3 100644 --- a/src/main/java/com/seaofnodes/simple/type/TypeInteger.java +++ b/src/main/java/com/seaofnodes/simple/type/TypeInteger.java @@ -75,11 +75,17 @@ public class TypeInteger extends Type { @Override public boolean isConstant() { return _min == _max; } @Override public int log_size() { + if( isHigh() ) return 0; // High types are dead, and should never hit code emission. if( this==I8 || this==U8 || this==BOOL ) return 0; // 1<<0 == 1 bytes if( this==I16 || this==U16 ) return 1; // 1<<1 == 2 bytes if( this==I32 || this==U32 ) return 2; // 1<<2 == 4 bytes if( this==BOT ) return 3; // 1<<3 == 8 bytes - if( isHighOrConst() ) return 0; + if( isConstant() ) { // just const here + if (-0xFF <= _min && _min <= 0xFF) return 0; + else if (-0xFFFF <= _min && _min <= 0xFFFF) return 1; + else if (-0xFFFFFFFFL <= _min && _min <= 0xFFFFFFFFL) return 2; + else return 3; + } throw Utils.TODO(); } @@ -119,7 +125,7 @@ public class TypeInteger extends Type { return this; } @Override public Type makeZero() { return ZERO; } - @Override public TypeInteger glb() { return BOT; } + @Override public Type glb(boolean mem) { return mem ? (isHigh() ? dual() : this) : BOT; } @Override int hash() { return Utils.fold(_min) * Utils.fold(_max); } @Override public boolean eq( Type t ) { TypeInteger i = (TypeInteger)t; // Contract diff --git a/src/main/java/com/seaofnodes/simple/type/TypeMem.java b/src/main/java/com/seaofnodes/simple/type/TypeMem.java index 9ed4c7a8..63fb30ce 100644 --- a/src/main/java/com/seaofnodes/simple/type/TypeMem.java +++ b/src/main/java/com/seaofnodes/simple/type/TypeMem.java @@ -50,7 +50,8 @@ public class TypeMem extends Type { } @Override public boolean isHigh() { return _t.isHigh(); } - @Override public Type glb() { return make(_alias,_t.glb()); } + @Override public int log_size() { throw Utils.TODO(); } + @Override public Type glb(boolean mem) { return make(_alias,_t.glb(true)); } @Override int hash() { return 9876543 + _alias + _t.hashCode(); } diff --git a/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java b/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java index 284bf581..78605e81 100644 --- a/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java +++ b/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java @@ -25,18 +25,22 @@ public class TypeMemPtr extends TypeNil { // (TOP ,true ) - a nil public final TypeStruct _obj; + public final boolean _one; // Singleton instance - private TypeMemPtr(byte nil, TypeStruct obj) { + private TypeMemPtr(byte nil, TypeStruct obj, boolean singleton) { super(TMEMPTR,nil); assert obj!=null; _obj = obj; + _one = singleton; } - static TypeMemPtr make(byte nil, TypeStruct obj) { return new TypeMemPtr(nil, obj).intern(); } + public static TypeMemPtr make(byte nil, TypeStruct obj, boolean one) { return new TypeMemPtr(nil, obj, one).intern(); } + static TypeMemPtr make(byte nil, TypeStruct obj) { return make(nil, obj, false); } + public static TypeMemPtr make (TypeStruct obj) { return make((byte)2, obj); } public static TypeMemPtr makeNullable(TypeStruct obj) { return make((byte)3, obj); } - public static TypeMemPtr make(TypeStruct obj) { return new TypeMemPtr((byte)2, obj).intern(); } - public TypeMemPtr makeFrom(TypeStruct obj) { return obj==_obj ? this : make(_nil, obj); } + + public TypeMemPtr makeFrom(TypeStruct obj) { return obj==_obj ? this : make(_nil, obj, _one); } public TypeMemPtr makeNullable() { return makeFrom((byte)3); } - @Override TypeMemPtr makeFrom(byte nil) { return nil==_nil ? this : make(nil,_obj); } + @Override TypeMemPtr makeFrom(byte nil) { return nil==_nil ? this : make(nil, _obj, _one); } @Override public TypeMemPtr makeRO() { return makeFrom(_obj.makeRO()); } @Override public boolean isFinal() { return _obj.isFinal(); } @@ -52,15 +56,16 @@ public class TypeMemPtr extends TypeNil { @Override public TypeNil xmeet(Type t) { TypeMemPtr that = (TypeMemPtr) t; - return TypeMemPtr.make(xmeet0(that), (TypeStruct)_obj.meet(that._obj)); + return make(xmeet0(that), (TypeStruct)_obj.meet(that._obj), _one & that._one); } @Override - public TypeMemPtr dual() { return TypeMemPtr.make( dual0(), _obj.dual()); } + public TypeMemPtr dual() { return make( dual0(), _obj.dual(), !_one); } // RHS is NIL; do not deep-dual when crossing the centerline @Override Type meet0() { return _nil==3 ? this : make((byte)3,_obj); } + @Override public boolean isConstant() { return _one && _obj.isConstant(); } // True if this "isa" t up to named structures @Override public boolean shallowISA( Type t ) { @@ -73,17 +78,17 @@ public class TypeMemPtr extends TypeNil { throw Utils.TODO(); // return _obj.shallowISA(that._obj); } - @Override public TypeMemPtr glb() { return make((byte)3,_obj.glb()); } + @Override public TypeMemPtr glb(boolean mem) { return make((byte)3,_obj.glb(true)); } // Is forward-reference @Override public boolean isFRef() { return _obj.isFRef(); } @Override public int log_size() { return 3; } // (1<<3)==8-byte pointers - @Override int hash() { return _obj.hashCode() ^ super.hash(); } + @Override int hash() { return _obj.hashCode() ^ super.hash() ^ (_one ? 2048 : 0); } @Override boolean eq(Type t) { TypeMemPtr ptr = (TypeMemPtr)t; // Invariant - return _obj == ptr._obj && super.eq(ptr); + return _obj == ptr._obj && _one == ptr._one && super.eq(ptr); } @Override public String str() { diff --git a/src/main/java/com/seaofnodes/simple/type/TypeNil.java b/src/main/java/com/seaofnodes/simple/type/TypeNil.java index aee623ba..3902c40b 100644 --- a/src/main/java/com/seaofnodes/simple/type/TypeNil.java +++ b/src/main/java/com/seaofnodes/simple/type/TypeNil.java @@ -48,7 +48,7 @@ public abstract class TypeNil extends Type { @Override public boolean isConstant () { return false; } @Override public boolean isHighOrConst() { return isHigh() || isConstant(); } - @Override public Type glb() { return Type.NIL; } + @Override public Type glb(boolean mem) { return Type.NIL; } public boolean notNull() { return _nil==1 || _nil==2; } public boolean nullable() { return _nil==3; } diff --git a/src/main/java/com/seaofnodes/simple/type/TypePtr.java b/src/main/java/com/seaofnodes/simple/type/TypePtr.java index e9091c25..d9de5cd7 100644 --- a/src/main/java/com/seaofnodes/simple/type/TypePtr.java +++ b/src/main/java/com/seaofnodes/simple/type/TypePtr.java @@ -45,7 +45,7 @@ public class TypePtr extends TypeNil { // 0->xscalar, 1->nscalar, 2->nscalar, 3->scalar @Override Type meetX() { return _nil==0 ? XNIL : (_nil==3 ? PTR : NPTR); } - @Override public TypePtr glb() { return PTR; } + @Override public TypePtr glb(boolean mem) { return PTR; } private static final String[] STRS = new String[]{"~ptr","~nptr","nptr","ptr"}; @Override public String str() { return STRS[_nil]; } diff --git a/src/main/java/com/seaofnodes/simple/type/TypeStruct.java b/src/main/java/com/seaofnodes/simple/type/TypeStruct.java index aa6f2dfd..66a37d41 100644 --- a/src/main/java/com/seaofnodes/simple/type/TypeStruct.java +++ b/src/main/java/com/seaofnodes/simple/type/TypeStruct.java @@ -24,15 +24,18 @@ public class TypeStruct extends Type { // "bottom out" is again the forward-ref struct. public final String _name; public final Field[] _fields; + public final TypeConAry _con; // Optional, constant array contents - TypeStruct(String name, Field[] fields) { + private TypeStruct(String name, TypeConAry con, Field[] fields) { super(TSTRUCT); _name = name; _fields = fields; + _con = con; } // All fields directly listed - public static TypeStruct make(String name, Field... fields) { return new TypeStruct(name, fields).intern(); } + public static TypeStruct make(String name, TypeConAry con, Field... fields) { return new TypeStruct(name, con, fields).intern(); } + public static TypeStruct make(String name, Field... fields) { return make(name, TypeConAry.BOT, fields); } public static final TypeStruct TOP = make("$TOP",new Field[0]); public static final TypeStruct BOT = make("$BOT",new Field[0]); public static final TypeStruct TEST = make("test",new Field[]{Field.TEST}); @@ -48,12 +51,16 @@ public class TypeStruct extends Type { } // Array - public static TypeStruct makeAry(TypeInteger len, int lenAlias, Type body, int bodyAlias) { + public static TypeStruct makeAry(String name, TypeInteger len, int lenAlias, Type body, int bodyAlias) { + return makeAry(name,len,lenAlias,body,bodyAlias,false,TypeConAry.BOT); + } + public static TypeStruct makeAry(String name, TypeInteger len, int lenAlias, Type body, int bodyAlias, boolean efinal, TypeConAry con) { assert body instanceof TypeInteger || body instanceof TypeFloat || (body instanceof TypeNil tn && tn.nullable()); - assert len.isa(TypeInteger.U32); - return make("[" + body.str() + "]", + assert efinal || con==TypeConAry.BOT; // No mutable constant arrays + return make(name, + con, Field.make("#" ,len , lenAlias,true ), - Field.make("[]",body,bodyAlias,false)); + Field.make("[]",body,bodyAlias,efinal)); } // A pair of self-cyclic types @@ -62,9 +69,11 @@ public class TypeStruct extends Type { public static final TypeStruct S1 = make("S1", Field.make("a", TypeInteger.BOT, -1, false), Field.make("s2",TypeMemPtr.make((byte)2,S2F),-2, false) ); private static final TypeStruct S2 = make("S2", Field.make("b", TypeFloat .F64, -3, false), Field.make("s1",TypeMemPtr.make((byte)2,S1F),-4, false) ); - private static final TypeStruct ARY = makeAry(TypeInteger.U32,-1,TypeInteger.BOT,-2); + private static final TypeStruct ARY = makeAry("[i64]",TypeInteger.U32,-1,TypeInteger.BOT,-2); + private static final TypeStruct STR = makeAry("[u8]",TypeInteger.U32,-1,TypeInteger.U8,-2); + private static final TypeStruct HELLO= makeAry("[u8]",TypeInteger.constant(3),-1,TypeInteger.U8,-2,true,TypeConAryB.ABC); - public static void gather(ArrayList ts) { ts.add(TEST); ts.add(BOT); ts.add(S1); ts.add(S2); ts.add(ARY); } + public static void gather(ArrayList ts) { ts.add(TEST); ts.add(BOT); ts.add(S1); ts.add(S2); ts.add(ARY); ts.add(STR); ts.add(HELLO); } // Find field index by name public int find(String fname) { @@ -114,7 +123,8 @@ public class TypeStruct extends Type { return BOT; flds[i] = (Field)f0.meet(f1); } - return make(_name,flds); + TypeConAry con = (TypeConAry)_con.meet(that._con); + return make(_name, con, flds); } @Override @@ -125,22 +135,23 @@ public class TypeStruct extends Type { Field[] flds = new Field[_fields.length]; for( int i=0; i<_fields.length; i++ ) flds[i] = _fields[i].dual(); - return make(_name,flds); + TypeConAry con = _con.dual(); + return make(_name,con,flds); } // Keeps the same struct, but lower-bounds all fields. - @Override public TypeStruct glb() { - if( _glb() ) return this; + @Override public TypeStruct glb(boolean mem) { + if( _glb(mem) ) return this; // Need to glb each field Field[] flds = new Field[_fields.length]; for( int i=0; i<_fields.length; i++ ) - flds[i] = _fields[i].glb(); + flds[i] = _fields[i].glb(mem); return make(_name,flds); } - private boolean _glb() { + private boolean _glb(boolean mem) { if( _fields!=null ) for( Field f : _fields ) - if( f.glb() != f ) + if( f.glb(mem) != f ) return false; return true; } @@ -156,10 +167,56 @@ public class TypeStruct extends Type { return true; } + @Override public boolean isConstant() { + int len = _fields.length; + // If we have a constant array, then ignore the last 2 array fields- + // they are effectively constant. + + // Fields are given sizes according to their types at *offset time*. + // And thus layout, packing & padding are determined by the Types + // at *offset-time*, which means *offset-time* has to be picked + // when types are Stable - post/during TypeCheck. + + // The array-length field is shared with other like-sized arrays, so + // you are allowed to mix "abc" and "very_hugh_>256_char_string" and + // the resulting TMP can query the length field (getting 3 or >256 + // respectively), and reading from a prolly 16bit length field. + + // Basically asking: forward-flow declared-types, are these compatible? + // Only array-lengths have an issue here? So internally name fields + // `#0` or `#1` or `#2` or `#3`. + // + // Also means I cannot track exact array length without losing the + // "declared type requires a 4-byte field". + // + // Same-same for all fields in a struct, space in memory is based on + // declared-type at least, possible (future work) optimized where all + // memory versions of the same field get a smaller footprint (or even a + // constant past or moved into prototype) past construction + + if( _con!=TypeConAry.BOT ) len -= 2; + // Check all fields for being constant + for( int i=0; i=2 && _fields[_fields.length-1]._fname=="[]"; } public int aryBase() { assert isAry(); @@ -221,14 +276,12 @@ public class TypeStruct extends Type { // Compute a layout for a collection of fields assert _fields != null; // No forward refs - // Array layout is different: len,[pad],body... - if( isAry() ) - return _offs = new int[]{ 0, _fields[1]._type.log_size() < 3 ? 4 : 8 }; - // Compute a layout - int[] cnts = new int[4]; // Count of fields at log field size - for( Field f : _fields ) - cnts[f._type.log_size()]++; // Log size is 0(byte), 1(i16/u16), 2(i32/f32), 3(i64/dbl) + int[] cnts = new int[5]; // Count of fields at log field size + int flen = _fields.length; + if( isAry() ) flen--; // Array field is aligned differently + for( int i=0; i