summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-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
-rw-r--r--src/main/smp/sys.smp57
-rw-r--r--src/test/java/com/seaofnodes/simple/BrainFuckTest.java2
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter01Test.java6
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter09Test.java9
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter10Test.java2
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter13Test.java7
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter15Test.java6
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter16Test.java4
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter17Test.java8
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter18Test.java17
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter19Test.java7
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter20Test.java7
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter21Test.java8
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter22Test.java240
-rw-r--r--src/test/java/com/seaofnodes/simple/Eval2.java22
-rw-r--r--src/test/java/com/seaofnodes/simple/EvalArm64.java1156
-rw-r--r--src/test/java/com/seaofnodes/simple/EvalRisc5.java28
-rw-r--r--src/test/java/com/seaofnodes/simple/Simple.java246
-rw-r--r--src/test/java/com/seaofnodes/simple/TestC.java97
-rw-r--r--src/test/java/com/seaofnodes/simple/TypeTest.java16
-rw-r--r--src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java2
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/echo.smp3
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/helloWorld.smp3
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/jig.smp4
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/person.smp2
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/riscv_load.smp4
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/sext_str_1.smp4
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/sext_str_fold_away.smp5
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/sext_str_not_fold_away.smp4
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/sext_str_not_fold_away_2.smp4
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/store_peep.smp3
123 files changed, 2688 insertions, 1560 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 c0c2e606..eadb984a 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);
@@ -239,7 +239,7 @@ public abstract class Node implements Cloneable {
// Shortcut for "popping" until n nodes. A "pop" is basically a
// setDef(last,null) followed by lowering the nIns() count.
- void popUntil(int n) {
+ public void popUntil(int n) {
unlock();
while( nIns() > n ) {
Node old_def = _inputs.pop();
@@ -255,9 +255,9 @@ public abstract class Node implements Cloneable {
* code elimination.
*/
public void kill( ) {
+ assert isUnused(); // Has no uses, so it is dead
unlock();
moveDepsToWorklist();
- assert isUnused(); // Has no uses, so it is dead
_type=null; // Flag as dead
while( nIns()>0 ) { // Set all inputs to null, recursively killing unused Nodes
Node old_def = _inputs.removeLast();
@@ -295,6 +295,10 @@ public abstract class Node implements Cloneable {
if( unkeep().isUnused() )
kill();
}
+ public void isKill() {
+ if( isUnused() )
+ kill();
+ }
// Replace self with nnn in the graph, making 'this' go dead
diff --git a/src/main/java/com/seaofnodes/simple/node/OrNode.java b/src/main/java/com/seaofnodes/simple/node/OrNode.java
index 9b03dc5d..ac0d8454 100644
--- a/src/main/java/com/seaofnodes/simple/node/OrNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/OrNode.java
@@ -4,26 +4,16 @@ import com.seaofnodes.simple.Parser;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeInteger;
-import java.util.BitSet;
-
-public class OrNode extends LogicalNode {
+public class OrNode extends ArithNode {
public OrNode(Parser.Lexer loc, Node lhs, Node rhs) { super(loc, lhs, rhs); }
@Override public String label() { return "Or"; }
@Override public String op() { return "|"; }
-
@Override public String glabel() { return "|"; }
- @Override
- public Type compute() {
- Type t1 = in(1)._type, t2 = in(2)._type;
- if( t1.isHigh() || t2.isHigh() )
- return TypeInteger.TOP;
- if( t1 instanceof TypeInteger i0 &&
- t2 instanceof TypeInteger i1 ) {
- if( i0.isConstant() && i1.isConstant() )
- return TypeInteger.constant(i0.value()|i1.value());
- }
+
+ @Override long doOp( long x, long y ) { return x | y; }
+ @Override TypeInteger doOp(TypeInteger x, TypeInteger y) {
return TypeInteger.BOT;
}
@@ -49,7 +39,7 @@ public class OrNode extends LogicalNode {
Node phicon = AddNode.phiCon(this,true);
if( phicon!=null ) return phicon;
- return null;
+ return super.idealize();
}
@Override Node copy(Node lhs, Node rhs) { return new OrNode(_loc,lhs,rhs); }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/ParmNode.java b/src/main/java/com/seaofnodes/simple/node/ParmNode.java
index 9e11a129..32e41f54 100644
--- a/src/main/java/com/seaofnodes/simple/node/ParmNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/ParmNode.java
@@ -45,4 +45,8 @@ public class ParmNode extends PhiNode {
// Always in-progress until we run out of unknown callers
@Override public boolean inProgress() { return fun().inProgress(); }
+
+ @Override public boolean eq( Node n ) {
+ return ((ParmNode)n)._idx==_idx && super.eq(n);
+ }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/PhiNode.java b/src/main/java/com/seaofnodes/simple/node/PhiNode.java
index 0579ecd0..971b52ae 100644
--- a/src/main/java/com/seaofnodes/simple/node/PhiNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/PhiNode.java
@@ -54,7 +54,7 @@ public class PhiNode extends Node {
// During parsing Phis have to be computed type pessimistically.
if( r.inProgress() ) return _declaredType;
// Set type to local top of the starting type
- Type t = _declaredType.glb().dual();//Type.TOP;
+ Type t = _declaredType.glb(false).dual();//Type.TOP;
for (int i = 1; i < nIns(); i++)
// If the region's control input is live, add this as a dependency
// to the control because we can be peeped should it become dead.
@@ -121,12 +121,17 @@ public class PhiNode extends Node {
Node op = in(i);
if( in(1).getClass() != op.getClass() || op.in(0)!=null || in(1).nIns() != op.nIns() )
return false; // Wrong class or CFG bound or mismatched inputs
+ if( in(1) instanceof MemOpNode mem && mem._alias != ((MemOpNode)op)._alias )
+ return false;
if( op.nOuts() > 1 ) { // Too many users, but addDep in case lose users
for( Node out : op._outputs )
if( out!=null && out!=this )
addDep(out);
return false;
}
+ for( int j=1; j<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]; }
diff --git a/src/main/smp/sys.smp b/src/main/smp/sys.smp
new file mode 100644
index 00000000..ede08d63
--- /dev/null
+++ b/src/main/smp/sys.smp
@@ -0,0 +1,57 @@
+// -*- 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/test/java/com/seaofnodes/simple/BrainFuckTest.java b/src/test/java/com/seaofnodes/simple/BrainFuckTest.java
index 2733620e..1e1a1dc3 100644
--- a/src/test/java/com/seaofnodes/simple/BrainFuckTest.java
+++ b/src/test/java/com/seaofnodes/simple/BrainFuckTest.java
@@ -16,7 +16,7 @@ public class BrainFuckTest {
String brain_fuck = "Hello World!\n";
TestC.run("brain_fuck", brain_fuck, 40);
- EvalRisc5 R5 = TestRisc5.build("brain_fuck", 0, 44, false);
+ EvalRisc5 R5 = TestRisc5.build("brain_fuck", 0, 28, false);
int trap = R5.step(100000);
assertEquals(0,trap);
int ptr = (int)R5.regs[riscv.A0];
diff --git a/src/test/java/com/seaofnodes/simple/Chapter01Test.java b/src/test/java/com/seaofnodes/simple/Chapter01Test.java
index cddbdfcf..ac9adacc 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter01Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter01Test.java
@@ -25,9 +25,9 @@ public class Chapter01Test {
public void testZero() {
CodeGen code = new CodeGen("return 0;");
code.parse();
- for( Node use : code._start._outputs )
- if( use instanceof ConstantNode con && con._type instanceof TypeInteger )
- assertEquals(TypeInteger.constant(0),con._type);
+ Node use = code.expr();
+ if( use instanceof ConstantNode con && con._type instanceof TypeInteger )
+ assertEquals(TypeInteger.constant(0),con._type);
}
@Test
diff --git a/src/test/java/com/seaofnodes/simple/Chapter09Test.java b/src/test/java/com/seaofnodes/simple/Chapter09Test.java
index 401f346f..d4d02aa9 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter09Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter09Test.java
@@ -128,14 +128,14 @@ while(v1+arg) {
}
""");
code.parse().opto();
- assertEquals("return 0;", code.print());
+ assertEquals("return Top;", code.print());
}
@Test
public void testWhile0() {
- CodeGen code = new CodeGen("while(0) continue; if(0) arg=0;");
+ CodeGen code = new CodeGen("while(0) continue; if(0) arg=0; return arg;");
code.parse().opto();
- assertEquals("return 0;", code.print());
+ assertEquals("return arg;", code.print());
}
@Test
@@ -145,6 +145,7 @@ if(0) while(0) {
int arg=arg;
while(0) {}
}
+return 0;
""");
code.parse().opto();
assertEquals("return 0;", code.print());
@@ -255,7 +256,7 @@ while(0==1) while(v0)
v0=1+v0;
""");
code.parse().opto();
- assertEquals("return 0;", code.print());
+ assertEquals("Stop[ ]", code.print());
}
@Test
diff --git a/src/test/java/com/seaofnodes/simple/Chapter10Test.java b/src/test/java/com/seaofnodes/simple/Chapter10Test.java
index 1253e4a6..712704db 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter10Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter10Test.java
@@ -304,6 +304,7 @@ if(0) {
v0=v2;
}
}
+return 0;
""");
code.parse().opto();
assertEquals("return 0;", code.print());
@@ -378,6 +379,7 @@ while(0) {}
}
} }
}
+return 0;
""");
code.parse().opto();
assertEquals("return 0;", code.print());
diff --git a/src/test/java/com/seaofnodes/simple/Chapter13Test.java b/src/test/java/com/seaofnodes/simple/Chapter13Test.java
index 6bf45a22..a19f85e3 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter13Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter13Test.java
@@ -130,15 +130,15 @@ return n.i;
@Test
public void testNullRef4() {
- CodeGen code = new CodeGen("-null-5/null-5;");
+ CodeGen code = new CodeGen("return -null-5/null-5;");
try { code.parse().opto().typeCheck(); fail(); }
- catch( Exception e ) { assertEquals("Cannot 'Add' null",e.getMessage()); }
+ catch( Exception e ) { assertEquals("Cannot '+' null",e.getMessage()); }
}
@Test public void testNullRef5() {
CodeGen code = new CodeGen("return null+42;");
try { code.parse().opto().typeCheck(); fail(); }
- catch( Exception e ) { assertEquals("Cannot 'Add' null",e.getMessage()); }
+ catch( Exception e ) { assertEquals("Cannot '+' null",e.getMessage()); }
}
@Test
@@ -146,6 +146,7 @@ return n.i;
CodeGen code = new CodeGen(
"""
struct S{};
+return 0;
""");
code.parse().opto();
assertEquals("return 0;", code.print());
diff --git a/src/test/java/com/seaofnodes/simple/Chapter15Test.java b/src/test/java/com/seaofnodes/simple/Chapter15Test.java
index 8b138b46..a98e62d8 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter15Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter15Test.java
@@ -253,10 +253,10 @@ return new flt;
public void testBad1() {
CodeGen code = new CodeGen(
"""
-int is = new int[2];
+flt is = new int[2];
""");
try { code.parse().opto(); fail(); }
- catch( Exception e ) { assertEquals("Type *[int] is not of declared type int",e.getMessage()); }
+ catch( Exception e ) { assertEquals("Type *[int] is not of declared type flt",e.getMessage()); }
}
@Test
@@ -323,6 +323,7 @@ while(0) {}
u32 v7=0;
int v8=0;
while(0<--1>>>---(v7*0==v8)) {}
+return 0;
""");
code.parse().opto();
assertEquals("return 0;", code.print());
@@ -347,6 +348,7 @@ else {
}
}
}
+return 0;
""");
code.parse().opto();
assertEquals("return 0;", code.print());
diff --git a/src/test/java/com/seaofnodes/simple/Chapter16Test.java b/src/test/java/com/seaofnodes/simple/Chapter16Test.java
index 930624e5..58bb4148 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter16Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter16Test.java
@@ -128,7 +128,7 @@ p.x++;
return p;
""");
try { code.parse().opto().typeCheck(); fail(); }
- catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); }
+ catch( Exception e ) { assertEquals("Cannot reassign final 'x'",e.getMessage()); }
}
@Test
@@ -140,7 +140,7 @@ p.x++;
return p;
""");
try { code.parse().opto().typeCheck(); fail(); }
- catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); }
+ catch( Exception e ) { assertEquals("Cannot reassign final 'x'",e.getMessage()); }
}
@Test
diff --git a/src/test/java/com/seaofnodes/simple/Chapter17Test.java b/src/test/java/com/seaofnodes/simple/Chapter17Test.java
index 78515083..bdeb645c 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter17Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter17Test.java
@@ -172,13 +172,13 @@ return s.x;
@Test public void testVar6() {
CodeGen code = new CodeGen("struct S{int x;}; S s=new S; s.x++; return s.x; // Error initializer so x is immutable ");
try { code.parse().opto().typeCheck(); fail(); }
- catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); }
+ catch( Exception e ) { assertEquals("Cannot reassign final 'x'",e.getMessage()); }
}
@Test public void testVar7() {
CodeGen code = new CodeGen("struct S{int x;}; S s=new S{x=3;}; s.x++; return s.x; // Error initializer so x is immutable ");
try { code.parse().opto().typeCheck(); fail(); }
- catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); }
+ catch( Exception e ) { assertEquals("Cannot reassign final 'x'",e.getMessage()); }
}
@Test public void testVar8() {
@@ -198,7 +198,7 @@ return s.x;
@Test public void testVar10() {
CodeGen code = new CodeGen("struct S{int x;}; val s=new S; s.x++; return s.x; // Error, has val so x is immutable ");
try { code.parse().opto().typeCheck(); fail(); }
- catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); }
+ catch( Exception e ) { assertEquals("Cannot reassign final 'x'",e.getMessage()); }
}
@Test public void testVar11() {
@@ -216,7 +216,7 @@ val xfoo = foo; // Throw away mutability
xfoo.bar.x++; // Error, cannot mutate through xfoo
""");
try { code.parse().opto().typeCheck(); fail(); }
- catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); }
+ catch( Exception e ) { assertEquals("Cannot reassign final 'x'",e.getMessage()); }
}
@Test public void testVar12() {
diff --git a/src/test/java/com/seaofnodes/simple/Chapter18Test.java b/src/test/java/com/seaofnodes/simple/Chapter18Test.java
index c0e28412..00d41a23 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter18Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter18Test.java
@@ -33,7 +33,7 @@ while(arg--) {
}
return a;
""");
- code.parse().opto().typeCheck().GCM();
+ code.parse().opto().typeCheck();
assertEquals("return Phi(Loop,1,Phi(Loop,2,Phi_a));", code._stop.toString());
assertEquals("1", Eval2.eval(code, 0));
assertEquals("2", Eval2.eval(code, 1));
@@ -78,7 +78,7 @@ var sq = { int x ->
return sq(arg)+sq(3);
""");
code.driver(Phase.LocalSched);
- assertEquals("Stop[ return (#2+#2); return (Parm_x(sq,int)*x); ]", code._stop.toString());
+ assertEquals("Stop[ return (Parm_x(sq,int)*x); return (#2+#2); ]", code._stop.toString());
assertEquals("13", Eval2.eval(code, 2));
}
@@ -116,7 +116,7 @@ var fcn = arg ? { int x -> x*x; } : { int x -> x+x; };
return fcn(3);
""");
code.parse().opto();
- assertEquals("Stop[ return #2; return (Parm_x($fun1,int,3)*x); return (Parm_x($fun2,int,3)<<1); ]", code._stop.toString());
+ assertEquals("Stop[ return (Parm_x($fun2,int,3)<<1); return (Parm_x($fun1,int,3)*x); return #2; ]", code._stop.toString());
assertEquals("6", Eval2.eval(code, 0));
assertEquals("9", Eval2.eval(code, 1));
}
@@ -126,7 +126,7 @@ return fcn(3);
public void testFcn5() {
CodeGen code = new CodeGen("val fact = { int x -> x <= 1 ? 1 : x*fact(x-1); }; return fact(arg);");
code.parse().opto().typeCheck();
- assertEquals("Stop[ return #2; return Phi(Region,1,(Parm_x(fact,int,arg,(x-1))*#2)); ]", code._stop.toString());
+ assertEquals("Stop[ return Phi(Region,1,(Parm_x(fact,int,arg,(x-1))*#2)); return #2; ]", code._stop.toString());
assertEquals( "1", Eval2.eval(code, 0));
assertEquals( "1", Eval2.eval(code, 1));
assertEquals( "2", Eval2.eval(code, 2));
@@ -158,7 +158,7 @@ val g = {->2;};
return 2;
""");
code.parse().opto().typeCheck().GCM();
- assertEquals("Stop[ return 1; return 1; return 2; ]", code._stop.toString());
+ assertEquals("Stop[ return 2; return 1; return 1; ]", code._stop.toString());
assertEquals("1", Eval2.eval(code, 0));
}
@@ -176,7 +176,7 @@ for(;;) {
}
""");
code.driver(Phase.LocalSched);
- assertEquals("Stop[ return #2; return Parm_i(x,int); ]", code._stop.toString());
+ assertEquals("Stop[ return Parm_i(x,int); return #2; ]", code._stop.toString());
assertEquals("3", Eval2.eval(code, 0));
}
@@ -214,7 +214,7 @@ val fcn = { Person?[] ps, int x ->
var ps = new Person?[2];
ps[0] = new Person;
ps[1] = new Person;
-fcn(ps,1);
+return fcn(ps,1);
""");
code.driver(Phase.LocalSched);
assertEquals("return 0;", code._stop.toString());
@@ -249,6 +249,7 @@ for(;;) {
if (i2i(2) == arg) break;
i2i = null;
}
+return 0;
""");
try { code.parse().opto().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Might be null calling { int -> int #1}?",e.getMessage()); }
@@ -347,7 +348,7 @@ if (i2i) return i2i(arg);
return f2f(o)(1);
""");
code.driver(Phase.LocalSched);
- assertEquals("Stop[ return Phi(Region,#2,#2); return Parm_i(o,int); ]", code._stop.toString());
+ assertEquals("Stop[ return Parm_i(i2i.o,int); return Phi(Region,#2,#2); ]", code._stop.toString());
assertEquals("1", Eval2.eval(code, 2));
}
diff --git a/src/test/java/com/seaofnodes/simple/Chapter19Test.java b/src/test/java/com/seaofnodes/simple/Chapter19Test.java
index bce75c81..b8ef22d7 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter19Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter19Test.java
@@ -25,7 +25,7 @@ return 0;
public void testString() throws IOException {
String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/stringHash.smp"));
CodeGen code = new CodeGen(src).parse().opto().typeCheck().GCM().localSched();
- assertEquals("Stop[ return Phi(Region,._hashCode,Phi(Region,123456789,Phi(Loop,0,(.[]+((Phi_hash<<5)-Phi_hash))))); return Phi(Region,1,0,0,1); ]", code._stop.toString());
+ assertEquals("Stop[ return Phi(Region,1,0,0,1); return Phi(Region,._hashCode,Phi(Region,123456789,Phi(Loop,0,(.[]+((Phi_hash<<5)-Phi_hash))))); ]", code._stop.toString());
//assertEquals("-4898613127354160978", Eval2.eval(code, 2));
}
@@ -271,6 +271,7 @@ for( int i=0; i<A#; i++ )
A[i] = i;
for( int i=0; i<A#; i++ )
B[i] += A[i];
+return 0;
""");
code.driver(Phase.LocalSched,"x86_64_v2", "SystemV");
assertEquals("return 0;", code.print());
@@ -315,7 +316,7 @@ val fcn = arg ? { int x -> x*x; } : { int x -> x+x; };
return fcn(2)*10 + fcn(3);
""");
code.driver(Phase.LocalSched,"x86_64_v2", "SystemV");
- assertEquals("Stop[ return (add,#2,(muli,#2)); return (mul,Parm_x($fun1,int),x); return (shli,Parm_x($fun2,int)); ]", code.print());
+ assertEquals("Stop[ return (shli,Parm_x($fun2,int)); return (mul,Parm_x($fun1,int),x); return (add,#2,(muli,#2)); ]", code.print());
}
@Test
@@ -326,6 +327,6 @@ val sq = { int x -> x*x; };
return sq(arg) + sq(3);
""");
code.driver(Phase.LocalSched,"x86_64_v2", "SystemV");
- assertEquals("Stop[ return (add,#2,#2); return (mul,Parm_x(sq,int),x); ]", code.print());
+ assertEquals("Stop[ return (mul,Parm_x(sq,int),x); return (add,#2,#2); ]", code.print());
}
}
diff --git a/src/test/java/com/seaofnodes/simple/Chapter20Test.java b/src/test/java/com/seaofnodes/simple/Chapter20Test.java
index 5b33d1e7..f442ae1a 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter20Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter20Test.java
@@ -63,9 +63,10 @@ val sqrt = { int x ->
guess = next;
}
};
-return sqrt(arg) + sqrt(arg+2);
+int cast_int = arg+2;
+return sqrt(arg) + sqrt(cast_int);
""";
- testCPU(src,"x86_64_v2", "Win64" ,23,null);
+ testCPU(src,"x86_64_v2", "Win64" ,55,null);
testCPU(src,"riscv" , "SystemV",19,null);
testCPU(src,"arm" , "SystemV",19,null);
}
@@ -103,7 +104,7 @@ return sqrt(arg) + sqrt(arg+2);
String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/stringHash.smp"));
testCPU(src,"x86_64_v2", "SystemV", 9,null);
testCPU(src,"riscv" , "SystemV", 5,null);
- testCPU(src,"arm" , "SystemV", 6,null);
+ testCPU(src,"arm" , "SystemV", 3,null);
}
@Test
diff --git a/src/test/java/com/seaofnodes/simple/Chapter21Test.java b/src/test/java/com/seaofnodes/simple/Chapter21Test.java
index d8a5cebc..77d1e337 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter21Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter21Test.java
@@ -39,7 +39,7 @@ public class Chapter21Test {
}
@Test public void testInfinite() {
- String src = "struct S { int i; }; S !s = new S; while(1) s.i++;";
+ String src = "struct S { int i; }; S !s = new S; while(1) s.i++; return s.i;";
testCPU(src,"x86_64_v2", "SystemV",0,"return Top;");
testCPU(src,"riscv" , "SystemV",2,"return Top;");
testCPU(src,"arm" , "SystemV",2,"return Top;");
@@ -66,7 +66,7 @@ public class Chapter21Test {
String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/stringHash.smp"));
testCPU(src,"x86_64_v2", "SystemV", 9,null);
testCPU(src,"riscv" , "SystemV", 5,null);
- testCPU(src,"arm" , "SystemV", 6,null);
+ testCPU(src,"arm" , "SystemV", 3,null);
}
@Test public void testStringExport() throws IOException {
@@ -150,14 +150,14 @@ public class Chapter21Test {
String fib = "[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]";
TestC.run("fib", fib, 24);
- EvalRisc5 R5 = TestRisc5.build("fib", 9, 16, false);
+ EvalRisc5 R5 = TestRisc5.build("fib", 9, 17, false);
int trap = R5.step(100);
assertEquals(0,trap);
// Return register A0 holds fib(8)==55
assertEquals(55,R5.regs[riscv.A0]);
// arm
- EvalArm64 A5 = TestArm64.build("fib", 9, 16, false);
+ EvalArm64 A5 = TestArm64.build("fib", 9, 17, false);
int trap_arm = A5.step(100);
assertEquals(0,trap_arm);
// Return register X0 holds fib(8)==55
diff --git a/src/test/java/com/seaofnodes/simple/Chapter22Test.java b/src/test/java/com/seaofnodes/simple/Chapter22Test.java
new file mode 100644
index 00000000..394ba336
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/Chapter22Test.java
@@ -0,0 +1,240 @@
+package com.seaofnodes.simple;
+
+import com.seaofnodes.simple.codegen.CodeGen;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+import com.seaofnodes.simple.node.cpus.arm.arm;
+import com.seaofnodes.simple.node.cpus.riscv.riscv;
+import org.junit.Ignore;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+public class Chapter22Test {
+
+ @Test @Ignore
+ public void testJig() throws IOException {
+ String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/jig.smp"));
+ //String src = Files.readString(Path.of("docs/examples/BubbleSort.smp"));
+ testCPU(src,"x86_64_v2", "Win64" ,-1,null);
+ testCPU(src,"riscv" , "SystemV",-1,null);
+ testCPU(src,"arm" , "SystemV",-1,null);
+ }
+
+ static CodeGen testCPU( String src, String cpu, String os, int spills, String stop ) {
+ CodeGen code = new CodeGen(src).driver(CodeGen.Phase.Encoding,cpu,os);
+ int delta = spills>>3;
+ if( delta==0 ) delta = 1;
+ if( spills != -1 )
+ assertEquals("Expect spills:",spills,code._regAlloc._spillScaled,delta);
+ if( stop != null )
+ assertEquals(stop, code._stop.toString());
+ return code;
+ }
+
+
+ static int testCPUSize( String src, String cpu, String os, int spills, String stop ) {
+ return testCPU(src,cpu,os,spills,stop)._encoding._bits.size();
+ }
+
+ // Should not fold away
+ @Test public void testSextFail() throws IOException {
+ String src = """
+struct Person { i32 age;};
+Person !p = new Person;
+p.age = (arg<<17)>>17;
+return 0;
+""";
+ assertEquals(46, testCPUSize(src, "x86_64_v2","Win64",2,"return 0;"));
+ assertEquals(60, testCPUSize(src, "riscv","SystemV",4,"return 0;"));
+ assertEquals(56, testCPUSize(src, "arm","SystemV",4,"return 0;"));
+
+ // do assertEquals here
+ EvalRisc5 R5 = TestRisc5.build("sext_str_not_fold_away", 0, 4, false);
+ int trap = R5.step(100);
+ assertEquals(0,trap);
+
+ EvalArm64 A5 = TestArm64.build("sext_str_not_fold_away", 0, 4, false);
+ trap = A5.step(100);
+ assertEquals(0,trap);
+
+ }
+
+ // Should not fold away
+ @Test public void testSextFail2() throws IOException {
+ String src = """
+
+ struct Person { i32 age;};
+ Person !p = new Person;
+ p.age = (arg<<48)>>48;
+ return 0;
+ """;
+
+ EvalRisc5 R5 = TestRisc5.build("sext_str_not_fold_away_2", 0, 4, false);
+ int trap = R5.step(100);
+ assertEquals(0,trap);
+
+ EvalArm64 A5 = TestArm64.build("sext_str_not_fold_away_2", 0, 4, false);
+ int trap_arm = A5.step(100);
+ assertEquals(0,trap_arm);
+
+ assertEquals(46, testCPUSize(src, "x86_64_v2","Win64",2,"return 0;"));
+ assertEquals(60, testCPUSize(src, "riscv","SystemV",4,"return 0;"));
+ assertEquals(56, testCPUSize(src, "arm","SystemV",4,"return 0;"));
+
+ }
+
+ // Should fold away
+ @Test public void testSextSuccess() throws IOException {
+ String src = """
+// Should fold away sign extend
+struct Person { i8 age;};
+Person !p = new Person;
+p.age = (arg<<48)>>48;
+return 0;
+ """;
+
+ EvalRisc5 R5 = TestRisc5.build("sext_str_fold_away", 0, 5, false);
+ int trap = R5.step(100);
+ assertEquals(0,trap);
+
+ EvalArm64 A5 = TestArm64.build("sext_str_fold_away", 0, 5, false);
+ int trap_arm = A5.step(100);
+ assertEquals(0,trap_arm);
+
+ assertEquals(41, testCPUSize(src, "x86_64_v2","Win64",2,"return 0;"));
+ assertEquals(56, testCPUSize(src, "riscv","SystemV",5,"return 0;"));
+ assertEquals(52, testCPUSize(src, "arm","SystemV",5,"return 0;"));
+ // do assertEquals here
+ }
+
+
+ // Int now is changed to 4 bytes.
+ @Test public void testPerson() throws IOException {
+ String person = "6\n";
+ TestC.run("person", person, 0);
+
+ // Memory layout starting at PS:
+ int ps = 1<<16; // Person array pointer starts at heap start
+ // Person[3] = { len,pad,P0,P1,P2 }; // sizeof = 4*8
+ // P0 = { age } // sizeof=8
+ int p0 = ps+4*8+0*8;
+ // P1 = { age } // sizeof=8
+ int p1 = ps+4*8+1*8;
+ // P2 = { age } // sizeof=8
+ int p2 = ps+4*8+2*8;
+ EvalRisc5 R5 = TestRisc5.build("person", ps, 0, false);
+ R5.regs[riscv.A1] = 1; // Index 1
+ R5.st8(ps,3); // Length
+ R5.st8(ps+1*8,p0);
+ R5.st8(ps+2*8,p1);
+ R5.st8(ps+3*8,p2);
+ R5.st8(p0, 5); // age= 5
+ R5.st8(p1,17); // age=17
+ R5.st8(p2,60); // age=60
+
+ int trap = R5.step(100);
+ assertEquals(0,trap);
+ assertEquals( 5+0,R5.ld8(p0));
+ assertEquals(17+1,R5.ld8(p1));
+ assertEquals(60+0,R5.ld8(p2));
+
+ EvalArm64 A5 = TestArm64.build("person", ps, 0, false);
+ A5.regs[arm.X1] = 1; // Index 1
+ A5.st8(ps, 3);
+ A5.st8(ps+1*8,p0);
+ A5.st8(ps+2*8,p1);
+ A5.st8(ps+3*8,p2);
+ A5.st8(p0, 5); // age= 5
+ A5.st8(p1,17); // age=17
+ A5.st8(p2,60); // age=60
+
+ int trap_arm = A5.step(100);
+ assertEquals(0,trap_arm);
+ assertEquals( 5+0, A5.ld8(p0));
+ assertEquals(17+1, A5.ld8(p1));
+ assertEquals(60+0, A5.ld8(p2));
+ }
+
+ @Test
+ public void testCoRecur() {
+ String src = """
+struct A { B? b; C? c; i64 ax; val az = x*2; };
+struct B { A? a; C? c; f32 bx; val bz = x*3; };
+struct C { A? a; B? b; f64 cx; val cz = x*x; };
+A !aa = new A{ ax=17; };
+B !bb = new B{ bx=3.14; a = aa; };
+C !cc = new C{ cx=2.73; a = aa; b = bb; };
+aa.b = bb;
+aa.c = cc;
+bb.c = cc;
+val x = 5; // aa.az; // Error to self-define forward ref
+return cc.cz;
+""";
+ CodeGen code = new CodeGen(src).parse().opto().typeCheck();
+ assertEquals("return 25;", code._stop.toString());
+ assertEquals("25", Eval2.eval(code, 0));
+ }
+
+ @Test
+ public void testHelloWorld() throws IOException {
+ String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/helloWorld.smp"));
+ TestC.run(src,TestC.CALL_CONVENTION,null,null,"build/objs/helloWorld","","Hello, World!",0);
+
+ // Evaluate on RISC5 emulator
+ EvalRisc5 R5 = TestRisc5.build("helloWorld", 0, 2, false);
+ int trap = R5.step(100);
+ assertEquals(0,trap);
+ assertEquals(0,R5.regs[riscv.A0]);
+ assertEquals("Hello, World!",R5._stdout.toString());
+
+ // Evaluate on ARM emulator
+ EvalArm64 arm = TestArm64.build("helloWorld", 0, 2, false);
+ trap = arm.step(100);
+ assertEquals(0,trap);
+ assertEquals(0,arm.regs[0]);
+ assertEquals("Hello, World!",arm._stdout.toString());
+ }
+
+ @Test
+ public void testFinalArray() {
+ String src = """
+int N=4;
+i32[] !is = new i32[N];
+for( int i=0; i<N; i++ )
+ is[i] = i*i;
+val sum = { i32[~] is -> // final array
+ int sum=0;
+ for( int i=0; i<is#; i++ )
+ sum += is[i];
+ return sum;
+};
+return sum(is);
+""";
+ CodeGen code = new CodeGen(src).parse().opto().typeCheck();
+ assertEquals("return Phi(Loop,0,(Phi_sum+.[]));", code._stop.toString());
+ assertEquals("14", Eval2.eval(code, 0));
+ }
+
+
+ @Test @Ignore
+ public void testEcho() throws IOException {
+ String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/echo.smp"));
+ TestC.run(src,TestC.CALL_CONVENTION,null,null,"build/objs/echo","","",0);
+
+ // Evaluate on RISC5 emulator
+ EvalRisc5 R5 = TestRisc5.build("echo", 0, 2, false);
+ int trap = R5.step(100);
+ assertEquals(0,trap);
+ assertEquals(0,R5.regs[riscv.A0]);
+
+ // Evaluate on ARM emulator
+ EvalArm64 arm = TestArm64.build("echo", 0, 2, false);
+ trap = arm.step(100);
+ assertEquals(0,trap);
+ assertEquals(0,arm.regs[0]);
+ }
+
+
+}
diff --git a/src/test/java/com/seaofnodes/simple/Eval2.java b/src/test/java/com/seaofnodes/simple/Eval2.java
index ecea97dd..86f0e9db 100644
--- a/src/test/java/com/seaofnodes/simple/Eval2.java
+++ b/src/test/java/com/seaofnodes/simple/Eval2.java
@@ -114,11 +114,10 @@ public abstract class Eval2 {
public static String eval( CodeGen code, long arg ) { return eval(code,arg,1000); }
public static String eval( CodeGen code, long arg, int timeout ) {
+ if( code._start.uctrl()==null ) return ""; // The empty program
SB trace = null; // new SB(); // TRACE, set to null for off, new SB() for on
// Force local scheduling phase
- if( code._phase.ordinal() < CodeGen.Phase.TypeCheck .ordinal() ) code.typeCheck();
- if( code._phase.ordinal() < CodeGen.Phase.Schedule .ordinal() ) code.GCM();
- if( code._phase.ordinal() < CodeGen.Phase.LocalSched.ordinal() ) code.localSched();
+ code.driver(CodeGen.Phase.LocalSched);
// Set global, so don't have to pass everywhere
CODE = code;
@@ -290,9 +289,9 @@ public abstract class Eval2 {
// Fetch without unboxing, searching up-Frame
static Object val( Node n ) { return F.get(n); }
// Fetch and unbox as primitive long
- static long x( Node n ) { Object d = F.get(n); return d==null ? 0 : (Long) F.get(n); }
+ static long x( Node n ) { Object d = F.get(n); return d==null ? 0 : (Long)d; }
// Fetch and unbox as primitive double
- static double d( Node n ) { Object d = F.get(n); return d==null ? 0 : (Double)F.get(n); }
+ static double d( Node n ) { Object d = F.get(n); return d==null ? 0 : (Double)d; }
// Fetch and unbox a function constant
static TypeFunPtr tfp(Node n) { return (TypeFunPtr)F.get(n); }
// Fetch and unbox a closure
@@ -348,10 +347,19 @@ public abstract class Eval2 {
}
private static Object load( LoadNode ld ) {
- TypeMemPtr tmp = (TypeMemPtr)ld.ptr()._type;
- Object[] fs = (Object[])val(ld.ptr());
+ Object f = val(ld.ptr());
+ // Check for dense constant array
+ if( f instanceof TypeMemPtr tmp ) {
+ assert tmp._obj._con != TypeConAry.BOT;
+ if( ld._name.equals("#") )
+ return (long)tmp._obj._con.len();
+ int idx = offToIdx(x(ld.off()),tmp._obj);
+ return tmp._obj._con.at(idx);
+ }
+ Object[] fs = (Object[])f;
if( ld._name.equals("#") )
return (long)fs.length;
+ TypeMemPtr tmp = (TypeMemPtr)ld.ptr()._type;
int idx = tmp._obj.isAry()
? offToIdx(x(ld.off()),tmp._obj)
: tmp._obj.find(ld._name);
diff --git a/src/test/java/com/seaofnodes/simple/EvalArm64.java b/src/test/java/com/seaofnodes/simple/EvalArm64.java
index 3ea9bea0..981799bb 100644
--- a/src/test/java/com/seaofnodes/simple/EvalArm64.java
+++ b/src/test/java/com/seaofnodes/simple/EvalArm64.java
@@ -2,6 +2,7 @@ package com.seaofnodes.simple;
import com.seaofnodes.simple.codegen.Encoding;
import com.seaofnodes.simple.node.cpus.arm.arm;
+import java.io.ByteArrayOutputStream;
public class EvalArm64 {
// Memory image, always 0-based
@@ -13,9 +14,13 @@ public class EvalArm64 {
final double[] fregs;
int _pc;
- int SENTINEL_CALLOC = -4;
+
// Start of free memory for allocation
int _heap;
+
+ // Standard out and err streams
+ ByteArrayOutputStream _stdout, _stderr;
+
// Cycle counters
int _cycle;
@@ -38,8 +43,7 @@ public class EvalArm64 {
public int ld1s(int x) { return _buf[x ] ; }
public int ld1z(int x) { return _buf[x ]&0xFF; }
public int ld2s(int x) { return ld1z(x) | ld1s(x+1)<< 8; }
- public int ld2z(int x) {
- return ld1z(x) | ld1z(x+1)<< 8; }
+ public int ld2z(int x) { return ld1z(x) | ld1z(x+1)<< 8; }
public int ld4s(int x) { return ld2z(x) | ld2s(x+2)<<16; }
public long ld8 (int x) { return (long)ld4s(x)&0xFFFFFFFFL | ((long)ld4s(x+4))<<32; }
@@ -81,652 +85,656 @@ public class EvalArm64 {
int rdid = ir & 0x1f;
// [31:24]
int opcode1 = (ir >> 24) & 0xFF;
- // [31:26]
- int opcode2 = (ir >> 26) & 0x3F;
-
- switch(opcode2) {
- case 5: {
- //b
- int imm26 = ir << 6 >> 6;
-
- pc = pc + (imm26 * 4) - 4;
- // won't hit pc += 4 at the bottom because continue
- // do it manually
- pc += 4;
+
+ // for the opcode we just match for bits[31:24];
+ switch( opcode1 ) {
+ case 0x14, 0x15, 0x16, 0x17: { //b
+ int imm26 = ir << 6 >> 6;
+ pc = pc + (imm26 * 4) - 4;
+ rdid = -1;
+ break;
+ }
+ case 0x1E: { // floats
+ is_f = true;
+ int rs1 = (ir >> 5) & 0x1F;
+ int rs2 = (ir >> 16) & 0x1F;
+
+ int op = (ir >> 10) & 0x3F;
+ switch(op) {
+ case 16: frval = fregs[rs1]; break; // fmov
+ case 10:frval = fregs[rs1] + fregs[rs2]; break; // fadd
+
+ case 6: if (fregs[rs2] == 0) frval = 0; else frval = fregs[rs1] / fregs[rs2]; break; // fdiv
+ case 8: {
+ // fcmp
+ double lhs = fregs[rs1];
+ double rhs = fregs[rs2];
+
+ Z = (lhs == rhs);
+ N = (lhs < rhs);
+ C = lhs >= rhs;
+ V = Double.isNaN(lhs) || Double.isNaN(rhs);
rdid = -1;
- continue ;
- }
- case 0x25: {
- // bl (just calloc)
- rval = pc + 4;
- regs[arm.X30] = rval;
- if(pc + 4 == 0) {
- regs[rdid] = rval;
- break outer;
- }
- int imm26 = (ir & 0x03FFFFFF);
- int imm = (imm26 << 6) >> 6;
- pc = pc + (imm << 2) ;
- if (pc == SENTINEL_CALLOC) {
- long size = regs[arm.X0]*regs[arm.X1];
- regs[arm.X0] = _heap;
- _heap += (int)size;
- // unwind pc
- pc = (int)rval;
- }
- continue;
+
+ break;
+ }
+ case 2: frval = fregs[rs1] * fregs[rs2]; break; // fmul
+ case 14: frval = fregs[rs1] - fregs[rs2]; break; // fsub
+ default: trap = (2+1);
}
+ break;
}
- // for the opcode we just match for bits[31:24];
- switch(opcode1) {
- case 0xD2: {
- // movz
- // get immediate
- rval = (ir >> 5) & 0x7FFF;
- break;
+
+ case 0x38: {
+ // STRB/LDRB(register)
+ int rn = (ir >> 5) & 0x1F;
+ int rm = (ir >> 16) & 0x1F;
+ // full opcode
+ opcode1 = (ir >> 21) & 0x7FF;
+ switch( ir >> 21 & 0x7 ) {
+ case 0x1 -> { // store
+ if( opcode1 == arm.OP_STORE_R_8 ) st1((int)regs[rn] + (int)regs[rm], (int)regs[rdid]);
+ else trap = (2+1);
+ rdid = -1;
}
- case 0x38: {
- // STRB/LDRB(register)
- int opc = ir >> 21 & 0x7;
- int rn = (ir >> 5) & 0x1F;
- int rm = (ir >> 16) & 0x1F;
- // full opcode
- opcode1 = (ir >> 21) & 0x7FF;
- switch(opc) {
- case 0x1: {
- // store
- if(opcode1 == arm.OP_STORE_R_8) {
- st1((int)regs[rn] + (int)regs[rm], (int)regs[rdid]);
- }else {
- trap = (2+1);
- }
- rdid = -1;
- break;
- }
- case 0x3: {
- // load
- if(opcode1 == arm.OP_LOAD_R_8) {
- rval = ld1s((int)regs[rn] + (int)regs[rm]);
- } else {
- trap = (2+1);
- }
- break;
- }
- default: trap = (2+1);
- }
+ case 0x3 -> { // load
+ if( opcode1 == arm.OP_LOAD_R_8 ) rval = ld1s((int)regs[rn] + (int)regs[rm]);
+ else trap = (2+1);
+ }
+ default -> trap = (2+1);
+ }
+ break;
+ }
+ case 0x39: {
+ // STRB/LDRB(immediate)
+ // str/ldr(immediate)
+ // rn
+ int base = ir >> 5 & 0x1F;
+ int imm = (ir >> 10) & 0xFFF;
+ rval = regs[base] + imm;
+ int opc = (ir >> 22) & 0x3;
+ // full opcode
+ opcode1 = (ir >> 22) & 0x3FF;
+
+ switch(opc) {
+ case 0x0: {
+ // store
+ if(opcode1 == arm.OP_STORE_IMM_8) {
+ st1((int)rval, (int)regs[rdid]);
+ } else trap = (2+1);
+ rdid = -1;
+
break;
}
- case 0x39: {
- // STRB/LDRB(immediate)
- // str/ldr(immediate)
- // rn
- int base = ir >> 5 & 0x1F;
- int imm = (ir >> 10) & 0xFFF;
- rval = regs[base] + imm;
- int opc = (ir >> 22) & 0x3;
- // full opcode
- opcode1 = (ir >> 22) & 0x3FF;
-
- switch(opc) {
- case 0x0: {
- // store
- if(opcode1 == arm.OP_STORE_IMM_8) {
- st1((int)rval, (int)regs[rdid]);
- } else trap = (2+1);
- rdid = -1;
-
- break;
- }
- case 0x1: {
- // LDR (immediate)
- if(opcode1 == arm.OP_LOAD_IMM_8) {
- rval = ld1s((int)rval);
- } else trap = (2+1);
- rdid = -1;
- break;
- }
- default: trap = (2+1);
- }
+ case 0x1: {
+ // LDR (immediate)
+ if(opcode1 == arm.OP_LOAD_IMM_8) {
+ rval = ld1s((int)rval);
+ } else trap = (2+1);
+ rdid = -1;
break;
}
- case 0x78: {
- // STRH/LDRH(register)
- int opc = ir >> 21 & 0x7;
- int rn = (ir >> 5) & 0x1F;
- int rm = (ir >> 16) & 0x1F;
- // full opcode
- opcode1 = (ir >> 21) & 0x7FF;
- switch(opc) {
- case 0x1: {
- // store
- if(opcode1 == arm.OP_STORE_R_16) {
- st1((int)regs[rn] + (int)regs[rm], (int)regs[rdid]);
- }else {
- trap = (2+1);
- }
- rdid = -1;
- break;
- }
- case 0x3: {
- // load
- if(opcode1 == arm.OP_LOAD_R_16) {
- rval = ld1s((int)regs[rn] + (int)regs[rm]);
- } else {
- trap = (2+1);
- }
- break;
- }
- default: trap = (2+1);
+ default: trap = (2+1);
+ }
+ break;
+ }
+ case 0x54: {
+ // b.cond
+ int imm19 = ir << 8 >> 13;
+ imm19 = pc + (imm19 * 4) - 4;
+ rdid = -1;
+
+ switch(ir & 0xF) {
+ case 0x0: if(Z) pc = imm19; break; // eq
+ case 0x1: if(!Z) pc = imm19; break; // ne
+ case 0x2: if(C) pc = imm19; break; // cs
+ case 0x3: if(!C) pc = imm19; break;
+ case 0x4: if(N) pc = imm19; break; // mi
+ case 0x5: if(!N) pc = imm19; break; // pl
+ case 0x6: if(V) pc = imm19; break; // vs
+ case 0x7: if(!V) pc = imm19; break; // vc
+ case 0x8: if(C && !Z) pc = imm19; break; // hi
+ case 0x9: if(!C || Z) pc = imm19; break; // ls
+ case 0xA: if(N == V) pc = imm19; break; // ge
+ case 0xB: if(N != V) pc = imm19; break; // lt
+ case 0xC: if(!Z && N == V) pc = imm19; break; // gt
+ case 0xD: if(Z || N != V) pc = imm19; break; // le
+ case 0xE: pc = imm19; break; // always executed(al)
+ default: trap = (2+1);
+ }
+ break;
+ }
+
+ case 0x5c: {
+ // ldr
+ is_f = true;
+ // address = delta
+ int address = ir << 8 >> 13;
+ address *= 4;
+ if(address >= _buf.length-3) trap = (5 + 1);
+ frval = ld8f(pc + address);
+ break;
+ }
+
+ case 0x78: {
+ // STRH/LDRH(register)
+ int opc = ir >> 21 & 0x7;
+ int rn = (ir >> 5) & 0x1F;
+ int rm = (ir >> 16) & 0x1F;
+ // full opcode
+ opcode1 = (ir >> 21) & 0x7FF;
+ switch(opc) {
+ case 0x1: {
+ // store
+ if(opcode1 == arm.OP_STORE_R_16) {
+ st1((int)regs[rn] + (int)regs[rm], (int)regs[rdid]);
+ }else {
+ trap = (2+1);
}
+ rdid = -1;
break;
}
-
- case 0x79: {
- // STRH/LDRH(immediate)
- int base = ir >> 5 & 0x1F;
- int imm = (ir >> 10) & 0xFFF;
- imm *= 2;
- rval = regs[base] + imm;
- int opc = (ir >> 22) & 0x3;
- // full opcode
- opcode1 = (ir >> 22) & 0x3FF;
-
- switch(opc) {
- case 0x0: {
- // store
- if(opcode1 == arm.OP_STORE_IMM_16) {
- st2((int)rval, (int)regs[rdid]);
- } else trap = (2+1);
- rdid = -1;
-
- break;
- }
- case 0x1: {
- // LDR (immediate)
- if(opcode1 == arm.OP_LOAD_IMM_16) {
- rval = ld2s((int)rval);
- } else trap = (2+1);
- rdid = -1;
- break;
- }
- default: trap = (2+1);
+ case 0x3: {
+ // load
+ if(opcode1 == arm.OP_LOAD_R_16) {
+ rval = ld1s((int)regs[rn] + (int)regs[rm]);
+ } else {
+ trap = (2+1);
}
break;
}
+ default: trap = (2+1);
+ }
+ break;
+ }
+ case 0x79: {
+ // STRH/LDRH(immediate)
+ int base = ir >> 5 & 0x1F;
+ int imm = (ir >> 10) & 0xFFF;
+ imm *= 2;
+ rval = regs[base] + imm;
+ int opc = (ir >> 22) & 0x3;
+ // full opcode
+ opcode1 = (ir >> 22) & 0x3FF;
+
+ switch(opc) {
+ case 0x0: {
+ // store
+ if(opcode1 == arm.OP_STORE_IMM_16) {
+ st2((int)rval, (int)regs[rdid]);
+ } else trap = (2+1);
+ rdid = -1;
- case 0x5c: {
- // ldr
- is_f = true;
- // address = delta
- int address = ir << 8 >> 13;
- address *= 4;
- if(address >= _buf.length-3) trap = (5 + 1);
- frval = ld8f(pc + address);
break;
}
- case 0xB9: {
- // str/ldr(immediate) 32 bits
- int base = ir >> 5 & 0x1F;
- int imm = (ir >> 10) & 0xFFF;
- imm *= 4;
-
- rval = regs[base] + imm;
- int opc = (ir >> 22) & 0x3;
-
- opcode1 = (ir >> 22) & 0x3FF;
- switch(opc) {
- case 0x0: {
- // store
- if(opcode1 == arm.OP_STORE_IMM_32) {
- st4((int)rval, (int)regs[rdid]);
- } else trap = (2+1);
-
- rdid = -1;
- break;
- }
- case 0x1: {
- // LDR (immediate)
- if(opcode1 == arm.OP_LOAD_IMM_32) {
- rval = ld4s((int) rval);
- } else trap = (2+1);
- break;
- }
- default: trap = (2+1);
- }
+ case 0x1: {
+ // LDR (immediate)
+ if(opcode1 == arm.OP_LOAD_IMM_16) {
+ rval = ld2s((int)rval);
+ } else trap = (2+1);
+ rdid = -1;
break;
}
- case 0xF9: {
- // str/ldr(immediate) 64 bits
- int base = ir >> 5 & 0x1F;
- int imm = (ir >> 10) & 0xFFF;
-
- imm *= 8;
-
- rval = regs[base] + imm;
- int opc = (ir >> 22) & 0x3;
- // full opcode
- opcode1 = (ir >> 22) & 0x3FF;
- switch(opc) {
- case 0x0: {
- // store
- if(opcode1 == arm.OP_STORE_IMM_64) {
- st8((int)rval, regs[rdid]);
- } else trap = (2+1);
-
- rdid = -1;
- break;
- }
- case 0x1: {
- // LDR (immediate)
- if(opcode1 == arm.OP_LOAD_IMM_64) {
- rval = ld8((int)rval);
- } else trap = (2+1);
- break;
- }
- default: trap = (2+1);
- }
- break;
+ default: trap = (2+1);
}
- case 0xf8: {
- // LDR/STR (register + index) 64 bits
- int opc = ir >> 21 & 0x7;
- int rn = (ir >> 5) & 0x1F;
- int rm = (ir >> 16) & 0x1F;
- // full opcode
- opcode1 = (ir >> 21) & 0x7FF;
- switch(opc) {
- case 0x1: {
- if(opcode1 == arm.OP_STORE_R_64) {
- st8((int)regs[rn] + (int)regs[rm], regs[rdid]);
- }else {
- trap = (2+1);
- }
- // store(expected to store the length here)
-
- rdid = -1;
- break;
- }
- case 0x3: {
- // load
- if(opcode1 == arm.OP_LOAD_R_64) {
- rval = ld8((int) (regs[rn] + regs[rm]));
- } else {
- trap = (2+1);
- }
- break;
- }
- default: trap = (2+1);
- }
+ break;
+ }
- break;
- }
+ case 0x8A: { // AND
+ int rn = (ir >> 5) & 0x1F;
+ int rm = (ir >> 16) & 0x1F;
+ rval = regs[rn] & regs[rm];
+ break;
+ }
- case 0xB8: {
- // LDR/STR (register + index) 32 bits
- int opc = ir >> 21 & 0x7;
- int rn = (ir >> 5) & 0x1F;
- int rm = (ir >> 16) & 0x1F;
- // full opcode
- opcode1 = (ir >> 21) & 0x7FF;
- switch(opc) {
- case 0x1: {
- if(opcode1 == arm.OP_STORE_R_32) {
- st4((int)regs[rn] + (int)regs[rm], (int)regs[rdid]);
- }else {
- trap = (2+1);
- }
- // store(expected to store the length here)
-
- rdid = -1;
- break;
- }
- case 0x3: {
- // load
- if( opcode1 == arm.OP_LOAD_R_32) {
- rval = ld4s((int) (regs[rn] + regs[rm]));
- } else {
- trap = (2+1);
- }
- break;
- }
- default: trap = (2+1);
- }
+ case 0x8B: {
+ // add(shifted register)
+ int rn = (ir >> 5) & 0x1F;
+ int rm = (ir >> 16) & 0x1F;
+ long lhs = regs[rn];
+ long rhs = regs[rm];
+ rval = lhs + rhs;
+ break;
+ }
- break;
- }
+ case 0x90: { // adrp
+ int immhi = ( (ir& ~0x1F) << 8 ) >> 8;
+ int immlo = (ir>>29)& 3;
+ int imm = ((immhi<<2) | immlo) << 12;
+ rval = (pc & ~0xFFF) + imm;
+ break;
+ }
- case 0xFC: {
- // LDR/STR (register + index) float 64 bits
- int opc = ir >> 21 & 0x7;
- int rn = (ir >> 5) & 0x1F;
- int rm = (ir >> 16) & 0x1F;
- is_f = true;
- // full opcode
- opcode1 = (ir >> 21) & 0x7FF;
- switch(opc) {
- case 0x1: {
- if(opcode1 == arm.OPF_STORE_R_64) {
- st8((int)regs[rn] + (int)regs[rm], (int)regs[rdid]);
- }else {
- trap = (2+1);
- }
- // store(expected to store the length here)
-
- rdid = -1;
- break;
- }
- case 0x3: {
- // load
- if(opcode1 == arm.OPF_LOAD_R_64) {
- frval = ld8f((int) (regs[rn] + regs[rm]));
- } else {
- trap = (2+1);
- }
- break;
- }
- default: trap = (2+1);
- }
+ case 0x91: { // add(immediate)
+ int rn = (ir >> 5) & 0x1F;
+ int immediate = ir << 10 >> 20;
+ // only hit it twice
+ rval = regs[rn] + immediate;
+ break;
+ }
+ case 0x92: { // and(immediate)
+ int imm12 = (ir >> 10) & 0x1FFF;
+ long immediate = arm.decodeImm12(imm12);
+ int rn = (ir >> 5) & 0x1F;
+ rval = regs[rn] & immediate;
+ break;
+ }
+ case 0x93: {
+ // asr(immediate)
+ int imm = (ir >> 16) & 0x3F;
+ int rn = (ir >> 5) & 0x1F;
+ rval = regs[rn] >> (imm);
+ break;
+ }
- break;
+ case 0x94, 0x95, 0x96, 0x97: { // bl
+ rval = pc + 4;
+ regs[arm.X30] = rval;
+ int imm26 = (ir & 0x03FFFFFF);
+ int imm = (imm26 << 6) >> 6;
+ pc = pc + (imm << 2) ;
+ if( pc == Encoding.SENTINEL_CALLOC ) {
+ assert (_heap&7) == 0; // 8-byte aligned
+ long size = regs[arm.X0]*regs[arm.X1];
+ size = (size+7) & -8; // 8-byte aligned
+ regs[arm.X0] = _heap;
+ _heap += (int)size;
+ pc = (int)rval;
+ rdid = -1;
}
+ if( pc == Encoding.SENTINEL_WRITE ) {
+ ByteArrayOutputStream baos = switch((int)regs[0]) {
+ case 1 -> _stdout==null ? (_stdout = new ByteArrayOutputStream()) : _stdout;
+ case 2 -> _stderr==null ? (_stderr = new ByteArrayOutputStream()) : _stderr;
+ default -> throw new IllegalArgumentException();
+ };
+ baos.write(_buf,(int)regs[1],(int)regs[2]);
+ regs[0] = regs[2];
+ pc = (int)rval;
+ rdid = -1;
+ }
+ pc -= 4;
+ break;
+ }
- case 0xBC: {
- // LDR/STR (register + index) float 32 bits
- int opc = ir >> 21 & 0x7;
- int rn = (ir >> 5) & 0x1F;
- int rm = (ir >> 16) & 0x1F;
- is_f = true;
- // full opcode
- opcode1 = (ir >> 21) & 0x7FF;
- switch(opc) {
- case 0x1: {
- if(opcode1 == arm.OPF_STORE_R_32) {
- st4((int)regs[rn] + (int)regs[rm], (int)regs[rdid]);
- }else {
- trap = (2+1);
- }
- // store(expected to store the length here)
-
- rdid = -1;
- break;
- }
- case 0x3: {
- // load
- if(opcode1 == arm.OPF_LOAD_R_32) {
- frval = ld4f((int) (regs[rn] + regs[rm]));
- } else {
- trap = (2+1);
- }
- break;
- }
- default: trap = (2+1);
+ case 0x9A: {
+ // conditional select(csel)
+ int rm = (ir >> 16) & 0x1F;
+ switch((ir >> 12) & 0xF) {
+ case 0x0: if(Z) rval = regs[rm]; break; // eq
+ case 0x1: if(!Z) rval = regs[rm]; break; // ne
+ case 0x2: if(C) rval = regs[rm]; break; // cs
+ case 0x3: if(!C) rval = regs[rm]; break;
+ case 0x4: if(N) rval = regs[rm]; break; // mi
+ case 0x5: if(!N) rval = regs[rm]; break; // pl
+ case 0x6: if(V) rval = regs[rm]; break; // vs
+ case 0x7: if(!V) rval = regs[rm]; break; // vc
+ case 0x8: if(C && !Z) rval = regs[rm]; break; // hi
+ case 0x9: if(!C || Z) rval = regs[rm]; break; // ls
+ case 0xA: if(N == V) rval = regs[rm]; break; // ge
+ case 0xB: if(N != V) rval = regs[rm]; break; // lt
+ case 0xC: if(!Z && N == V) rval = regs[rm]; break; // gt
+ case 0xD: if(Z || N != V) rval = regs[rm]; break; // le
+ case 0xE: rval = regs[rm]; break; // always executed(al)
+ default: rval = regs[(ir >> 5) & 0x1F];
+ }
+ break;
+ }
+ case 0x9B: { // mul
+ int rn = (ir >> 5) & 0x1F;
+ int rm = (ir >> 16) & 0x1F;
+ rval = regs[rn] * regs[rm];
+ break;
+ }
+ case 0x9E: {
+ // scvtf
+ is_f = true;
+ int rs1 = (ir >> 5) & 0x1F;
+ frval = (double)regs[rs1];
+ break;
+ }
+
+ case 0xAA: {
+ // MOV(register)
+ int rm = (ir >> 16) & 0x1F;
+ rval = regs[rm];
+ break;
+ }
+
+ case 0xB8: {
+ // LDR/STR (register + index) 32 bits
+ int opc = ir >> 21 & 0x7;
+ int rn = (ir >> 5) & 0x1F;
+ int rm = (ir >> 16) & 0x1F;
+ // full opcode
+ opcode1 = (ir >> 21) & 0x7FF;
+ switch(opc) {
+ case 0x1: {
+ if(opcode1 == arm.OP_STORE_R_32) {
+ st4((int)regs[rn] + (int)regs[rm], (int)regs[rdid]);
+ }else {
+ trap = (2+1);
}
+ // store(expected to store the length here)
+ rdid = -1;
break;
}
-
- case 0xBD: {
- // LDR/STR(immediate) 32 bits floats
- int base = ir >> 5 & 0x1F;
- int imm = (ir >> 10) & 0xFFF;
-
- imm *= 4;
- is_f = true;
- rval = regs[base] + imm;
- int opc = (ir >> 22) & 0x3;
- // full opcode
- opcode1 = (ir >> 22) & 0x3FF;
- switch(opc) {
- case 0x0: {
- // store
- if(opcode1 == arm.OPF_STORE_IMM_32) {
- st4((int)rval, (int)regs[rdid]);
- } else trap = (2+1);
-
- rdid = -1;
- break;
- }
- case 0x1: {
- // LDR (immediate)
- if(opcode1 == arm.OPF_LOAD_IMM_32) {
- frval = ld4s((int)rval);
- } else trap = (2+1);
- break;
- }
- default: trap = (2+1);
+ case 0x3: {
+ // load
+ if( opcode1 == arm.OP_LOAD_R_32) {
+ rval = ld4s((int) (regs[rn] + regs[rm]));
+ } else {
+ trap = (2+1);
}
break;
}
- case 0xFD: {
- // LDR/STR(immediate) 64 bits floats
- int base = ir >> 5 & 0x1F;
- int imm = (ir >> 10) & 0xFFF;
-
- imm *= 4;
- is_f = true;
- rval = regs[base] + imm;
- int opc = (ir >> 22) & 0x3;
- // full opcode
- opcode1 = (ir >> 22) & 0x3FF;
- switch(opc) {
- case 0x0: {
- // store
- if(opcode1 == arm.OPF_STORE_IMM_64) {
- st8((int)rval, regs[rdid]);
- } else trap = (2+1);
-
- rdid = -1;
- break;
- }
- case 0x1: {
- // LDR (immediate)
- if(opcode1 == arm.OPF_LOAD_IMM_64) {
- frval = ld8((int)rval);
- } else trap = (2+1);
- break;
- }
- default: trap = (2+1);
- }
+ default: trap = (2+1);
+ }
+
+ break;
+ }
+
+ case 0xB9: {
+ // str/ldr(immediate) 32 bits
+ int base = ir >> 5 & 0x1F;
+ int imm = (ir >> 10) & 0xFFF;
+ imm *= 4;
+
+ rval = regs[base] + imm;
+ int opc = (ir >> 22) & 0x3;
+
+ opcode1 = (ir >> 22) & 0x3FF;
+ switch(opc) {
+ case 0x0: {
+ // store
+ if(opcode1 == arm.OP_STORE_IMM_32) {
+ st4((int)rval, (int)regs[rdid]);
+ } else trap = (2+1);
+
+ rdid = -1;
break;
}
- case 0x9b: {
- // mul
- int rn = (ir >> 5) & 0x1F;
- int rm = (ir >> 16) & 0x1F;
- rval = regs[rn] * regs[rm];
+ case 0x1: {
+ // LDR (immediate)
+ if(opcode1 == arm.OP_LOAD_IMM_32) {
+ rval = ld4s((int) rval);
+ } else trap = (2+1);
break;
}
- case 0x54: {
- // b.cond
- int imm19 = ir << 8 >> 13;
- imm19 = pc + (imm19 * 4) - 4;
- rdid = -1;
+ default: trap = (2+1);
+ }
+ break;
+ }
- switch(ir & 0xF) {
- case 0x0: if(Z) pc = imm19; break; // eq
- case 0x1: if(!Z) pc = imm19; break; // ne
- case 0x2: if(C) pc = imm19; break; // cs
- case 0x3: if(!C) pc = imm19; break;
- case 0x4: if(N) pc = imm19; break; // mi
- case 0x5: if(!N) pc = imm19; break; // pl
- case 0x6: if(V) pc = imm19; break; // vs
- case 0x7: if(!V) pc = imm19; break; // vc
- case 0x8: if(C && !Z) pc = imm19; break; // hi
- case 0x9: if(!C || Z) pc = imm19; break; // ls
- case 0xA: if(N == V) pc = imm19; break; // ge
- case 0xB: if(N != V) pc = imm19; break; // lt
- case 0xC: if(!Z && N == V) pc = imm19; break; // gt
- case 0xD: if(Z || N != V) pc = imm19; break; // le
- case 0xE: pc = imm19; break; // always executed(al)
- default: trap = (2+1);
+ case 0xBC: {
+ // LDR/STR (register + index) float 32 bits
+ int opc = ir >> 21 & 0x7;
+ int rn = (ir >> 5) & 0x1F;
+ int rm = (ir >> 16) & 0x1F;
+ is_f = true;
+ // full opcode
+ opcode1 = (ir >> 21) & 0x7FF;
+ switch(opc) {
+ case 0x1: {
+ if(opcode1 == arm.OPF_STORE_R_32) {
+ st4((int)regs[rn] + (int)regs[rm], (int)regs[rdid]);
+ }else {
+ trap = (2+1);
}
+ // store(expected to store the length here)
+
+ rdid = -1;
break;
}
- case 0x9a: {
- // conditional select(csel)
- int rm = (ir >> 16) & 0x1F;
- switch((ir >> 12) & 0xF) {
- case 0x0: if(Z) rval = regs[rm]; break; // eq
- case 0x1: if(!Z) rval = regs[rm]; break; // ne
- case 0x2: if(C) rval = regs[rm]; break; // cs
- case 0x3: if(!C) rval = regs[rm]; break;
- case 0x4: if(N) rval = regs[rm]; break; // mi
- case 0x5: if(!N) rval = regs[rm]; break; // pl
- case 0x6: if(V) rval = regs[rm]; break; // vs
- case 0x7: if(!V) rval = regs[rm]; break; // vc
- case 0x8: if(C && !Z) rval = regs[rm]; break; // hi
- case 0x9: if(!C || Z) rval = regs[rm]; break; // ls
- case 0xA: if(N == V) rval = regs[rm]; break; // ge
- case 0xB: if(N != V) rval = regs[rm]; break; // lt
- case 0xC: if(!Z && N == V) rval = regs[rm]; break; // gt
- case 0xD: if(Z || N != V) rval = regs[rm]; break; // le
- case 0xE: rval = regs[rm]; break; // always executed(al)
- default: {
- rval = regs[(ir >> 5) & 0x1F];
- }
+ case 0x3: {
+ // load
+ if(opcode1 == arm.OPF_LOAD_R_32) {
+ frval = ld4f((int) (regs[rn] + regs[rm]));
+ } else {
+ trap = (2+1);
}
break;
}
- case 0xeb: {
- // subs(shifted register)
- int rn = (ir >> 5) & 0x1F;
- int rm = (ir >> 16) & 0x1F;
- long lhs = regs[rn];
- long rhs = regs[rm];
- rval = lhs - rhs;
-
- // set flags as a side effect
- Z = (rval == 0);
- N = (rval < 0);
- C = lhs >= rhs;
+ default: trap = (2+1);
+ }
+
+ break;
+ }
+
+ case 0xBD: {
+ // LDR/STR(immediate) 32 bits floats
+ int base = ir >> 5 & 0x1F;
+ int imm = (ir >> 10) & 0xFFF;
+
+ imm *= 4;
+ is_f = true;
+ rval = regs[base] + imm;
+ int opc = (ir >> 22) & 0x3;
+ // full opcode
+ opcode1 = (ir >> 22) & 0x3FF;
+ switch(opc) {
+ case 0x0: {
+ // store
+ if(opcode1 == arm.OPF_STORE_IMM_32) {
+ st4((int)rval, (int)regs[rdid]);
+ } else trap = (2+1);
- boolean sign_lhs = (lhs >>> 63) != 0;
- boolean sign_rhs = (rhs >>> 63) != 0;
- boolean sign_res = (rval >>> 63) != 0;
- V = (sign_lhs != sign_rhs) && (sign_res != sign_lhs);
rdid = -1;
break;
}
- case 0xF1: {
- // subs(immediate)
- int rn = (ir >> 5) & 0x1F;
- int immediate = ir << 10 >> 20;
- long lhs = regs[rn];
+ case 0x1: {
+ // LDR (immediate)
+ if(opcode1 == arm.OPF_LOAD_IMM_32) {
+ frval = ld4s((int)rval);
+ } else trap = (2+1);
+ break;
+ }
+ default: trap = (2+1);
+ }
+ break;
+ }
+
+ case 0xD2: {
+ // movz
+ // get immediate
+ rval = (ir >> 5) & 0x7FFF;
+ break;
+ }
+
+ case 0xEB: {
+ // subs(shifted register)
+ int rn = (ir >> 5) & 0x1F;
+ int rm = (ir >> 16) & 0x1F;
+ long lhs = regs[rn];
+ long rhs = regs[rm];
+ rval = lhs - rhs;
+
+ // set flags as a side effect
+ Z = (rval == 0);
+ N = (rval < 0);
+ C = lhs >= rhs;
+
+ boolean sign_lhs = (lhs >>> 63) != 0;
+ boolean sign_rhs = (rhs >>> 63) != 0;
+ boolean sign_res = (rval >>> 63) != 0;
+ V = (sign_lhs != sign_rhs) && (sign_res != sign_lhs);
+ rdid = -1;
+ break;
+ }
+
+ case 0xD3: {
+ // lsl(immediate)
+ int imm = (ir >> 16) & 0x3F;
+ int rn = (ir >> 5) & 0x1F;
+ rval = regs[rn] << (64 - imm);
+ break;
+ }
+ case 0xD6: {
+ rdid = -1;
+ // Return to zero breaks simulation
+ if(regs[30] == 0)
+ break outer;
+ throw Utils.TODO();
+ }
- rval = lhs - immediate;
+ case 0xF1: {
+ // subs(immediate)
+ int rn = (ir >> 5) & 0x1F;
+ int immediate = ir << 10 >> 20;
+ long lhs = regs[rn];
- // set flags as a side effect
- Z = (rval == 0);
- N = (rval < 0);
- C = lhs >= immediate;
+ rval = lhs - immediate;
+
+ // set flags as a side effect
+ Z = (rval == 0);
+ N = (rval < 0);
+ C = lhs >= immediate;
+
+ boolean sign_lhs = (lhs >>> 63) != 0;
+ boolean sign_rhs = (immediate >>> 63) != 0;
+ boolean sign_res = (rval >>> 63) != 0;
+ V = (sign_lhs != sign_rhs) && (sign_res != sign_lhs);
+ rdid = -1;
+ break;
+ }
+ case 0xF8: {
+ // LDR/STR (register + index) 64 bits
+ int opc = ir >> 21 & 0x7;
+ int rn = (ir >> 5) & 0x1F;
+ int rm = (ir >> 16) & 0x1F;
+ // full opcode
+ opcode1 = (ir >> 21) & 0x7FF;
+ switch(opc) {
+ case 0x1: {
+ if(opcode1 == arm.OP_STORE_R_64) {
+ st8((int)regs[rn] + (int)regs[rm], regs[rdid]);
+ }else {
+ trap = (2+1);
+ }
+ // store(expected to store the length here)
- boolean sign_lhs = (lhs >>> 63) != 0;
- boolean sign_rhs = (immediate >>> 63) != 0;
- boolean sign_res = (rval >>> 63) != 0;
- V = (sign_lhs != sign_rhs) && (sign_res != sign_lhs);
rdid = -1;
break;
}
- case 0x8B: {
- // add(shifted register)
- int rn = (ir >> 5) & 0x1F;
- int rm = (ir >> 16) & 0x1F;
- long lhs = regs[rn];
- long rhs = regs[rm];
- rval = lhs + rhs;
+ case 0x3: {
+ // load
+ if(opcode1 == arm.OP_LOAD_R_64) {
+ rval = ld8((int) (regs[rn] + regs[rm]));
+ } else {
+ trap = (2+1);
+ }
break;
}
- case 0x91: {
- // add(immediate)
- int rn = (ir >> 5) & 0x1F;
- int immediate = ir << 10 >> 20;
- // only hit it twice
- rval = regs[rn] + immediate;
- break;
+ default: trap = (2+1);
}
- case 0xD3: {
- // lsl(immediate)
- int imm = (ir >> 16) & 0x3F;
- int rn = (ir >> 5) & 0x1F;
- rval = regs[rn] << (64 - imm);
+
+ break;
+ }
+ case 0xF9: {
+ // str/ldr(immediate) 64 bits
+ int base = ir >> 5 & 0x1F;
+ int imm = (ir >> 10) & 0xFFF;
+
+ imm *= 8;
+
+ rval = regs[base] + imm;
+ int opc = (ir >> 22) & 0x3;
+ // full opcode
+ opcode1 = (ir >> 22) & 0x3FF;
+ switch(opc) {
+ case 0x0: {
+ // store
+ if(opcode1 == arm.OP_STORE_IMM_64) {
+ st8((int)rval, regs[rdid]);
+ } else trap = (2+1);
+
+ rdid = -1;
break;
}
- case 0x93: {
- // asr(immediate)
- int imm = (ir >> 16) & 0x3F;
- int rn = (ir >> 5) & 0x1F;
- rval = regs[rn] >> (imm);
+ case 0x1: {
+ // LDR (immediate)
+ if(opcode1 == arm.OP_LOAD_IMM_64) {
+ rval = ld8((int)rval);
+ } else trap = (2+1);
break;
}
- case 0x92: {
- // and(immediate)
- int imm12 = (ir >> 10) & 0x1FFF;
- long immediate = arm.decodeImm12(imm12);
- int rn = (ir >> 5) & 0x1F;
- rval = regs[rn] & immediate;
- break;
+ default: trap = (2+1);
}
- case 0xAA: {
- // MOV(register)
- int rm = (ir >> 16) & 0x1F;
- rval = regs[rm];
+ break;
+ }
+
+ case 0xFC: {
+ // LDR/STR (register + index) float 64 bits
+ int opc = ir >> 21 & 0x7;
+ int rn = (ir >> 5) & 0x1F;
+ int rm = (ir >> 16) & 0x1F;
+ is_f = true;
+ // full opcode
+ opcode1 = (ir >> 21) & 0x7FF;
+ switch(opc) {
+ case 0x1: {
+ if(opcode1 == arm.OPF_STORE_R_64) {
+ st8((int)regs[rn] + (int)regs[rm], (int)regs[rdid]);
+ }else {
+ trap = (2+1);
+ }
+ // store(expected to store the length here)
+
+ rdid = -1;
break;
}
- case 0xD6: {
- rdid = -1;
- if(ld4s(pc + 4) == 0) {
- break outer;
+ case 0x3: {
+ // load
+ if(opcode1 == arm.OPF_LOAD_R_64) {
+ frval = ld8f((int) (regs[rn] + regs[rm]));
+ } else {
+ trap = (2+1);
}
- }
- // bitwise
- case 0x8A: {
- // AND
- int rn = (ir >> 5) & 0x1F;
- int rm = (ir >> 16) & 0x1F;
- rval = regs[rn] & regs[rm];
break;
}
- // floats
- case 0x1E: {
- is_f = true;
- int rs1 = (ir >> 5) & 0x1F;
- int rs2 = (ir >> 16) & 0x1F;
-
- int op = (ir >> 10) & 0x3F;
- switch(op) {
- case 16: frval = fregs[rs1]; break; // fmov
- case 10:frval = fregs[rs1] + fregs[rs2]; break; // fadd
-
- case 6: if (fregs[rs2] == 0) frval = 0; else frval = fregs[rs1] / fregs[rs2]; break; // fdiv
- case 8: {
- // fcmp
- double lhs = fregs[rs1];
- double rhs = fregs[rs2];
-
- Z = (lhs == rhs);
- N = (lhs < rhs);
- C = lhs >= rhs;
- V = Double.isNaN(lhs) || Double.isNaN(rhs);
- rdid = -1;
-
- break;
- }
- case 2: frval = fregs[rs1] * fregs[rs2]; break; // fmul
- case 14: frval = fregs[rs1] - fregs[rs2]; break; // fsub
- default: trap = (2+1);
- }
+ default: trap = (2+1);
+ }
+
+ break;
+ }
+ case 0xFD: {
+ // LDR/STR(immediate) 64 bits floats
+ int base = ir >> 5 & 0x1F;
+ int imm = (ir >> 10) & 0xFFF;
+
+ imm *= 4;
+ is_f = true;
+ rval = regs[base] + imm;
+ int opc = (ir >> 22) & 0x3;
+ // full opcode
+ opcode1 = (ir >> 22) & 0x3FF;
+ switch(opc) {
+ case 0x0: {
+ // store
+ if(opcode1 == arm.OPF_STORE_IMM_64) {
+ st8((int)rval, regs[rdid]);
+ } else trap = (2+1);
+
+ rdid = -1;
break;
}
- case 0x9e: {
- // scvtf
- is_f = true;
- int rs1 = (ir >> 5) & 0x1F;
- frval = (double)regs[rs1];
+ case 0x1: {
+ // LDR (immediate)
+ if(opcode1 == arm.OPF_LOAD_IMM_64) {
+ frval = ld8((int)rval);
+ } else trap = (2+1);
break;
}
default: trap = (2+1);
+ }
+ break;
}
+
+ default: trap = (2+1);
+ }
+
if(trap != 0) {
_pc = pc;
break;
diff --git a/src/test/java/com/seaofnodes/simple/EvalRisc5.java b/src/test/java/com/seaofnodes/simple/EvalRisc5.java
index fd953342..0a1c034f 100644
--- a/src/test/java/com/seaofnodes/simple/EvalRisc5.java
+++ b/src/test/java/com/seaofnodes/simple/EvalRisc5.java
@@ -10,10 +10,10 @@
import com.seaofnodes.simple.codegen.Encoding;
import com.seaofnodes.simple.node.cpus.riscv.riscv;
-
+import java.io.ByteArrayOutputStream;
import java.util.Arrays;
- public class EvalRisc5 {
+public class EvalRisc5 {
// Memory image, always 0-based
public final byte[] _buf;
@@ -28,6 +28,9 @@ import java.util.Arrays;
// Start of free memory for allocation
int _heap;
+ // Standard out and err streams
+ ByteArrayOutputStream _stdout, _stderr;
+
// Cycle counters
int _cycle;
@@ -100,6 +103,17 @@ import java.util.Arrays;
if( (reladdy & 0x00100000)!=0 ) reladdy |= 0xffe00000; // Sign extension.
rval = pc + 4;
pc = pc + reladdy - 4;
+ if( pc+4 == Encoding.SENTINEL_WRITE ) {
+ ByteArrayOutputStream baos = switch((int)regs[10]) {
+ case 1 -> _stdout==null ? (_stdout = new ByteArrayOutputStream()) : _stdout;
+ case 2 -> _stderr==null ? (_stderr = new ByteArrayOutputStream()) : _stderr;
+ default -> throw new IllegalArgumentException();
+ };
+ baos.write(_buf,(int)regs[11],(int)regs[12]);
+ regs[10] = regs[12];
+ rdid = 0;
+ pc = (int)(rval - 4); // Unwind PC, as-if returned from write
+ }
break;
}
case 0x67: { // JALR (0b1100111)
@@ -112,11 +126,11 @@ import java.util.Arrays;
break outer;
}
// Inline CALLOC effect
- if( pc == Encoding.SENTINAL_CALLOC ) {
+ if( pc == Encoding.SENTINEL_CALLOC ) {
+ assert (_heap&7) == 0; // 8-byte aligned
int size = (int)(regs[10]*regs[11]);
+ size = (size+7) & -8; // 8-byte aligned
regs[10] = _heap; // Return next free address
- // Pre-zeroed; epsilon (null) collector, never recycles memory so always zero
- // Arrays.fill(_buf,_heap,_heap+size, (byte) 0 );
_heap += size;
pc = (int)(rval - 4); // Unwind PC, as-if returned from calloc
} else
@@ -233,11 +247,11 @@ import java.util.Arrays;
} else {
rval = switch( (ir >> 12) & 7 ) { // These could be either op-immediate or op commands. Be careful.
case 0 -> (is_reg && (ir & 0x40000000) != 0) ? (rs1 - rs2) : (rs1 + rs2);
- case 1 -> rs1 << (rs2 & 0x1F);
+ case 1 -> rs1 << (rs2 & 0x3F);
case 2 -> (rs1 < rs2) ? 1 : 0;
case 3 -> (rs1 < rs2) ? 1 : 0;
case 4 -> rs1 ^ rs2;
- case 5 -> (ir & 0x40000000) != 0 ? (((int) rs1) >> (rs2 & 0x1F)) : (rs1 >> (rs2 & 0x1F));
+ case 5 -> (ir & 0x40000000) != 0 ? (rs1 >>> (rs2 & 0x3F)) : (rs1 >> (rs2 & 0x3F));
case 6 -> rs1 | rs2;
case 7 -> rs1 & rs2;
default -> rval;
diff --git a/src/test/java/com/seaofnodes/simple/Simple.java b/src/test/java/com/seaofnodes/simple/Simple.java
index 9b1e2827..1465aa6d 100644
--- a/src/test/java/com/seaofnodes/simple/Simple.java
+++ b/src/test/java/com/seaofnodes/simple/Simple.java
@@ -17,7 +17,7 @@ public class Simple {
static final int DUMP_AFTER_OPTO = 1<<1;
static final int DUMP_AFTER_TYPE_CHECK = 1<<2;
static final int DUMP_AFTER_LOOP_TREE = 1<<3;
- static final int DUMP_AFTER_INSTR_SELECT = 1<<4;
+ static final int DUMP_AFTER_SELECT = 1<<4;
static final int DUMP_AFTER_GCM = 1<<5;
static final int DUMP_AFTER_LOCAL_SCHED = 1<<6;
static final int DUMP_AFTER_REG_ALLOC = 1<<7;
@@ -32,33 +32,30 @@ public class Simple {
static final int DUMP_DISASSEMBLE = 1<<31;
- static String system_cpu = null;
- static String system_abi = null;
-
static void usage() {
System.out.println(
"""
-simple <input-file> [options] [--eval|--run ...]
+simple [options] <input-file> ...args...
Options:
- --dump - dump final intermediate representation
- --dump-after-parse - dump intermediate representation after parse
- --dump-after-opto - dump intermediate representation after opto pass
- --dump-after-type-check - dump intermediate representation after type check pass
- --dump-after-instr-select - dump intermediate representation after instrution selection pass
- --dump-after-gcm - dump intermediate representation after GCM pass
- --dump-after-local-sched - dump intermediate representation after local scheduling pass
- --dump-after-reg-alloc - dump intermediate representation after register allocation pass
- --dump-after-encode - dump intermediate representation after encoding pass
- --dump-after-all - dump intermediate representation after all passes
- --dot - dump grapical representation of intermediate code into *.dot file(s)
- -S - dump generated assembler code
- --eval ... - evaluate the compiled code in emulator
- --run ... - run the compiled code natively
- --dump-size - print the size of generated code
- --dump-time - print compilation and execution times
- --cpu <cpu-name> - use specific CPU (x86_64_v2, riscv, arm)
- --abi <abi-name> - use speific ABI variant (SystemV)
- --target - print native CPU and ABI
+ --dump - dump final intermediate representation
+ --dump-after-parse - dump intermediate representation after parse
+ --dump-after-opto - dump intermediate representation after opto pass
+ --dump-after-type-check - dump intermediate representation after type check pass
+ --dump-after-select - dump intermediate representation after instrution selection pass
+ --dump-after-gcm - dump intermediate representation after GCM pass
+ --dump-after-local-sched - dump intermediate representation after local scheduling pass
+ --dump-after-reg-alloc - dump intermediate representation after register allocation pass
+ --dump-after-encode - dump intermediate representation after encoding pass
+ --dump-after-all - dump intermediate representation after all passes
+ --dot - dump grapical representation of intermediate code into *.dot file(s)
+ -S - dump generated assembler code
+ --eval - evaluate the compiled code in emulator
+ --run - run the compiled code natively; this is the default
+ --dump-size - print the size of generated code
+ --dump-time - print compilation and execution times
+ --cpu <cpu-name> - use specific CPU (x86_64_v2, riscv, arm)
+ --abi <abi-name> - use speific ABI variant (SystemV)
+ --target - print native CPU and ABI
--version
--help
"""
@@ -66,46 +63,40 @@ Options:
System.exit(0);
}
- static void bad_usage() {
- System.err.println("ERROR: Invalid usage (use --help)");
+ static void bad_usage() { bad("Invalid usage (use --help)"); }
+ static RuntimeException bad(String err) {
+ System.err.println("ERROR: "+err);
System.exit(1);
+ return new RuntimeException(err);
}
static void target() {
- if (system_cpu == null || system_abi == null) {
- System.err.println("ERROR: Unknown target");
- System.exit(1);
- }
- System.out.print(system_cpu);
+ System.out.print(TestC.CPU_PORT);
System.out.print("-");
- System.out.println(system_abi);
- System.exit(0);
+ System.out.println(TestC.CALL_CONVENTION);
}
static void dump(CodeGen code, int dump, int pass) {
if ((dump & pass) != 0) {
if ((dump & DUMP_DOT) != 0) {
String fn = switch (pass) {
- case DUMP_AFTER_PARSE -> "01-parse.dot";
- case DUMP_AFTER_OPTO -> "02-opto.dot";
- case DUMP_AFTER_TYPE_CHECK -> "03-type_check.dot";
- case DUMP_AFTER_LOOP_TREE -> "04-loop_tree.dot";
- case DUMP_AFTER_INSTR_SELECT -> "05-instr_select.dot";
- case DUMP_AFTER_GCM -> "06-gcm.dot";
- case DUMP_AFTER_LOCAL_SCHED -> "07-local_sched.dot";
- case DUMP_AFTER_REG_ALLOC -> "08-reg_allos.dot";
- case DUMP_AFTER_ENCODE -> "09-local_sched.dot";
- case DUMP_FINAL -> "10-final.dot";
- default -> throw Utils.TODO();
+ case DUMP_AFTER_PARSE -> "01-parse.dot";
+ case DUMP_AFTER_OPTO -> "02-opto.dot";
+ case DUMP_AFTER_TYPE_CHECK -> "03-type_check.dot";
+ case DUMP_AFTER_LOOP_TREE -> "04-loop_tree.dot";
+ case DUMP_AFTER_SELECT -> "05-instr_select.dot";
+ case DUMP_AFTER_GCM -> "06-gcm.dot";
+ case DUMP_AFTER_LOCAL_SCHED -> "07-local_sched.dot";
+ case DUMP_AFTER_REG_ALLOC -> "08-reg_allos.dot";
+ case DUMP_AFTER_ENCODE -> "09-local_sched.dot";
+ case DUMP_FINAL -> "10-final.dot";
+ default -> throw Utils.TODO();
};
try {
Files.writeString(Path.of(fn),
- new GraphVisualizer().generateDotOutput(code._stop, null, null));
- } catch(IOException e) {
- System.err.println("ERROR: Cannot write DOT file");
- System.exit(1);
- }
+ new GraphVisualizer().generateDotOutput(code._stop, null, null));
+ } catch(IOException e) { throw bad("Cannot write DOT file"); }
} else {
if ((dump & DUMP_PASS_NAME) != 0) {
System.err.println(switch (pass) {
@@ -113,11 +104,11 @@ Options:
case DUMP_AFTER_OPTO -> "After OPTO:";
case DUMP_AFTER_TYPE_CHECK -> "After Type Check:";
case DUMP_AFTER_LOOP_TREE -> "After Loop Tree:";
- case DUMP_AFTER_INSTR_SELECT -> "After Instruction Selection:";
+ case DUMP_AFTER_SELECT -> "After Code Selection:";
case DUMP_AFTER_GCM -> "After GCM:";
case DUMP_AFTER_LOCAL_SCHED -> "After Local Scheduling:";
case DUMP_AFTER_REG_ALLOC -> "After Register Allocation:";
- case DUMP_AFTER_ENCODE -> "After Instruction Encoding:";
+ case DUMP_AFTER_ENCODE -> "After Encoding:";
case DUMP_FINAL -> "Final:";
default -> throw Utils.TODO();
});
@@ -132,24 +123,24 @@ Options:
double t, total = 0;
total += t = code._tParse / 1e3;
- System.out.println(String.format("Parsing Time: %.3f sec", t));
+ System.out.printf( "Parsing Time: %.3f sec%n", t);
total += t = code._tOpto / 1e3;
- System.out.println(String.format("Optimization Time: %.3f sec", t));
+ System.out.printf( "Optimization Time: %.3f sec%n", t);
total += t = code._tTypeCheck / 1e3;
- System.out.println(String.format("Type Checking Time: %.3f sec", t));
+ System.out.printf( "Type Checking Time: %.3f sec%n", t);
total += t = code._tLoopTree / 1e3;
- System.out.println(String.format("Loop Tree Time: %.3f sec", t));
+ System.out.printf( "Loop Tree Time: %.3f sec%n", t);
total += t = code._tInsSel / 1e3;
- System.out.println(String.format("Instruction Selection Time: %.3f sec", t));
+ System.out.printf( "Code Selection Time: %.3f sec%n", t);
total += t = code._tGCM / 1e3;
- System.out.println(String.format("GCM Time: %.3f sec", t));
+ System.out.printf( "GCM Time: %.3f sec%n", t);
total += t = code._tLocal / 1e3;
- System.out.println(String.format("Local Scheduling Time: %.3f sec", t));
+ System.out.printf( "Local Scheduling Time: %.3f sec%n", t);
total += t = code._tRegAlloc / 1e3;
- System.out.println(String.format("Register Allocation Time: %.3f sec", t));
+ System.out.printf( "Register Allocation Time: %.3f sec%n", t);
total += t = code._tEncode / 1e3;
- System.out.println(String.format("Instruction Encoding Time: %.3f sec", t));
- System.out.println(String.format("TOTAL COMPILATION TIME: %.3f sec", total));
+ System.out.printf( "Encoding Time: %.3f sec%n", t);
+ System.out.printf( "TOTAL COMPILATION TIME: %.3f sec%n", total);
}
static String getInput() {
@@ -159,9 +150,8 @@ Options:
public static void main(String[] args) throws Exception {
String input_filename = null;
- boolean use_stdin = false;
boolean do_eval = false;
- boolean do_run = false;
+ boolean do_run = true;
boolean do_codegen = false;
boolean do_print_size = false;
boolean do_print_time = false;
@@ -171,78 +161,62 @@ Options:
String cpu = null;
String abi = null;
- // TODO: autodetect
- system_cpu = "x86_64_v2";
- system_abi = "SystemV";
-
// Parse command line
-loop: for (int i = 0; i < args.length; i++) {
+ int i; for( i = 0; i < args.length; i++ ) {
String arg = args[i];
- if (arg.charAt(0) == '-') {
- switch (arg) {
- case "-": use_stdin = true; break;
- case "--dump": dump |= DUMP_FINAL; break;
- case "--dump-after-parse": dump |= DUMP_AFTER_PARSE; break;
- case "--dump-after-opto": dump |= DUMP_AFTER_OPTO; break;
- case "--dump-after-type-check": dump |= DUMP_AFTER_TYPE_CHECK; break;
- case "--dump-after-loop-tree": dump |= DUMP_AFTER_LOOP_TREE; break;
- case "--dump-after-instr-select": dump |= DUMP_AFTER_INSTR_SELECT; break;
- case "--dump-after-gcm": dump |= DUMP_AFTER_GCM; break;
- case "--dump-after-local-sched": dump |= DUMP_AFTER_LOCAL_SCHED; break;
- case "--dump-after-reg-alloc": dump |= DUMP_AFTER_REG_ALLOC; break;
- case "--dump-after-encode": dump |= DUMP_AFTER_ENCODE; break;
- case "--dump-after-all": dump |= DUMP_AFTER_ALL; break;
- case "--dot": dump |= DUMP_DOT; break;
- case "-S": dump |= DUMP_DISASSEMBLE; break;
- case "--eval": do_eval = true; first_arg = i + 1; break loop;
- case "--run": do_run = true; first_arg = i + 1; break loop;
- case "--dump-size": do_print_size = true; break;
- case "--dump-time": do_print_time = true; break;
- case "--cpu": if (cpu != null
- || i + 1 >= args.length
- || args[i + 1].charAt(0) == '-') bad_usage();
- cpu = args[i + 1];
- i++;
- break;
- case "--abi": if (abi != null
- || i + 1 >= args.length
- || args[i + 1].charAt(0) == '-') bad_usage();
- abi = args[i + 1];
- i++;
- break;
- case "--target": target();
- case "--version": throw Utils.TODO();
- case "--help": usage();
- default: bad_usage();
- }
- } else {
- if (input_filename != null || use_stdin) bad_usage();
+ if( arg.charAt(0) != '-' ) {
input_filename = arg;
+ break;
}
- }
- if (input_filename == null && !use_stdin) {
- System.err.println("ERROR: no input file' (use --help)");
- System.exit(1);
+ switch (arg) {
+ case "-": input_filename=""; break;
+ case "--dump": dump |= DUMP_FINAL; break;
+ case "--dump-after-parse": dump |= DUMP_AFTER_PARSE; break;
+ case "--dump-after-opto": dump |= DUMP_AFTER_OPTO; break;
+ case "--dump-after-type-check": dump |= DUMP_AFTER_TYPE_CHECK; break;
+ case "--dump-after-loop-tree": dump |= DUMP_AFTER_LOOP_TREE; break;
+ case "--dump-after-select": dump |= DUMP_AFTER_SELECT; break;
+ case "--dump-after-gcm": dump |= DUMP_AFTER_GCM; break;
+ case "--dump-after-local-sched": dump |= DUMP_AFTER_LOCAL_SCHED; break;
+ case "--dump-after-reg-alloc": dump |= DUMP_AFTER_REG_ALLOC; break;
+ case "--dump-after-encode": dump |= DUMP_AFTER_ENCODE; break;
+ case "--dump-after-all": dump |= DUMP_AFTER_ALL; break;
+ case "--dot": dump |= DUMP_DOT; break;
+ case "-S": dump |= DUMP_DISASSEMBLE; break;
+ case "--eval": do_eval = true ; do_run = false; break;
+ case "--run": do_run = true ; break;
+ case "--norun": do_run = false; break;
+ case "--dump-size": do_print_size = true; break;
+ case "--dump-time": do_print_time = true; break;
+ case "--cpu": if (cpu != null || i + 1 >= args.length || args[i + 1].charAt(0) == '-') bad_usage();
+ cpu = args[++i];
+ break;
+ case "--abi": if (abi != null || i + 1 >= args.length || args[i + 1].charAt(0) == '-') bad_usage();
+ abi = args[++i];
+ break;
+ case "--target": target(); break;
+ case "--version": throw Utils.TODO();
+ case "--help": usage();
+ default: bad_usage();
+ }
}
+ if (input_filename == null) throw bad("no input file' (use --help)");
+ if( !input_filename.isEmpty() && !input_filename.endsWith(".smp") )
+ throw bad("File extension must be .smp");
+ String base = input_filename.substring(0,input_filename.length()-4);
+
if (do_run || (dump & DUMP_DISASSEMBLE) != 0 || do_print_size) {
- if (cpu == null) cpu = system_cpu;
- if (abi == null) abi = system_abi;
- if (cpu == null || abi == null) {
- System.err.println("ERROR: Cannot compile for unknown target");
- System.exit(1);
- }
+ if (cpu == null) cpu = TestC.CPU_PORT;
+ if (abi == null) abi = TestC.CALL_CONVENTION;
do_codegen = true;
}
// Read input file
try {
- src = use_stdin ? getInput() : Files.readString(Path.of(input_filename));
- } catch(IOException e) {
- System.err.println("ERROR: Cannot read input file");
- System.exit(1);
- }
+ src = input_filename.isEmpty() ? getInput() : Files.readString(Path.of(input_filename));
+ } catch( IOException e ) { throw bad("Cannot read input file: "+input_filename); }
// Compilation pipeline
CodeGen code = new CodeGen(src);
@@ -260,8 +234,8 @@ loop: for (int i = 0; i < args.length; i++) {
dump(code, dump, DUMP_AFTER_LOOP_TREE);
if (do_codegen) {
- code.instSelect(PORTS, cpu, abi);
- dump(code, dump, DUMP_AFTER_INSTR_SELECT);
+ code.instSelect(cpu, abi, PORTS);
+ dump(code, dump, DUMP_AFTER_SELECT);
}
code.GCM();
@@ -276,6 +250,8 @@ loop: for (int i = 0; i < args.length; i++) {
code.encode();
dump(code, dump, DUMP_AFTER_ENCODE);
+
+ code.exportELF(base+".o");
}
dump(code, dump, DUMP_FINAL);
@@ -285,7 +261,7 @@ loop: for (int i = 0; i < args.length; i++) {
}
if (do_print_size) {
- System.out.println(String.format("Code Size: %d", code._encoding._bits.size()));
+ System.out.printf( "Code Size: %d%n", code._encoding._bits.size());
}
if (do_print_time) {
@@ -295,19 +271,19 @@ loop: for (int i = 0; i < args.length; i++) {
if (do_eval) {
// TODO: Support for evaluation of functions with different argument numbers and types
long t = System.currentTimeMillis();
- long arg = (first_arg < args.length) ? Integer.valueOf(args[first_arg]) : 0;
+ long arg = (i+1 < args.length) ? Integer.parseInt(args[i+1]) : 0;
System.out.println(Eval2.eval(code, arg, 100000));
if (do_print_time) {
- System.out.println(String.format("EXECUTION TIME: %.3f sec",
- (System.currentTimeMillis() - t) / 1e3));
- }
- } else if (do_run) {
- if (cpu != system_cpu || abi != system_abi) {
- System.err.println("ERROR: cannot run code on not native target");
- System.exit(1);
+ System.out.printf( "EXECUTION TIME: %.3f sec%n",
+ (System.currentTimeMillis() - t) / 1e3);
}
-
- throw Utils.TODO();
+ }
+ if (do_run) {
+ if ( !TestC.CPU_PORT.equals( cpu ) || !TestC.CALL_CONVENTION.equals( abi ) )
+ throw bad("cannot run code on not native target");
+ String exe = TestC.OS.startsWith("Windows") ? base+".exe" : base;
+ String result = com.seaofnodes.simple.TestC.gcc(base+".o", null, null, true, exe);
+ System.out.print(result);
}
}
}
diff --git a/src/test/java/com/seaofnodes/simple/TestC.java b/src/test/java/com/seaofnodes/simple/TestC.java
index 5609758b..93fa9b66 100644
--- a/src/test/java/com/seaofnodes/simple/TestC.java
+++ b/src/test/java/com/seaofnodes/simple/TestC.java
@@ -1,10 +1,10 @@
package com.seaofnodes.simple;
import com.seaofnodes.simple.codegen.CodeGen;
+import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
-import java.io.InputStream;
-import java.io.IOException;
+import java.util.concurrent.TimeUnit;
import static org.junit.Assert.*;
// Call a C driver program, which calls a Simple-generated .o, and self-checks.
@@ -16,8 +16,8 @@ public abstract class TestC {
public static final String CALL_CONVENTION = OS.startsWith("Windows") ? "Win64" : "SystemV";
public static final String CPU_PORT = switch( CPU ) {
- case "amd64" -> "x86_64_v2";
- default -> throw Utils.TODO("Map Yer CPU Port Here");
+ case "amd64" -> "x86_64_v2";
+ default -> throw Utils.TODO("Map Yer CPU Port Here");
};
public static void run( String file, int spills ) throws IOException { run(file,"",spills); }
@@ -35,48 +35,16 @@ public abstract class TestC {
// Compile and export Simple
String src = Files.readString(Path.of(sfile));
- _run(src,CALL_CONVENTION,"",cfile,efile,"S",expected,spills);
+ run(src,CALL_CONVENTION,"",cfile,efile,"S",expected,spills);
}
- static void _run( String src, String simple_conv, String c_conv, String cfile, String efile, String xtn, String expected, int spills ) throws IOException {
+ public static void run( String src, String simple_conv, String c_conv, String cfile, String efile, String xtn, String expected, int spills ) throws IOException {
String bin = efile+xtn;
String obj = bin+".o";
+ String exe = OS.startsWith("Windows") ? bin+".exe" : bin;
// Compile simple, emit ELF
CodeGen code = new CodeGen(src).driver( CPU_PORT, simple_conv, obj);
-
- // Compile the C program
- var params = new String[] {
- //if (USE_WSL) "wsl.exe";
- "gcc",
- cfile,
- obj,
- "-lm", // Picks up 'sqrt' for newtonFloat tests to compare
- "-g",
- "-o",
- bin,
- "-D",
- "CALL_CONV="+c_conv,
- };
- Process gcc = new ProcessBuilder(params).redirectErrorStream(true).start();
- byte error;
- try { error = (byte)gcc.waitFor(); } catch( InterruptedException e ) { throw new IOException("interrupted"); }
- String result = new String(gcc.getInputStream().readAllBytes());
- if( error!=0 ) {
- System.err.println("gcc error code: "+error);
- System.err.println(result);
- }
- assertEquals( 0, error );
- //assertTrue(result.isEmpty()); // No data in error stream
-
- // Execute results
- Process smp = new ProcessBuilder(bin).redirectErrorStream(true).start();
- try { error = (byte)smp.waitFor(); } catch( InterruptedException e ) { throw new IOException("interrupted"); }
- result = new String(smp.getInputStream().readAllBytes());
- if( error!=0 ) {
- System.err.println("exec error code: "+error);
- System.err.println(result);
- }
- assertEquals( 0, error );
+ String result = gcc(obj, c_conv, cfile, false, exe );
assertEquals(expected,result);
// Allocation quality not degraded
@@ -84,6 +52,55 @@ public abstract class TestC {
if( delta==0 ) delta = 1;
if( spills != -1 )
assertEquals("Expect spills:",spills,code._regAlloc._spillScaled,delta);
+ }
+
+ public static String gcc( String obj, String c_conv, String cfile, boolean stdin, String... args ) throws IOException {
+
+ // Compile the C program. Compiling code and constants in the low
+ // 2Gig. Pointers are 64b BUT since always in the low 2G all the
+ // high bits are zero - and Simple code can be emitted treating
+ // pointers as 4bytes.
+ var params = new Ary<>(String.class);
+ params.add("gcc");
+ if( cfile!=null ) params.add(cfile); // Associated C driver, usually has a `main`
+ params.addAll(new String[] {
+ obj,
+ "-lm", // Picks up 'sqrt' for newtonFloat tests to compare
+ "-g",
+ "-o",
+ args[0],
+ });
+ // Calling convention for C calls, if any
+ if( cfile!=null ) {
+ params.add("-D");
+ params.add("CALL_CONV="+c_conv);
+ }
+ // Run GCC to link (optionally compile C driver code)
+ Process gcc = new ProcessBuilder(params.asAry()).redirectErrorStream(true).start();
+ int exit;
+ try {
+ boolean normal = gcc.waitFor(2, TimeUnit.SECONDS);
+ exit = normal ? gcc.exitValue() : -1; // no exit???
+ } catch( InterruptedException e ) {
+ throw new IOException("interrupted");
+ }
+ String result = new String(gcc.getInputStream().readAllBytes());
+ if( exit!=0 ) {
+ System.err.println("gcc error code: "+exit);
+ System.err.println(result);
+ }
+ assertEquals( 0, exit );
+ //assertTrue(result.isEmpty()); // No data in error stream
+
+ // Execute results
+ ProcessBuilder smp = new ProcessBuilder(args);
+ if( stdin ) smp.redirectInput(ProcessBuilder.Redirect.INHERIT);
+ Process p = smp.start();
+ try { exit = (byte)p.waitFor(); } catch( InterruptedException e ) { throw new IOException("interrupted"); }
+ result = new String(p.getInputStream().readAllBytes());
+ if( exit!=0 )
+ System.err.println("exec exit code: "+exit);
+ return result;
}
}
diff --git a/src/test/java/com/seaofnodes/simple/TypeTest.java b/src/test/java/com/seaofnodes/simple/TypeTest.java
index d94f5b5a..660f984a 100644
--- a/src/test/java/com/seaofnodes/simple/TypeTest.java
+++ b/src/test/java/com/seaofnodes/simple/TypeTest.java
@@ -20,9 +20,9 @@ public class TypeTest {
TypeStruct s2 = TypeStruct.make("s2", new Field[]{
Field.make("a", TypeInteger.BOT,-3, false),
Field.make("b", TypeInteger.BOT,-4, false) });
- Assert.assertEquals(s1.makeRO(), s1.glb());
+ Assert.assertEquals(s1.makeRO(), s1.glb(false));
Assert.assertNotEquals(s1, s1.dual());
- Assert.assertEquals(s1.makeRO(), s1.dual().glb());
+ Assert.assertEquals(s1.makeRO(), s1.dual().glb(false));
TypeMem m1 = TypeMem.make(1,TypeNil.NIL);
TypeMem m2 = TypeMem.make(2,TypeInteger.U16);
@@ -38,9 +38,9 @@ public class TypeTest {
Assert.assertEquals(TypeMem.make(0,Type.BOTTOM), m1.meet(m3));
Assert.assertEquals(TypeMem .BOT, m3.meet(m4));
- Assert.assertEquals(TypeMem.make(1,Type.BOTTOM), m1.glb());
+ Assert.assertEquals(TypeMem.make(1,Type.BOTTOM), m1.glb(false));
Assert.assertEquals(TypeMem.make(1,Type.XNIL), m1.dual());
- Assert.assertEquals(m4.dual(), m4.glb().dual());
+ Assert.assertEquals(m4.dual(), m4.glb(false).dual());
TypeMemPtr ptr1 = TypeMemPtr.make(s1);
Assert.assertEquals(s1, ptr1._obj);
@@ -55,13 +55,13 @@ public class TypeTest {
Assert.assertEquals(s2, ptr2nil._obj);
Assert.assertNotEquals(ptr1, ptr2);
- Assert.assertNotEquals(ptr1, ptr1.glb());
- Assert.assertEquals(ptr1nil.makeRO(), ptr1.glb());
+ Assert.assertNotEquals(ptr1, ptr1.glb(false));
+ Assert.assertEquals(ptr1nil.makeRO(), ptr1.glb(false));
Assert.assertEquals(ptr1, ptr1.dual().dual());
- Assert.assertEquals(ptr1.glb().makeRO(), ptr1.dual().glb());
+ Assert.assertEquals(ptr1.glb(false).makeRO(), ptr1.dual().glb(false));
Assert.assertEquals(TypeMemPtr.makeNullable(TypeStruct.BOT), ptr1.meet(ptr2nil));
- Assert.assertEquals(ptr1.glb(), ptr1.meet(TypeNil.NIL).makeRO());
+ Assert.assertEquals(ptr1.glb(false), ptr1.meet(TypeNil.NIL).makeRO());
TypeMemPtr TOP = TypeMemPtr.TOP;
TypeMemPtr BOT = TypeMemPtr.makeNullable(TypeStruct.BOT);
diff --git a/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java b/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java
index f119fa1a..4f86d0be 100644
--- a/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java
+++ b/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java
@@ -97,7 +97,7 @@ public class ScriptGenerator {
private static final List<TypeInt> INTTYPES = new ArrayList<>();
static {
- for( var e : Parser.defaultTypes().entrySet() ) {
+ for( var e : Parser.TYPES.entrySet() ) {
if( e.getValue() instanceof TypeInteger ) {
INTTYPES.add(new TypeInt(e.getKey()));
}
diff --git a/src/test/java/com/seaofnodes/simple/progs/echo.smp b/src/test/java/com/seaofnodes/simple/progs/echo.smp
new file mode 100644
index 00000000..8eafd73b
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/progs/echo.smp
@@ -0,0 +1,3 @@
+// -*- mode: java; -*-
+// Echo stdin to stdout.
+return sys.io.p( sys.io.stdin() );
diff --git a/src/test/java/com/seaofnodes/simple/progs/helloWorld.smp b/src/test/java/com/seaofnodes/simple/progs/helloWorld.smp
new file mode 100644
index 00000000..07ff935f
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/progs/helloWorld.smp
@@ -0,0 +1,3 @@
+// -*- mode: java; -*-
+sys.io.p("Hello, World!");
+return 0;
diff --git a/src/test/java/com/seaofnodes/simple/progs/jig.smp b/src/test/java/com/seaofnodes/simple/progs/jig.smp
index 1daeee09..fd3e7b9c 100644
--- a/src/test/java/com/seaofnodes/simple/progs/jig.smp
+++ b/src/test/java/com/seaofnodes/simple/progs/jig.smp
@@ -1,2 +1,6 @@
// -*- mode: java; -*-
+// Should fold away sign extend
+struct Person { i8 age;};
+Person !p = new Person;
+p.age = (arg<<48)>>48;
return 0;
diff --git a/src/test/java/com/seaofnodes/simple/progs/person.smp b/src/test/java/com/seaofnodes/simple/progs/person.smp
index 9f0f5bea..507fb83b 100644
--- a/src/test/java/com/seaofnodes/simple/progs/person.smp
+++ b/src/test/java/com/seaofnodes/simple/progs/person.smp
@@ -1,6 +1,6 @@
// -*-java-*-
struct Person {
- int age;
+ i32 age;
};
val fcn = { Person?[] ps, int x ->
diff --git a/src/test/java/com/seaofnodes/simple/progs/riscv_load.smp b/src/test/java/com/seaofnodes/simple/progs/riscv_load.smp
new file mode 100644
index 00000000..67369feb
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/progs/riscv_load.smp
@@ -0,0 +1,4 @@
+struct Person { i32 age;};
+Person !p = new Person;
+p.age += arg;
+return p.age;
diff --git a/src/test/java/com/seaofnodes/simple/progs/sext_str_1.smp b/src/test/java/com/seaofnodes/simple/progs/sext_str_1.smp
new file mode 100644
index 00000000..cfb3fbcc
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/progs/sext_str_1.smp
@@ -0,0 +1,4 @@
+struct Person {i64 age;}
+Person !p = new Person;
+p.age += arg;
+return p.age;
diff --git a/src/test/java/com/seaofnodes/simple/progs/sext_str_fold_away.smp b/src/test/java/com/seaofnodes/simple/progs/sext_str_fold_away.smp
new file mode 100644
index 00000000..0cf70392
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/progs/sext_str_fold_away.smp
@@ -0,0 +1,5 @@
+// Should fold away sign extend
+struct Person { i8 age;};
+Person !p = new Person;
+p.age = (arg<<48)>>48;
+return 0; \ No newline at end of file
diff --git a/src/test/java/com/seaofnodes/simple/progs/sext_str_not_fold_away.smp b/src/test/java/com/seaofnodes/simple/progs/sext_str_not_fold_away.smp
new file mode 100644
index 00000000..48cc6409
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/progs/sext_str_not_fold_away.smp
@@ -0,0 +1,4 @@
+struct Person { i32 age;};
+Person !p = new Person;
+p.age = (arg<<17)>>17;
+return 0; \ No newline at end of file
diff --git a/src/test/java/com/seaofnodes/simple/progs/sext_str_not_fold_away_2.smp b/src/test/java/com/seaofnodes/simple/progs/sext_str_not_fold_away_2.smp
new file mode 100644
index 00000000..3120429b
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/progs/sext_str_not_fold_away_2.smp
@@ -0,0 +1,4 @@
+struct Person { i32 age;};
+Person !p = new Person;
+p.age = (arg<<48)>>48;
+return 0; \ No newline at end of file
diff --git a/src/test/java/com/seaofnodes/simple/progs/store_peep.smp b/src/test/java/com/seaofnodes/simple/progs/store_peep.smp
new file mode 100644
index 00000000..c3feddd5
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/progs/store_peep.smp
@@ -0,0 +1,3 @@
+i64 a = 123456789012345;
+i32 b = a; // truncate high order bits
+return b; \ No newline at end of file