summaryrefslogtreecommitdiff
path: root/src/main/java
diff options
context:
space:
mode:
authorCliff Click <cliffc@acm.org>2025-04-21 14:18:23 -0700
committerCliff Click <cliffc@acm.org>2026-08-10 11:11:18 -0700
commit532e0aa730fb8711597ebd846b2687a81f3869f8 (patch)
tree6e4e1031432d5d1af0e28e2ec28619627b162094 /src/main/java
parent1c61d09c8538e894980b3b9580343d5cd2846658 (diff)
downloadSimple-532e0aa730fb8711597ebd846b2687a81f3869f8.tar.gz
Simple-532e0aa730fb8711597ebd846b2687a81f3869f8.zip
Chapter 22: A Hello, World!
Co-authored-by: godalming123 <68993177+godalming123@users.noreply.github.com> Co-authored-by: Tamás Hadházy <85063808+Hels15@users.noreply.github.com> Co-authored-by: Hsiang-Ying Fu <66062782+fuhsnn@users.noreply.github.com>
Diffstat (limited to 'src/main/java')
-rw-r--r--src/main/java/com/seaofnodes/simple/Parser.java235
-rw-r--r--src/main/java/com/seaofnodes/simple/Var.java12
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/CodeGen.java59
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/ElfFile.java9
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/Encoding.java120
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java17
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java26
-rw-r--r--src/main/java/com/seaofnodes/simple/node/AddNode.java47
-rw-r--r--src/main/java/com/seaofnodes/simple/node/AndNode.java29
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ArithNode.java98
-rw-r--r--src/main/java/com/seaofnodes/simple/node/BoolNode.java21
-rw-r--r--src/main/java/com/seaofnodes/simple/node/CFGNode.java14
-rw-r--r--src/main/java/com/seaofnodes/simple/node/CallEndMach.java36
-rw-r--r--src/main/java/com/seaofnodes/simple/node/CallEndNode.java35
-rw-r--r--src/main/java/com/seaofnodes/simple/node/CallNode.java10
-rw-r--r--src/main/java/com/seaofnodes/simple/node/CastMach.java (renamed from src/main/java/com/seaofnodes/simple/node/cpus/arm/CastARM.java)13
-rw-r--r--src/main/java/com/seaofnodes/simple/node/CastNode.java5
-rw-r--r--src/main/java/com/seaofnodes/simple/node/DivNode.java28
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ExternNode.java23
-rw-r--r--src/main/java/com/seaofnodes/simple/node/FunNode.java15
-rw-r--r--src/main/java/com/seaofnodes/simple/node/LoadNode.java6
-rw-r--r--src/main/java/com/seaofnodes/simple/node/LogicalNode.java29
-rw-r--r--src/main/java/com/seaofnodes/simple/node/LoopNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/MemMergeNode.java41
-rw-r--r--src/main/java/com/seaofnodes/simple/node/MemOpNode.java3
-rw-r--r--src/main/java/com/seaofnodes/simple/node/MinusNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/MulNode.java34
-rw-r--r--src/main/java/com/seaofnodes/simple/node/Node.java12
-rw-r--r--src/main/java/com/seaofnodes/simple/node/OrNode.java20
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ParmNode.java4
-rw-r--r--src/main/java/com/seaofnodes/simple/node/PhiNode.java9
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ReadOnlyMach.java (renamed from src/main/java/com/seaofnodes/simple/node/cpus/riscv/CastRISC.java)13
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ReadOnlyNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ReturnNode.java1
-rw-r--r--src/main/java/com/seaofnodes/simple/node/SarNode.java26
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ScopeNode.java44
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ShlNode.java27
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ShrNode.java22
-rw-r--r--src/main/java/com/seaofnodes/simple/node/StopNode.java20
-rw-r--r--src/main/java/com/seaofnodes/simple/node/StoreNode.java29
-rw-r--r--src/main/java/com/seaofnodes/simple/node/SubNode.java36
-rw-r--r--src/main/java/com/seaofnodes/simple/node/XorNode.java18
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/CallARM.java9
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/CallEndARM.java8
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/FloatARM.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/TFPARM.java14
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/TMPARM.java53
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java90
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallEndRISC.java8
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRISC.java20
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/FltRISC.java3
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/ImmRISC.java3
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/Int8RISC.java47
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/LUI.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/MemOpRISC.java12
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraIRISC.java4
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/TFPRISC.java11
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/TMPRISC.java41
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorIRISC.java1
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java16
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddIX86.java11
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallEndX86.java8
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallX86.java11
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CastX86.java16
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpMemX86.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpX86.java6
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivX86.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ImmX86.java3
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemAddX86.java5
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulIX86.java33
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RegX86.java1
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SetX86.java4
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TFPX86.java1
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TMPX86.java35
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java28
-rw-r--r--src/main/java/com/seaofnodes/simple/print/ASMPrinter.java87
-rw-r--r--src/main/java/com/seaofnodes/simple/sys.java25
-rw-r--r--src/main/java/com/seaofnodes/simple/sys/sys.smp58
-rw-r--r--src/main/java/com/seaofnodes/simple/type/Field.java8
-rw-r--r--src/main/java/com/seaofnodes/simple/type/Type.java14
-rw-r--r--src/main/java/com/seaofnodes/simple/type/TypeConAry.java69
-rw-r--r--src/main/java/com/seaofnodes/simple/type/TypeConAryB.java21
-rw-r--r--src/main/java/com/seaofnodes/simple/type/TypeConAryI.java15
-rw-r--r--src/main/java/com/seaofnodes/simple/type/TypeFloat.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/type/TypeInteger.java10
-rw-r--r--src/main/java/com/seaofnodes/simple/type/TypeMem.java3
-rw-r--r--src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java25
-rw-r--r--src/main/java/com/seaofnodes/simple/type/TypeNil.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/type/TypePtr.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/type/TypeStruct.java145
-rw-r--r--src/main/java/com/seaofnodes/simple/type/TypeTuple.java15
92 files changed, 1515 insertions, 750 deletions
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<String, Type> TYPES;
+ public static HashMap<String, Type> TYPES = defaultTypes();
// Mapping from a type name to the constructor for a Type.
public final HashMap<String, StructNode> 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 extends Node> 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<Field> fs = new Ary<>(Field.class);
for( int i=lexlen; i<varlen; i++ ) {
- s.addDef(_scope.in(i));
- Var v = _scope._vars.at(i);
- if( !v.isFRef() ) // Promote to outer scope, not defined here
- fs.push(Field.make(v._name,v.type(),_code.getALIAS(),v._final));
+ Var v = _scope.var(i);
+ if( v.isFRef() ) continue; // Promote to outer scope, not defined here
+ s.addDef(_scope.in(i)); // Keep the program code to generate instance
+ fs.push(Field.make(v._name,v.type(),_code.getALIAS(),v._final));
}
+ // Save instance default constructor
TypeStruct ts = s._ts = TypeStruct.make(typeName, fs.asAry());
+ s.setType(s.compute()); // Do not call peephole, in case the entire struct is a big plain constant
TYPES.put(typeName, TypeMemPtr.make(ts));
- INITS.put(typeName,s.peephole().keep());
+ INITS.put(typeName,s.keep());
+
// Done with struct/block scope
+ _nestedType = old;
require("}");
require(";");
_scope.pop();
@@ -815,6 +847,20 @@ public class Parser {
if( t0 == null && KEYWORDS.contains(tname) )
return posT(old1);
if( t0 == Type.BOTTOM || t0 == Type.TOP ) return t0; // var/val type inference
+
+ // Check for subtype.
+ while( true ) {
+ int old2 = pos();
+ if( !match(".") ) break;
+ String sname = _lexer.matchId();
+ if( sname==null ) { pos(old2); break; }
+ String tsname = tname+"."+sname;
+ t0 = TYPES.get(tsname);
+ if( t0==null ) { pos(old2); t0 = TYPES.get(tname); break; }
+ tname = tsname;
+ }
+
+ // Still no type found? Assume forward reference
Type t1 = t0 == null ? TypeMemPtr.make(TypeStruct.makeFRef(tname)) :t0; // Null: assume a forward ref type
// Nest arrays and '?' as needed
Type t2 = t1;
@@ -824,6 +870,9 @@ public class Parser {
throw error("Type "+t0+" cannot be null");
if( tmp.nullable() ) throw error("Type "+t2+" already allows null");
t2 = tmp.makeNullable();
+ } else if( match("[~]") ) {
+ TypeMemPtr tmp = typeAry(t2);
+ t2 = tmp.makeFrom(tmp._obj.makeRO());
} else if( match("[]") ) {
t2 = typeAry(t2);
} else
@@ -837,13 +886,12 @@ public class Parser {
int old2 = pos();
match("!");
String id = _lexer.matchId();
- if( !(peek(',') || peek(';') || match("->")) )
- 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<Node> args = new Ary<Node>(Node.class);
+ Ary<Node> 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<<fidx );
}
// Signature for MAIN
- public TypeFunPtr _main = makeFun(TypeTuple.MAIN,Type.BOTTOM);
+ public final TypeFunPtr _main = makeFun(TypeTuple.MAIN,Type.BOTTOM);
// Reverse from a constant function pointer to the IR function being called
public FunNode link( TypeFunPtr tfp ) {
assert tfp.isConstant();
@@ -183,6 +185,25 @@ public class CodeGen {
// Pessimistic peephole optimization on a worklist
_iter.iterate(this);
+
+ // Not really a true optimistic pass, but look for unlinked functions.
+ // This can be removed, which may trigger another round of pessimistic.
+ // This is the point where we flip from a virtual Call Graph (any call
+ // can call any function) to having a correct (but conservative) CG.
+
+ FunNode main = link(_main);
+ for( int i=0; i<_start.nOuts(); i++ ) {
+ Node use = _start.out(i);
+ if( use instanceof FunNode fun &&
+ fun.nIns()==2 && fun.in(1)==_start && fun != main &&
+ (fun._name==null || fun._name.startsWith("sys.")) ) {
+ add(fun).setDef(1,Parser.XCTRL);
+ addAll(fun._outputs);
+ i--;
+ }
+ }
+ _iter.iterate(this);
+
_tOpto = (int)(System.currentTimeMillis() - t0);
// TODO:
@@ -220,7 +241,7 @@ public class CodeGen {
_phase = Phase.LoopTree;
long t0 = System.currentTimeMillis();
// Build the loop tree, fix never-exit loops
- _start.buildLoopTree(_stop);
+ _start.buildLoopTree(_start,_stop);
_tLoopTree = (int)(System.currentTimeMillis() - t0);
return this;
}
@@ -248,7 +269,7 @@ public class CodeGen {
public CodeGen instSelect( String cpu, String callingConv ) { return instSelect(cpu,callingConv,PORTS); }
public CodeGen instSelect( String cpu, String callingConv, String base ) {
assert _phase.ordinal() == Phase.LoopTree.ordinal();
- _phase = Phase.InstSelect;
+ _phase = Phase.Select;
_callingConv = callingConv;
@@ -279,14 +300,14 @@ public class CodeGen {
_rpcMask = new RegMask(_mach.rpc());
_retMasks[3] = _rpcMask;
-
// Convert to machine ops
long t0 = System.currentTimeMillis();
_uid = 1; // All new machine nodes reset numbering
var map = new IdentityHashMap<Node,Node>();
_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<Node,String> _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<CFGNode> rpo = new Ary<>(CFGNode.class);
rpos.put(_code._start.loop(),rpo);
BitSet visit = _code.visit();
- //IdentityHashMap<CFGNode,LoopNode> 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<len; i++ ) {
CFGNode bb = _code._cfg.at(i);
- // Functions pad to align 16
- if( bb instanceof FunNode ) {
- int newStart = _opStart[bb._nid]+slide;
- slide += (newStart+15 & -16)-newStart;
- }
_opStart[bb._nid] += slide;
// Slide down all other (non-CFG) ops in the block
for( Node n : bb._outputs )
@@ -389,6 +382,23 @@ public class Encoding {
}
+ // Go again, inserting padding on function headers. Since no
+ // short-jumps span function headers, the padding will not make any
+ // short jumps fail.
+ for( int i=0; i<len; i++ ) {
+ CFGNode bb = _code._cfg.at(i);
+ // Functions pad to align 16
+ if( bb instanceof FunNode ) {
+ int newStart = _opStart[bb._nid]+slide;
+ slide += (newStart+15 & -16)-newStart;
+ }
+ _opStart[bb._nid] += slide;
+ for( Node n : bb._outputs )
+ if( n instanceof MachNode && !(n instanceof CFGNode) )
+ _opStart[n._nid] += slide;
+ }
+
+
// Copy/slide the bits to make space for all the longer branches
int grow = _opStart[_code._cfg.at(len-1)._nid] - oldStarts[len-1];
if( grow > 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<Relo>[] raligns = new Ary[5];
+ for( Node op : _bigCons.keySet() ) {
+ Relo relo = _bigCons.get(op);
+ int align = relo._t.alignment();
+ Ary<Relo> 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<Type,Integer> 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<Relo> 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<ts._fields.length; fn++ ) {
+ Field f = ts._fields[layout[fn]];
+ int foff = ts. offset(layout[fn]);
+ // Pad up to field
+ while( off < foff ) { bits.write(0); off++; };
+ // Constant array fields are special
+ if( f._fname=="[]" ) { // Must be a constant array
+ ts._con.write(bits); // Write the constant array bits
+ off += ts._con.len();
+ } else {
+ int log = f._type.log_size();
+ if( f._fname=="#" ) addN(log,ts._con.len(),bits); // Must be a constant array
+ else addN(log,f._type ,bits);
+ off += 1<<log;
}
}
}
@@ -457,14 +507,16 @@ public class Encoding {
// A series of libc/external calls that Simple can link against in a JIT.
// Since no runtime in the JVM process, using magic numbers for the CPU
// emulators to pick up on.
- public static int SENTINAL_CALLOC = -4;
+ public static int SENTINEL_CALLOC = -4;
+ public static int SENTINEL_WRITE = -8;
void patchGlobalRelocations() {
for( Node src : _externals.keySet() ) {
int start = _opStart[src._nid];
String dst = _externals.get(src);
int target = switch( dst ) {
- case "calloc" -> 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<CFGNode> 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<Node> 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<bb.nOuts(); j++ ) {
diff --git a/src/main/java/com/seaofnodes/simple/node/AddNode.java b/src/main/java/com/seaofnodes/simple/node/AddNode.java
index dd128e76..0f472829 100644
--- a/src/main/java/com/seaofnodes/simple/node/AddNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/AddNode.java
@@ -7,42 +7,21 @@ import java.util.BitSet;
import static com.seaofnodes.simple.Parser.con;
-public class AddNode extends Node {
+public class AddNode extends ArithNode {
public AddNode(Node lhs, Node rhs) { super(null, lhs, rhs); }
@Override public String label() { return "Add"; }
-
- @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;
- if( t1 instanceof TypeInteger i1 &&
- t2 instanceof TypeInteger i2 ) {
- if (i1.isConstant() && i2.isConstant())
- return TypeInteger.constant(i1.value()+i2.value());
- // Fold ranges like {0-1} + {2-3} into {2-4}.
- if( !overflow(i1._min,i2._min) &&
- !overflow(i1._max,i2._max) )
- return TypeInteger.make(i1._min+i2._min,i1._max+i2._max);
- }
+ @Override public String op() { return "+"; }
+
+ @Override long doOp( long x, long y ) { return x + y; }
+ @Override TypeInteger doOp(TypeInteger x, TypeInteger y) {
+ // Fold ranges like {0-1} + {2-3} into {2-4}.
+ if( !overflow(x._min,y._min) &&
+ !overflow(x._max,y._max) )
+ return TypeInteger.make(x._min+y._min,x._max+y._max);
return TypeInteger.BOT;
}
- static boolean overflow( long x, long y ) {
- if( (x ^ y ) < 0 ) return false; // unequal signs, never overflow
- return (x ^ (x + y)) < 0; // sum has unequal signs, so overflow
- }
@Override
public Node idealize () {
Node lhs = in(1);
@@ -113,7 +92,7 @@ public class AddNode extends Node {
if( spine_cmp(lhs.in(2),rhs,this) )
return new AddNode(new AddNode(lhs.in(1),rhs).peephole(),lhs.in(2));
- return null;
+ return super.idealize();
}
// Rotation is only valid for associative ops, e.g. Add, Mul, And, Or, Xor.
@@ -190,10 +169,4 @@ public class AddNode extends Node {
@Override Node copy(Node lhs, Node rhs) { return new AddNode(lhs,rhs); }
@Override Node copyF() { return new AddFNode(null,null); }
- @Override public Parser.ParseException err() {
- if( in(1)._type.isHigh() || in(2)._type.isHigh() ) return null;
- if( !(in(1)._type instanceof TypeInteger) ) return Parser.error("Cannot '"+label()+"' " + in(1)._type,null);
- if( !(in(2)._type instanceof TypeInteger) ) return Parser.error("Cannot '"+label()+"' " + in(2)._type,null);
- return null;
- }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/AndNode.java b/src/main/java/com/seaofnodes/simple/node/AndNode.java
index 3cfcbda3..70b57d6d 100644
--- a/src/main/java/com/seaofnodes/simple/node/AndNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/AndNode.java
@@ -4,30 +4,18 @@ import com.seaofnodes.simple.Parser;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeInteger;
-import java.util.BitSet;
-
-public class AndNode extends LogicalNode {
+public class AndNode extends ArithNode {
public AndNode(Parser.Lexer loc, Node lhs, Node rhs) { super(loc, lhs, rhs); }
@Override public String label() { return "And"; }
@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());
- // Sharpen allowed bits if either value is narrowed
- long mask = i0.mask() & i1.mask();
- return mask < 0 ? TypeInteger.BOT : TypeInteger.make(0,mask);
- }
- return TypeInteger.BOT;
+ @Override long doOp( long x, long y ) { return x & y; }
+ @Override TypeInteger doOp(TypeInteger x, TypeInteger y) {
+ // Sharpen allowed bits if either value is narrowed
+ long mask = x.mask() & y.mask();
+ return mask < 0 ? TypeInteger.BOT : TypeInteger.make(0,mask);
}
@Override
@@ -53,9 +41,10 @@ public class AndNode extends LogicalNode {
// Do we have ((x & (phi cons)) & (phi cons)) ?
// Push constant up through the phi: x & (phi con0&con0 con1&con1...)
Node phicon = AddNode.phiCon(this,true);
- if( phicon!=null ) return phicon;
+ if( phicon!=null )
+ return phicon;
- return null;
+ return super.idealize();
}
@Override Node copy(Node lhs, Node rhs) { return new AndNode(_loc,lhs,rhs); }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/ArithNode.java b/src/main/java/com/seaofnodes/simple/node/ArithNode.java
new file mode 100644
index 00000000..3e376418
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/ArithNode.java
@@ -0,0 +1,98 @@
+package com.seaofnodes.simple.node;
+
+import com.seaofnodes.simple.Parser;
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.util.BitSet;
+
+public abstract class ArithNode extends Node {
+ // Source location for late reported errors
+ Parser.Lexer _loc;
+
+ public ArithNode( Parser.Lexer loc, Node lhs, Node rhs) { super(null, lhs, rhs); _loc = loc; }
+ @Override public String glabel() { return op(); }
+ abstract String op();
+
+ @Override
+ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
+ in(1)._print0(sb.append("("), visited);
+ in(2)._print0(sb.append(op()), visited);
+ return sb.append(")");
+ }
+
+ abstract long doOp(long x, long y);
+ abstract TypeInteger doOp(TypeInteger x, TypeInteger y);
+
+ // Generic airthmetic op math: high returns high; low returns low; 2
+ // constants fold; only 2 non-constants call specialized math.
+ @Override
+ public final TypeInteger compute() {
+ Type t1 = in(1)._type, t2 = in(2)._type;
+ if( t1.isHigh() || t2.isHigh() )
+ return TypeInteger.TOP;
+ if( t1 instanceof TypeInteger x &&
+ t2 instanceof TypeInteger y )
+ return x.isConstant() && y.isConstant()
+ ? con(x,y)
+ : doOp(x,y);
+ return TypeInteger.BOT;
+ }
+
+ private TypeInteger con( Type t1, Type t2 ) {
+ return TypeInteger.constant( doOp( ((TypeInteger)t1).value(), ((TypeInteger)t2).value()) );
+ }
+
+ // Check for "op(Phi(con,x),Phi(con,y))" and push-up through the Phi.
+ // Note that this is the exact reverse of Phi pulling a common op down
+ // to reduce total op-count. We don't get in an endless push-up
+ // push-down peephole cycle because the constants all fold first.
+ // Returns "Phi(op(con,con),op(x,y))".
+
+ // This check is slightly different from the
+
+ // Expected to be called after other ideal checks are done.
+ @Override public Node idealize() {
+ if( in(1) instanceof PhiNode lhs &&
+ in(2) instanceof PhiNode rhs &&
+ lhs.nIns() >= 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<lhs.nIns(); i++ )
+ if( isCon(lhs,rhs,i) )
+ cnt++;
+ if( lhs.nIns()-1 - cnt <= 1 ) {
+ // Profit! Push up-phi and fold
+ Node[] ns = new Node[lhs.nIns()];
+ ns[0] = lhs.in(0);
+ for( int i=1; i<lhs.nIns(); i++ ) {
+ ns[i] = isCon(lhs,rhs,i)
+ ? new ConstantNode(con(lhs.in(i)._type, rhs.in(i)._type)).peephole()
+ : copy(lhs.in(i), rhs.in(i)).peephole();
+ }
+ String label = lhs._label==rhs._label ? lhs._label : lhs._label + rhs._label;
+ return new PhiNode(label,lhs._declaredType,ns).peephole();
+ }
+ }
+ return null;
+ }
+
+ static boolean isCon( PhiNode lhs, PhiNode rhs, int i ) {
+ return lhs.in(i)._type.isConstant() && rhs.in(i)._type.isConstant();
+ }
+
+
+ static boolean overflow( long x, long y ) {
+ if( (x ^ y ) < 0 ) return false; // unequal signs, never overflow
+ return (x ^ (x + y)) < 0; // sum has unequal signs, so overflow
+ }
+
+ @Override public Parser.ParseException err() {
+ if( in(1)._type.isHigh() || in(2)._type.isHigh() ) return null;
+ if( !(in(1)._type instanceof TypeInteger) ) return Parser.error("Cannot '"+op()+"' " + in(1)._type,_loc);
+ if( !(in(2)._type instanceof TypeInteger) ) return Parser.error("Cannot '"+op()+"' " + in(2)._type,_loc);
+ return null;
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/BoolNode.java b/src/main/java/com/seaofnodes/simple/node/BoolNode.java
index fd6448a2..301e1517 100644
--- a/src/main/java/com/seaofnodes/simple/node/BoolNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/BoolNode.java
@@ -130,9 +130,24 @@ abstract public class BoolNode extends Node {
Node copyF() { return new LEF(null,null); }
}
- public static class EQF extends EQ { public EQF(Node lhs, Node rhs) { super(lhs,rhs); } boolean doOp(double lhs, double rhs) { return lhs == rhs; } public boolean isFloat() { return true; } }
- public static class LTF extends LT { public LTF(Node lhs, Node rhs) { super(lhs,rhs); } boolean doOp(double lhs, double rhs) { return lhs < rhs; } public boolean isFloat() { return true; } }
- public static class LEF extends LE { public LEF(Node lhs, Node rhs) { super(lhs,rhs); } boolean doOp(double lhs, double rhs) { return lhs <= rhs; } public boolean isFloat() { return true; } }
+ public static class EQF extends EQ {
+ public EQF(Node lhs, Node rhs) { super(lhs,rhs); }
+ @Override boolean doOp(double lhs, double rhs) { return lhs == rhs; }
+ @Override public boolean isFloat() { return true; }
+ @Override Node copy(Node lhs, Node rhs) { return new EQF(lhs,rhs); }
+ }
+ public static class LTF extends LT {
+ public LTF(Node lhs, Node rhs) { super(lhs,rhs); }
+ @Override boolean doOp(double lhs, double rhs) { return lhs < rhs; }
+ @Override public boolean isFloat() { return true; }
+ @Override Node copy(Node lhs, Node rhs) { return new LTF(lhs,rhs); }
+ }
+ public static class LEF extends LE {
+ public LEF(Node lhs, Node rhs) { super(lhs,rhs); }
+ @Override boolean doOp(double lhs, double rhs) { return lhs <= rhs; }
+ @Override public boolean isFloat() { return true; }
+ @Override Node copy(Node lhs, Node rhs) { return new LEF(lhs,rhs); }
+ }
// Unsigned less that, for range checks. Not directly user writable.
public static class ULT extends BoolNode {
diff --git a/src/main/java/com/seaofnodes/simple/node/CFGNode.java b/src/main/java/com/seaofnodes/simple/node/CFGNode.java
index 7877f93a..1dddc171 100644
--- a/src/main/java/com/seaofnodes/simple/node/CFGNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/CFGNode.java
@@ -126,7 +126,19 @@ public abstract class CFGNode extends Node {
// Tag all CFG Nodes with their containing LoopNode; LoopNodes themselves
// also refer to *their* containing LoopNode, as well as have their depth.
// Start is a LoopNode which contains all at depth 1.
- public void buildLoopTree(StopNode stop) {
+ public void buildLoopTree(StartNode start, StopNode stop) {
+ // Unlink all linked calls. This can remove RPC constants which
+ // shuffled the StartNode outputs so requires a while loop.
+ boolean done=false;
+ while(!done) {
+ done = true;
+ for( Node use : start._outputs )
+ if( use instanceof FunNode fun )
+ for( Node c : fun._inputs )
+ if( c instanceof CallNode call )
+ { call.unlink_all(); done=false; }
+ }
+
_ltree = stop._ltree = Parser.XCTRL._ltree = new LoopTree((StartNode)this);
_bltWalk(2,null,stop, new BitSet());
}
diff --git a/src/main/java/com/seaofnodes/simple/node/CallEndMach.java b/src/main/java/com/seaofnodes/simple/node/CallEndMach.java
new file mode 100644
index 00000000..47cab07a
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/CallEndMach.java
@@ -0,0 +1,36 @@
+package com.seaofnodes.simple.node;
+
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.codegen.*;
+
+public class CallEndMach extends CallEndNode implements MachNode {
+ public CallEndMach( CallEndNode n ) { super(n); }
+ // MachNode specifics, shared across all CPUs
+ public int _xslot;
+ private RegMask _retMask;
+ private RegMask _kills;
+ public void cacheRegs(CodeGen code) {
+ // Return mask depends on TFP (either GPR or FPR)
+ _retMask = code._mach.retMask(call().tfp());
+ // Kill mask is all caller-saves, and any mirror stack slots for args
+ // in registers.
+ RegMaskRW kills = code._callerSave.copy();
+ // Start of stack slots
+ int maxReg = code._mach.regs().length;
+ // Incoming function arg slots, all low numbered in the RA
+ int fslot = fun()._maxArgSlot;
+ // Killed slots for this calls outgoing args
+ int xslot = code._mach.maxArgSlot(call().tfp());
+ _xslot = (maxReg+fslot)+xslot;
+ for( int i=0; i<xslot; i++ )
+ kills.set((maxReg+fslot)+i);
+ _kills = kills;
+ }
+ public String op() { return "cend"; }
+ public RegMask regmap(int i) { return null; }
+ public RegMask outregmap() { return null; }
+ public RegMask outregmap(int idx) { return idx==2 ? _retMask : null; }
+ public RegMask killmap() { return _kills; }
+ public void encoding( Encoding enc ) { }
+ public void asm(CodeGen code, SB sb) { }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/CallEndNode.java b/src/main/java/com/seaofnodes/simple/node/CallEndNode.java
index f99a0b89..cf3f337c 100644
--- a/src/main/java/com/seaofnodes/simple/node/CallEndNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/CallEndNode.java
@@ -22,6 +22,11 @@ public class CallEndNode extends CFGNode implements MultiNode {
public CallNode call() { return (CallNode)in(0); }
+ @Override public CFGNode idom(Node dep) {
+ // Folding the idom is the one inlining Return
+ return _folding ? cfg(1) : super.idom(dep);
+ }
+
@Override
public StringBuilder _print1(StringBuilder sb, BitSet visited) {
sb.append("cend( ");
@@ -37,7 +42,6 @@ public class CallEndNode extends CFGNode implements MultiNode {
if( !(in(0) instanceof CallNode call) )
return TypeTuple.RET.dual();
Type ret = Type.BOTTOM;
- TypeMem mem = TypeMem.BOT;
if( addDep(call.fptr())._type instanceof TypeFunPtr tfp ) {
ret = tfp.ret();
// Here, if I can figure out I've found *all* callers, then I can meet
@@ -102,33 +106,4 @@ public class CallEndNode extends CFGNode implements MultiNode {
return _folding ? in(1).in(idx) : null;
}
- // ------------
- // MachNode specifics, shared across all CPUs
- public int _xslot;
- private RegMask _retMask;
- private RegMask _kills;
- public void cacheRegs(CodeGen code) {
- // Return mask depends on TFP (either GPR or FPR)
- _retMask = code._mach.retMask(call().tfp());
- // Kill mask is all caller-saves, and any mirror stack slots for args
- // in registers.
- RegMaskRW kills = code._callerSave.copy();
- // Start of stack slots
- int maxReg = code._mach.regs().length;
- // Incoming function arg slots, all low numbered in the RA
- int fslot = fun()._maxArgSlot;
- // Killed slots for this calls outgoing args
- int xslot = code._mach.maxArgSlot(call().tfp());
- _xslot = (maxReg+fslot)+xslot;
- for( int i=0; i<xslot; i++ )
- kills.set((maxReg+fslot)+i);
- _kills = kills;
- }
- public String op() { return "cend"; }
- public RegMask regmap(int i) { return null; }
- public RegMask outregmap() { return null; }
- public RegMask outregmap(int idx) { return idx==2 ? _retMask : null; }
- public RegMask killmap() { return _kills; }
- public void encoding( Encoding enc ) { }
- public void asm(CodeGen code, SB sb) { }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/CallNode.java b/src/main/java/com/seaofnodes/simple/node/CallNode.java
index 78a0b153..8b243e87 100644
--- a/src/main/java/com/seaofnodes/simple/node/CallNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/CallNode.java
@@ -32,8 +32,11 @@ public class CallNode extends CFGNode {
return sb.append(")");
}
public String name() {
- if( fptr()._type instanceof TypeFunPtr tfp && tfp.isConstant() )
- return CodeGen.CODE.link(tfp)._name;
+ if( fptr()._type instanceof TypeFunPtr tfp && tfp.isConstant() ) {
+ FunNode fun = CodeGen.CODE.link(tfp);
+ if( fun !=null ) return fun._name;
+ if( fptr() instanceof ExternNode ex ) return ex._extern;
+ }
return null;
}
@@ -162,6 +165,9 @@ public class CallNode extends CFGNode {
if( !arg(i+2)._type.isa(tfp.arg(i)) )
return Parser.error( "Argument #"+i+" isa "+arg(i+2)._type+", but must be a "+tfp.arg(i), _loc);
+ if( tfp.fidxs() < 0 )
+ throw Utils.TODO(); // Infinite unknown TFPs? Should be fairly precise CG
+
return null;
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/CastARM.java b/src/main/java/com/seaofnodes/simple/node/CastMach.java
index a5a4e112..92822433 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/CastARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/CastMach.java
@@ -1,18 +1,19 @@
-package com.seaofnodes.simple.node.cpus.arm;
+package com.seaofnodes.simple.node;
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); }
+/**
+ * Cast a pointer to read-only
+ */
+public class CastMach extends CastNode implements MachNode {
+ public CastMach( CastNode n ) { super(n); }
@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 ")); }
+ @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(in(1))); }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/CastNode.java b/src/main/java/com/seaofnodes/simple/node/CastNode.java
index 0947bcf0..8d8f0257 100644
--- a/src/main/java/com/seaofnodes/simple/node/CastNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/CastNode.java
@@ -4,6 +4,8 @@ import com.seaofnodes.simple.Parser;
import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeInteger;
+import com.seaofnodes.simple.type.TypeMemPtr;
+
import java.util.BitSet;
// Upcast (join) the input to a t. Used after guard test to lift an input.
@@ -37,6 +39,9 @@ public class CastNode extends Node {
@Override
public Type compute() {
+ // Cast array to int
+ if( _t == TypeInteger.BOT && in(1)._type instanceof TypeMemPtr tmp && tmp._obj.isAry() )
+ return _t;
return in(1)._type.join(_t);
}
diff --git a/src/main/java/com/seaofnodes/simple/node/DivNode.java b/src/main/java/com/seaofnodes/simple/node/DivNode.java
index 41518311..0de1c946 100644
--- a/src/main/java/com/seaofnodes/simple/node/DivNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/DivNode.java
@@ -4,32 +4,14 @@ import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeInteger;
import java.util.BitSet;
-public class DivNode extends Node {
+public class DivNode extends ArithNode {
public DivNode(Node lhs, Node rhs) { super(null, lhs, rhs); }
@Override public String label() { return "Div"; }
+ @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;
- if( t1 instanceof TypeInteger i1 &&
- t2 instanceof TypeInteger i2 ) {
- if (i1.isConstant() && i2.isConstant())
- return i2.value() == 0
- ? TypeInteger.ZERO
- : TypeInteger.constant(i1.value()/i2.value());
- }
+ @Override long doOp( long x, long y ) { return y==0 ? 0 : x / y; }
+ @Override TypeInteger doOp(TypeInteger x, TypeInteger y) {
return TypeInteger.BOT;
}
@@ -38,7 +20,7 @@ public class DivNode extends Node {
// Div of 1.
if( in(2)._type == TypeInteger.TRUE )
return in(1);
- return null;
+ return super.idealize();
}
@Override Node copy(Node lhs, Node rhs) { return new DivNode(lhs,rhs); }
diff --git a/src/main/java/com/seaofnodes/simple/node/ExternNode.java b/src/main/java/com/seaofnodes/simple/node/ExternNode.java
new file mode 100644
index 00000000..33fc0c1e
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/ExternNode.java
@@ -0,0 +1,23 @@
+package com.seaofnodes.simple.node;
+
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.Parser;
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeFunPtr;
+import java.util.BitSet;
+
+/**
+ A constant with external linkage.
+ */
+
+public class ExternNode extends ConstantNode {
+ public final String _extern;
+ public ExternNode(Type t, String ex) { super(t); _extern = ex; }
+
+ @Override public String label() { return "#"+_con+":"+_extern; }
+ @Override public String glabel() { return _con.gprint(new SB().p("#")).p(":").p(_extern).toString(); }
+ @Override public String uniqueName() { return "Extern_" + _nid; }
+
+ @Override public boolean eq(Node n) { return this==n; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/FunNode.java b/src/main/java/com/seaofnodes/simple/node/FunNode.java
index b41a7e54..81fa627a 100644
--- a/src/main/java/com/seaofnodes/simple/node/FunNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/FunNode.java
@@ -21,7 +21,7 @@ public class FunNode extends RegionNode {
public String _name; // Debug name
- public FunNode( Parser.Lexer loc, TypeFunPtr sig, Node... nodes ) { super(loc,nodes); _sig = sig; }
+ public FunNode( Parser.Lexer loc, TypeFunPtr sig, String name, Node... nodes ) { super(loc,nodes); _name=name; _sig = sig; }
public FunNode( FunNode fun ) {
super( fun, fun==null ? null : fun._loc );
if( fun!=null ) {
@@ -69,10 +69,17 @@ public class FunNode extends RegionNode {
}
}
+ public void setName( String name ) {
+ if( _name==null ) _name=name;
+ else _name += "."+name;
+ }
+
@Override
public Type compute() {
// Only dead if no callers after SCCP
- return Type.CONTROL;
+ if( unknownCallers() )
+ return Type.CONTROL;
+ return super.compute();
}
@Override
@@ -118,10 +125,10 @@ public class FunNode extends RegionNode {
return _folding ? super.idepth() : CodeGen.CODE.iDepthAt(1);
}
// Bypass Region idom, always assume idom is Start
- @Override public CFGNode idom(Node dep) { return cfg(1); }
+ @Override public CFGNode idom(Node dep) { return _folding && nIns()==3 ? cfg(2) : cfg(1); }
// Always in-progress until we run out of unknown callers
- public boolean unknownCallers() { return in(1) instanceof StartNode; }
+ public boolean unknownCallers() { return nIns()<2 || in(1) instanceof StartNode; }
@Override public boolean inProgress() { return unknownCallers(); }
diff --git a/src/main/java/com/seaofnodes/simple/node/LoadNode.java b/src/main/java/com/seaofnodes/simple/node/LoadNode.java
index 6147de81..eebe94a3 100644
--- a/src/main/java/com/seaofnodes/simple/node/LoadNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/LoadNode.java
@@ -56,6 +56,12 @@ public class LoadNode extends MemOpNode {
return extend(st.val());
}
+ // Simple load-after-MemMerge to a known alias can bypass. Happens when inlining.
+ if( mem instanceof MemMergeNode mem2 ) {
+ setDef(1,mem2.alias(_alias));
+ return this;
+ }
+
// Simple Load-after-New on same address.
if( mem instanceof ProjNode p && p.in(0) instanceof NewNode nnn &&
ptr == nnn.proj(1) ) // Must check same object
diff --git a/src/main/java/com/seaofnodes/simple/node/LogicalNode.java b/src/main/java/com/seaofnodes/simple/node/LogicalNode.java
deleted file mode 100644
index e8552789..00000000
--- a/src/main/java/com/seaofnodes/simple/node/LogicalNode.java
+++ /dev/null
@@ -1,29 +0,0 @@
-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 abstract class LogicalNode extends Node {
- // Source location for late reported errors
- Parser.Lexer _loc;
-
- public LogicalNode(Parser.Lexer loc, Node lhs, Node rhs) { super(null, lhs, rhs); _loc = loc; }
- abstract String op();
-
- @Override
- public StringBuilder _print1(StringBuilder sb, BitSet visited) {
- in(1)._print0(sb.append("("), visited);
- in(2)._print0(sb.append(op()), visited);
- return sb.append(")");
- }
-
-
- @Override public Parser.ParseException err() {
- if( in(1)._type.isHigh() || in(2)._type.isHigh() ) return null;
- if( !(in(1)._type instanceof TypeInteger) ) return Parser.error("Cannot '"+op()+"' " + in(1)._type,_loc);
- if( !(in(2)._type instanceof TypeInteger) ) return Parser.error("Cannot '"+op()+"' " + in(2)._type,_loc);
- return null;
- }
-}
diff --git a/src/main/java/com/seaofnodes/simple/node/LoopNode.java b/src/main/java/com/seaofnodes/simple/node/LoopNode.java
index 2fa070bc..ec9055dc 100644
--- a/src/main/java/com/seaofnodes/simple/node/LoopNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/LoopNode.java
@@ -26,7 +26,7 @@ public class LoopNode extends RegionNode {
}
// Bypass Region idom, same as the default idom() using use in(1) instead of in(0)
- public int idepth() {
+ @Override public int idepth() {
return CodeGen.CODE.validIDepth(_idepth) ? _idepth : (_idepth=CodeGen.CODE.iDepthFrom(idom().idepth()));
}
// Bypass Region idom, same as the default idom() using use in(1) instead of in(0)
diff --git a/src/main/java/com/seaofnodes/simple/node/MemMergeNode.java b/src/main/java/com/seaofnodes/simple/node/MemMergeNode.java
index 32c25a8b..9f2fdd16 100644
--- a/src/main/java/com/seaofnodes/simple/node/MemMergeNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/MemMergeNode.java
@@ -54,7 +54,8 @@ public class MemMergeNode extends Node {
MemMergeNode merge = new MemMergeNode(false);
for( Node n : _inputs )
merge.addDef(n);
- merge._mem(1,null);
+ for( int i=1; i<nIns(); i++ )
+ merge._mem(i,null);
return merge.peephole();
}
@@ -64,24 +65,38 @@ public class MemMergeNode extends Node {
@Override public Node idealize() {
if( inProgress() ) return null;
+ // Fold defaults into the default
+ boolean progress=false, allDefault=true;
+ for( int i=2; i<nIns(); i++ )
+ if( in(1) == in(i) ) { setDef(i,null); progress=true; }
+ else { allDefault=false; }
+
// If not merging any memory (all memory is just the default)
- if( allDefault() )
+ if( allDefault )
return in(1); // Become default memory
- return null;
- }
- private boolean allDefault() {
- for( int i=2; i<nIns(); i++ )
- if( in(1) != in(i) )
- return false;
- return true;
+ // Collapse stacked all-mem
+ if( in(1) instanceof MemMergeNode mem ) {
+ // Goal is to swap my default mem with mem's default mem
+ for( int i=2; i<mem.nIns(); i++ ) {
+ if( mem.in(i) != null ) {
+ // deeper default mem has a non-default
+ if( 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<nIns() && in(alias)!=null ? alias : 1);
+ return alias < nIns() && in(alias)!=null ? in(alias) : in(1);
}
Node alias( int alias, Node st ) {
@@ -116,13 +131,13 @@ public class MemMergeNode extends Node {
void _merge( MemMergeNode that, RegionNode r) {
int len = Math.max(nIns(),that.nIns());
- for( int i = 2; i < len; i++)
+ for( int i = 1; i < len; i++)
if( alias(i) != that.alias(i) ) { // No need for redundant Phis
// If we are in lazy phi mode we need to a lookup
// by alias as it will trigger a phi creation
Node lhs = this._mem(i,null);
Node rhs = that._mem(i,null);
- alias(i, new PhiNode(Parser.memName(i), lhs._type.glb().meet(rhs._type.glb()), r, lhs, rhs).peephole());
+ alias(i, new PhiNode(Parser.memName(i), lhs._type.glb(true).meet(rhs._type.glb(true)), r, lhs, rhs).peephole());
}
}
@@ -132,7 +147,7 @@ public class MemMergeNode extends Node {
for( int i=1; i<nIns(); i++ ) {
if( in(i) instanceof PhiNode phi && phi.region()==scope.ctrl() ) {
assert phi.in(2)==null;
- phi.setDef(2,back.in(i)==scope ? phi : back.in(i)); // Fill backedge
+ phi.setDef(2,back.alias(i)==scope ? phi : back.alias(i)); // Fill backedge
}
if( exit_def == scope ) // Replace a lazy-phi on the exit path also
exit.alias(i,in(i));
diff --git a/src/main/java/com/seaofnodes/simple/node/MemOpNode.java b/src/main/java/com/seaofnodes/simple/node/MemOpNode.java
index 058c86fa..bb84019e 100644
--- a/src/main/java/com/seaofnodes/simple/node/MemOpNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/MemOpNode.java
@@ -75,7 +75,6 @@ public abstract class MemOpNode extends Node {
_declaredType = Type.BOTTOM;
}
- //
static String mlabel(String name) { return "[]".equals(name) ? "ary" : ("#".equals(name) ? "len" : name); }
String mlabel() { return mlabel(_name); }
@@ -85,7 +84,7 @@ public abstract class MemOpNode extends Node {
@Override public StringBuilder _print1( StringBuilder sb, BitSet visited ) { return _printMach(sb,visited); }
public StringBuilder _printMach( StringBuilder sb, BitSet visited ) { throw Utils.TODO(); }
-
+ public int log_size() { return _declaredType.log_size(); }
@Override
public boolean eq(Node n) {
diff --git a/src/main/java/com/seaofnodes/simple/node/MinusNode.java b/src/main/java/com/seaofnodes/simple/node/MinusNode.java
index e06e0c69..059d0d00 100644
--- a/src/main/java/com/seaofnodes/simple/node/MinusNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/MinusNode.java
@@ -23,7 +23,7 @@ public class MinusNode extends Node {
public Type compute() {
if( in(1)._type.isHigh() ) return TypeInteger.TOP;
if( in(1)._type instanceof TypeInteger i0 &&
- !(i0==TypeInteger.BOT || i0._min == Long.MIN_VALUE || i0._max == Long.MIN_VALUE) )
+ i0._min != Long.MIN_VALUE && i0._max != Long.MIN_VALUE )
return TypeInteger.make(-i0._max,-i0._min);
return TypeInteger.BOT;
}
diff --git a/src/main/java/com/seaofnodes/simple/node/MulNode.java b/src/main/java/com/seaofnodes/simple/node/MulNode.java
index 384dbace..debc2c56 100644
--- a/src/main/java/com/seaofnodes/simple/node/MulNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/MulNode.java
@@ -5,32 +5,14 @@ import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeInteger;
import java.util.BitSet;
-public class MulNode extends Node {
+public class MulNode extends ArithNode {
public MulNode(Node lhs, Node rhs) { super(null, lhs, rhs); }
@Override public String label() { return "Mul"; }
+ @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;
- if( t1 instanceof TypeInteger i1 &&
- t2 instanceof TypeInteger i2 ) {
- if( i1==TypeInteger.ZERO || i2==TypeInteger.ZERO)
- return TypeInteger.ZERO;
- if (i1.isConstant() && i2.isConstant())
- return TypeInteger.constant(i1.value()*i2.value());
- }
+ @Override long doOp( long x, long y ) { return x * y; }
+ @Override TypeInteger doOp(TypeInteger x, TypeInteger y) {
return TypeInteger.BOT;
}
@@ -74,14 +56,8 @@ public class MulNode extends Node {
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 MulNode(lhs,rhs); }
@Override Node copyF() { return new MulFNode(null,null); }
- @Override public Parser.ParseException err() {
- if( in(1)._type.isHigh() || in(2)._type.isHigh() ) return null;
- if( !(in(1)._type instanceof TypeInteger) ) return Parser.error("Cannot '"+label()+"' " + in(1)._type.glb(),null);
- if( !(in(2)._type instanceof TypeInteger) ) return Parser.error("Cannot '"+label()+"' " + in(2)._type.glb(),null);
- return null;
- }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/Node.java b/src/main/java/com/seaofnodes/simple/node/Node.java
index 4ca95cae..064f295f 100644
--- a/src/main/java/com/seaofnodes/simple/node/Node.java
+++ b/src/main/java/com/seaofnodes/simple/node/Node.java
@@ -43,7 +43,7 @@ public abstract class Node implements Cloneable {
* walked in either direction. These outputs are typically used for
* efficient optimizations but otherwise have no semantics meaning.
*/
- public Ary<Node> _outputs;
+ public Ary<Node> _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);
@@ -238,7 +238,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();
@@ -254,9 +254,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();
@@ -292,6 +292,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<in(1).nIns(); j++ )
+ if( op.in(j) instanceof ScopeNode || (op.in(j)==null ^ in(1).in(j)==null) )
+ return false; // Lazy Phi input
}
return true;
}
@@ -145,7 +150,7 @@ public class PhiNode extends Node {
if( in(i).in(j) != x )
{ needsPhi=true; break; }
if( needsPhi ) {
- x = new PhiNode(_label,op.in(j)._type.glb());
+ x = new PhiNode(_label,op.in(j)._type.glb(false));
x.addDef(region());
for( int i=1; i<nIns(); i++ )
x.addDef(in(i).in(j));
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CastRISC.java b/src/main/java/com/seaofnodes/simple/node/ReadOnlyMach.java
index c404aaa5..86d17f9b 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CastRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/ReadOnlyMach.java
@@ -1,18 +1,19 @@
-package com.seaofnodes.simple.node.cpus.riscv;
+package com.seaofnodes.simple.node;
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); }
+/**
+ * Cast a pointer to read-only
+ */
+public class ReadOnlyMach extends ReadOnlyNode implements MachNode {
+ public ReadOnlyMach( ReadOnlyNode n ) { super(n); }
@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 ")); }
+ @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(in(1))); }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/ReadOnlyNode.java b/src/main/java/com/seaofnodes/simple/node/ReadOnlyNode.java
index e9c25f47..d8fe6286 100644
--- a/src/main/java/com/seaofnodes/simple/node/ReadOnlyNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/ReadOnlyNode.java
@@ -2,7 +2,6 @@ package com.seaofnodes.simple.node;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeMemPtr;
-
import java.util.BitSet;
/**
@@ -27,4 +26,5 @@ public class ReadOnlyNode extends Node {
return in(1);
return null;
}
+
}
diff --git a/src/main/java/com/seaofnodes/simple/node/ReturnNode.java b/src/main/java/com/seaofnodes/simple/node/ReturnNode.java
index 317f4ce8..b4173871 100644
--- a/src/main/java/com/seaofnodes/simple/node/ReturnNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/ReturnNode.java
@@ -51,7 +51,6 @@ public class ReturnNode extends CFGNode {
@Override
public Type compute() {
if( inProgress () ) return TypeTuple.RET; // In progress
- if( _fun.isDead() ) return TypeTuple.RET.dual(); // Dead another way
return TypeTuple.make(ctrl()._type,mem()._type,expr()._type);
}
diff --git a/src/main/java/com/seaofnodes/simple/node/SarNode.java b/src/main/java/com/seaofnodes/simple/node/SarNode.java
index 4a71ec06..ee15fa65 100644
--- a/src/main/java/com/seaofnodes/simple/node/SarNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/SarNode.java
@@ -3,31 +3,19 @@ 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 SarNode extends LogicalNode {
+public class SarNode extends ArithNode {
public SarNode(Parser.Lexer loc, Node lhs, Node rhs) { super(loc, lhs, rhs); }
@Override public String label() { return "Sar"; }
@Override public String op() { return ">>"; }
-
@Override public String glabel() { return "&gt;&gt;"; }
- @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<Integer> _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<Kind> _kinds;
@@ -49,7 +52,7 @@ public class ScopeNode extends MemMergeNode {
int j=1;
for( int i=0; i<nIns(); i++ ) {
if( j < _lexSize._len && i == _lexSize.at(j) ) { sb.append("| "); j++; }
- Var v = _vars.get(i);
+ Var v = _vars.at(i);
sb.append(v.type().print(new SB()));
sb.append(" ");
if( v._final ) sb.append("!");
@@ -71,6 +74,7 @@ public class ScopeNode extends MemMergeNode {
public Node ctrl() { return in(0); }
public MemMergeNode mem() { return (MemMergeNode)in(1); }
+ public Var var(int i) { return _vars.at(i); }
/**
* The ctrl of a ScopeNode is always bound to the currently active
@@ -85,7 +89,7 @@ public class ScopeNode extends MemMergeNode {
public <N extends Node> 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<nIns(); i++ ) {
- Var v = _vars.at(i);
+ Var v = var(i);
if( !v.isFRef() ) continue;
if( _lexSize._len==1 )
throw Parser.error("Undefined name '" + v._name + "'",v._loc);
@@ -120,7 +124,7 @@ public class ScopeNode extends MemMergeNode {
}
- public boolean inCon() { return _kinds.last() == Kind.Constructor; }
+ public boolean inConstructor() { return _kinds.last() == Kind.Constructor; }
// Is v outside any current function scope?
public boolean outOfFunction( Var v ) {
@@ -134,9 +138,9 @@ public class ScopeNode extends MemMergeNode {
// Find name in reverse, return an index into _vars or -1. Linear scan
// instead of hashtable, but probably doesn't matter until the scan
// typically hits many dozens of variables.
- int find( String name ) {
+ public int find( String name ) {
for( int i=_vars.size()-1; 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<nIns(); i++ )
// For lazy phis on loops we use a sentinel
// that will trigger phi creation on update
- dup.addDef(loop && !_vars.at(i)._final ? this : in(i));
+ dup.addDef(loop && !var(i)._final ? this : in(i));
return dup;
}
@@ -286,7 +290,7 @@ public class ScopeNode extends MemMergeNode {
if( in(i) != that.in(i) ) { // No need for redundant Phis
// If we are in lazy phi mode we need to a lookup
// by name as it will trigger a phi creation
- Var v = _vars.at(i);
+ Var v = var(i);
Node lhs = this.in(this.update(v,null));
Node rhs = that.in(that.update(v,null));
setDef(i, new PhiNode(v._name, v.type(), r, lhs, rhs).peephole());
@@ -297,7 +301,7 @@ public class ScopeNode extends MemMergeNode {
// Forward refs are copied to the other side, "as if" they were there all along.
public void balanceIf( ScopeNode scope ) {
for( int i = nIns(); i < scope.nIns(); i++ ) {
- Var n = scope._vars.at(i);
+ Var n = scope.var(i);
if( n.isFRef() ) { // RHS has forward refs
_vars.add(n); // Copy to LHS
addDef(scope.in(i));
@@ -329,8 +333,8 @@ public class ScopeNode extends MemMergeNode {
// Fill in the backedge of any inserted Phis
void _endLoop( ScopeNode scope, Node back, Node exit ) {
for( int i=2; i<nIns(); i++ ) {
- if( _vars.at(i)._final ) continue; // Final vars did not get modified in the loop
- if( _vars.at(i).type().isHighOrConst() ) continue; // Cannot lift higher than a constant, so no Phi
+ if( var(i)._final ) continue; // Final vars did not get modified in the loop
+ if( var(i).type().isHighOrConst() ) continue; // Cannot lift higher than a constant, so no Phi
if( back.in(i) != scope ) {
PhiNode phi = (PhiNode)in(i);
assert phi.region()==scope.ctrl() && phi.in(2)==null;
diff --git a/src/main/java/com/seaofnodes/simple/node/ShlNode.java b/src/main/java/com/seaofnodes/simple/node/ShlNode.java
index 82e01841..2557bf6b 100644
--- a/src/main/java/com/seaofnodes/simple/node/ShlNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/ShlNode.java
@@ -3,27 +3,24 @@ 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 ShlNode extends LogicalNode {
+public class ShlNode extends ArithNode {
public ShlNode(Parser.Lexer loc, Node lhs, Node rhs) { super(loc, lhs, rhs); }
@Override public String label() { return "Shl"; }
@Override public String op() { return "<<"; }
-
@Override public String glabel() { return "&lt;&lt;"; }
- @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());
+ @Override long doOp( long x, long y ) { return x << y; }
+ @Override TypeInteger doOp( TypeInteger x, TypeInteger y ) {
+ if( x == TypeInteger.ZERO )
+ return TypeInteger.ZERO;
+ if( y.isConstant() ) {
+ int shf = (int)y.value();
+ // If no overflow, shift endpoints
+ if( !(((x._min<<shf)>>shf) != x._min ||
+ ((x._max<<shf)>>shf) != x._max ) )
+ return TypeInteger.make(x._min<<shf,x._max<<shf);
}
return TypeInteger.BOT;
}
@@ -47,7 +44,7 @@ public class ShlNode 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 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 "&gt;&gt;&gt;"; }
- @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<nIns(); i++ )
- if( ((ReturnNode)in(i)).fun().isDead() )
+ if( addDep(((ReturnNode)in(i)).fun()).isDead() )
delDef(i--);
if( len != nIns() ) return this;
return null;
diff --git a/src/main/java/com/seaofnodes/simple/node/StoreNode.java b/src/main/java/com/seaofnodes/simple/node/StoreNode.java
index a6992f04..9b561bed 100644
--- a/src/main/java/com/seaofnodes/simple/node/StoreNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/StoreNode.java
@@ -71,6 +71,12 @@ public class StoreNode extends MemOpNode {
return this;
}
+ // Simple store-after-MemMerge to a known alias can bypass. Happens when inlining.
+ if( mem() instanceof MemMergeNode mem ) {
+ setDef(1,mem.alias(_alias));
+ return this;
+ }
+
// Value is automatically truncated by narrow store
if( val() instanceof AndNode and && and.in(2)._type.isConstant() ) {
int log = _declaredType.log_size();
@@ -84,12 +90,23 @@ public class StoreNode extends MemOpNode {
}
}
- // Store of zero after alloc
- if( mem() instanceof ProjNode prj && prj.in(0) instanceof NewNode &&
- prj.in(0)==ptr().in(0) && // Same NewNode memory & pointer
- (val()._type==TypeInteger.ZERO || val()._type==Type.NIL ) )
- return mem();
+ // Store will chop high order bits off; math to change those bits can be dropped.
+ if( val() instanceof SarNode shr &&
+ shr.in(1) instanceof ShlNode shl &&
+ shr.in(2)._type.isConstant() &&
+ shl.in(2)._type.isConstant() ) {
+ TypeInteger shrC = (TypeInteger) shr.in(2)._type;
+ // size of the thing that sign-extends
+ int base_size = (1 << shr.in(1)._type.log_size()) << 3;
+ int not_affected_bits = base_size - (int) shrC.value();
+ int store_size = (1 << log_size()) << 3;
+ // if the store is unrelated to the shift amount, then get rid of the shift
+ if( shl.in(2)._type == shr.in(2)._type && shrC.value() >= 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/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<idx; i++ )
if( tfp.arg(i-2) instanceof TypeFloat)
@@ -644,44 +646,44 @@ public class arm extends Machine {
// Instruction selection
@Override public Node instSelect(Node n ) {
return switch( n ) {
- 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 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/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<idx; i++ )
@@ -318,7 +318,7 @@ public class riscv extends Machine {
// shift left 32 to clear out the upper 32 bits.
// shift right SIGNED to sign-extend upper 32 bits; then shift 12 more to clear out lower 12 bits.
// shift left 12 to re-center the bits.
- return ti.isConstant() && (((ti.value()<<32)>>>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<<RAX) & ~(1L<<RDX));
static final long SPILLS = -(1L << MAX_REG);
static final RegMask SPLIT_MASK = new RegMask(WR_BITS | FP_BITS /* | (1L<<FLAGS)*/ | SPILLS, -1L );
@@ -61,7 +63,7 @@ public class x86_64_v2 extends Machine {
public static RegMask XMM0_MASK = new RegMask(XMM0);
// Encoding
- public static int REX = 0x40;
+ public static int REX = 0x40;
public static int REX_W = 0x48;
public static int REX_WR = 0x4C;
public static int REX_WRB= 0x4D;
@@ -189,6 +191,7 @@ public class x86_64_v2 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
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<Relo>[] raligns = new Ary[5];
+ for( Node op : enc._bigCons.keySet() ) {
+ Relo relo = enc._bigCons.get(op);
+ int align = relo._t.alignment();
+ Ary<Relo> relos = raligns[align]==null ? (raligns[align]=new Ary<>( Relo.class)) : raligns[align];
+ relos.add(relo);
+ }
+
HashSet<Type> 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<Relo> 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<<log);
- sb.p(" ");
- }
- } else {
- switch( log ) {
- case 0: sb.hex1(enc.read1(iadr)).fix(9-1,""); break;
- case 1: sb.hex2(enc.read2(iadr)).fix(9-2,""); break;
- case 2: sb.hex4(enc.read4(iadr)).fix(9-4,""); break;
- case 3: sb.hex8(enc.read8(iadr)).p(" "); break;
- }
- iadr += (1<<log);
+ sb.hex2(iadr).p(" ");
+ switch( relo._t ) {
+ case TypeTuple tt -> {
+ for( Type tx : tt._types ) {
+ pN(enc,sb,iadr,align).p(" ");
+ iadr += (1<<align);
}
- relo._t.print(sb).nl();
}
+
+ case TypeStruct ts -> {
+ int sz = ts.size();
+ int log = 1<<align;
+ sz = (sz + (log -1)) & -log; // Round up final padding
+ for( int i=0; i<sz; i++ )
+ sb.hex1(enc.read1(iadr++));
+ }
+
+ // Simple primitive (e.g. larger int, float)
+ default -> {
+ pN(enc,sb,iadr,align).fix(9-(1<<align),"");
+ iadr += (1<<align);
+ }
+ }
+ relo._t.print(sb).nl();
}
}
}
+ return sb;
+ }
+ private static SB pN( Encoding enc, SB sb, int iadr, int log ) {
+ 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;
+ }
return sb;
}
+
private static int print(int iadr, SB sb, CodeGen code, FunNode fun, int cfgidx) {
FunNode old=null;
if( code._encoding!=null ) {
@@ -175,7 +194,7 @@ public abstract class ASMPrinter {
// Encoding
int fatEncoding = 0;
- if( code._encoding != null ) {
+ if( code._encoding != null && code._encoding._opLen!=null ) {
int size = code._encoding._opLen[n._nid];
if( code._asmLittle )
for( int i=0; i<Math.min(size,dopz); i++ )
@@ -224,7 +243,7 @@ public abstract class ASMPrinter {
sb.nl();
// Printing more op bits than fit
- if( isMultiOp != null && code._encoding != null ) {
+ if( isMultiOp != null && code._encoding != null && code._encoding._opLen!=null ) {
// Multiple ops, template style, no RA, no scheduling. Print out
// one-line-per-newline, with encoding bits up front.
int size = code._encoding._opLen[n._nid];
diff --git a/src/main/java/com/seaofnodes/simple/sys.java b/src/main/java/com/seaofnodes/simple/sys.java
new file mode 100644
index 00000000..45106770
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/sys.java
@@ -0,0 +1,25 @@
+package com.seaofnodes.simple;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+
+/** Default included namespaces for Simple.
+
+ You can think of this as {@code #include <stdio.h>} 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/<libc call>.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<len; i++ )
+ dst[i+doff] = src[i+soff];
+ return dst;
+ };
+ };
+ struct io {
+ // Print string to stdout
+ val p = { u8[~] str ->
+ 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<<log_size(); }
+ public int alignment() { return log_size(); } // alignment; for structs, max align of Fields
// ----------------------------------------------------------
// Useful in the debugger, which calls toString everywhere.
diff --git a/src/main/java/com/seaofnodes/simple/type/TypeConAry.java b/src/main/java/com/seaofnodes/simple/type/TypeConAry.java
new file mode 100644
index 00000000..29496af6
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/type/TypeConAry.java
@@ -0,0 +1,69 @@
+package com.seaofnodes.simple.type;
+
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.Utils;
+import java.util.ArrayList;
+import java.io.ByteArrayOutputStream;
+
+/**
+ * Represents a constant array of primitives
+ */
+public class TypeConAry<A> 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<Type> 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<len; i++ ) {
+ min = Math.min(min,at(i));
+ max = Math.max(max,at(i));
+ }
+ return TypeInteger.make(min,max);
+ }
+ public long at(int idx) { throw Utils.TODO(); }
+ public int len() { throw Utils.TODO(); }
+ @Override public int alignment() { return 0; }
+ public void write( ByteArrayOutputStream baos ) { throw Utils.TODO(); }
+
+ @Override boolean eq(Type t) {
+ TypeConAry ary = (TypeConAry)t; // Invariant
+ return _any==ary._any && ary._ary==null;
+ }
+
+ @Override int hash() {
+ assert _ary==null;
+ return _any ? 1024 : 0;
+ }
+
+}
diff --git a/src/main/java/com/seaofnodes/simple/type/TypeConAryB.java b/src/main/java/com/seaofnodes/simple/type/TypeConAryB.java
new file mode 100644
index 00000000..c057c6a5
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/type/TypeConAryB.java
@@ -0,0 +1,21 @@
+package com.seaofnodes.simple.type;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.io.ByteArrayOutputStream;
+
+public class TypeConAryB extends TypeConAry<byte[]> {
+ 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<Type> 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<int[]> {
+ 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<Type> 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<Type> ts) { ts.add(TEST); ts.add(BOT); ts.add(S1); ts.add(S2); ts.add(ARY); }
+ public static void gather(ArrayList<Type> 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<len; i++ )
+ if( !_fields[i].isConstant() )
+ return false;
+ return true;
+ }
+
+ // log_size for a struct is not defined, unless its exactly some power of
+ // 2. *Total size* is well-defined, and is available in the offsets.
+ @Override public int log_size() { throw Utils.TODO(); }
+ @Override public int size() { return offset(_fields.length); }
+ @Override public int alignment() {
+ int align = 0;
+ for( Field f : _fields )
+ align = Math.max(align, (f._fname!="[]" || _con==TypeConAry.BOT ? f._type : _con).alignment());
+ return align;
+ }
+
@Override
boolean eq(Type t) {
TypeStruct ts = (TypeStruct)t; // Invariant
- if( !_name.equals(ts._name) )
+ if( !_name.equals(ts._name) || _con!=ts._con )
return false;
if( _fields == ts._fields ) return true;
if( _fields==null || ts._fields==null ) return false;
@@ -177,17 +234,15 @@ public class TypeStruct extends Type {
if( _fields != null )
for( Field f : _fields )
hash = Utils.rot(hash,13) ^ f.hashCode();
- return Utils.fold(hash);
+ return Utils.fold(hash ^ _con.hashCode());
}
@Override
public SB print(SB sb) {
- if( _fields == null ) return sb.p(_name); // Forward reference struct, just print the name
- if( isAry() ) {
- if( !isFinal() ) sb.p("!");
- return sb.p(_name); // Skip printing generic array fields
- }
+ if( _con!=TypeConAry.BOT ) return sb.p(_con.str());
sb.p(_name);
+ if( _fields == null || isAry() ) // Forward reference struct, just print the name
+ return sb.p(isFinal() ? "" : "!");
sb.p(" {");
for( Field f : _fields )
f._type.print(sb).p(f._final ? " " : " !").p(f._fname).p("; ");
@@ -195,10 +250,10 @@ public class TypeStruct extends Type {
}
@Override public SB gprint( SB sb ) { return sb.p(_name); }
- @Override public String str() { return _name; }
+ @Override public String str() { return _con==TypeConAry.BOT ? _name : _con.str(); }
- public boolean isAry() { return _fields.length==2 && _fields[1]._fname.equals("[]"); }
+ public boolean isAry() { return _fields.length>=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<flen; i++ )
+ cnts[_fields[i]._type.log_size()]++; // Log size is 0(byte), 1(i16/u16), 2(i32/f32), 3(i64/dbl)
int off = 0, idx = 0; // Base common struct fields go here, e.g. Mark/Klass
// Compute offsets to the start of each power-of-2 aligned fields.
int[] offs = new int[4];
@@ -239,14 +292,38 @@ public class TypeStruct extends Type {
// Assign offsets to all fields.
// Really a hidden radix sort.
_offs = new int[_fields.length+1];
- for( Field f : _fields ) {
- int log = f._type.log_size();
+ for( int i=0; i<flen; i++ ) {
+ int log = _fields[i]._type.log_size();
_offs[idx++] = offs[log]; // Field offset
offs[log] += 1<<log; // Next field offset at same alignment
cnts[log]--; // Count down, should be all zero at end
}
- _offs[_fields.length] = (off+7)& ~7; // Round out to max alignment
+
+ // Array bits
+ if( isAry() ) {
+ // Pad out to the element alignment
+ int align = (_fields[flen]._fname!="[]" || _con==TypeConAry.BOT ? _fields[flen]._type : _con).alignment();
+ off = (off + ((1<<align)-1)) & -(1<<align);
+ _offs[flen] = off;
+ if( _con!=TypeConAry.BOT )
+ _offs[flen+1] = off+_con.len();
+ } else
+ _offs[flen] = off; // Max struct size, no trailing padding
return _offs;
}
+ // Field order with increasing offset
+ public int[] layout() {
+ offset(0);
+ int len = _offs.length;
+ int[] is = new int[len];
+ for( int i=0; i<len; i++ ) is[i] = i;
+ // Stoopid hand rolled bubble sort
+ for( int i=0; i<len; i++ )
+ for( int j=i+1; j<len; j++ )
+ if( _offs[is[j]] < _offs[is[i]] )
+ { int tmp = is[i]; is[i] = is[j]; is[j] = tmp; }
+ return is;
+ }
+
}
diff --git a/src/main/java/com/seaofnodes/simple/type/TypeTuple.java b/src/main/java/com/seaofnodes/simple/type/TypeTuple.java
index d3b19b52..bc57871a 100644
--- a/src/main/java/com/seaofnodes/simple/type/TypeTuple.java
+++ b/src/main/java/com/seaofnodes/simple/type/TypeTuple.java
@@ -1,6 +1,7 @@
package com.seaofnodes.simple.type;
import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.Utils;
import java.util.ArrayList;
public class TypeTuple extends Type {
@@ -48,11 +49,10 @@ public class TypeTuple extends Type {
return make(ts);
}
- @Override
- public Type glb() {
+ @Override public Type glb(boolean mem) {
Type[] ts = new Type[_types.length];
for( int i=0; i<_types.length; i++ )
- ts[i] = _types[i].glb();
+ ts[i] = _types[i].glb(mem);
return make(ts);
}
@@ -63,12 +63,13 @@ public class TypeTuple extends Type {
return true;
}
- @Override public int log_size() {
+ @Override public int log_size() { throw Utils.TODO(); }
+ @Override public int alignment() {
assert isConstant();
- int log_size = 0;
+ int align = 0;
for( Type t : _types )
- log_size = Math.max(log_size,t.log_size());
- return log_size;
+ align = Math.max(align,t.alignment());
+ return align;
}
public Type ret() { assert _types.length==3; return _types[2]; }