summaryrefslogtreecommitdiff
path: root/chapter25/src/main/java/com
diff options
context:
space:
mode:
authorCliff Click <cliffc@acm.org>2026-09-28 13:30:22 -0700
committerCliff Click <cliffc@acm.org>2026-09-28 13:30:22 -0700
commit586c8f65f2d0c0e1f73aadd0aa8d850aa653bddd (patch)
tree2f539c308452ddf5fa37826da95b17ba557e8352 /chapter25/src/main/java/com
parent8212a66ff27e56531e0b48807088f1c544d75756 (diff)
downloadSimple-586c8f65f2d0c0e1f73aadd0aa8d850aa653bddd.tar.gz
Simple-586c8f65f2d0c0e1f73aadd0aa8d850aa653bddd.zip
Support extern C ver direct updates
FIx minor RISC encodings.
Diffstat (limited to 'chapter25/src/main/java/com')
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/Parser.java59
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/Var.java1
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java13
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/codegen/ElfWriter.java8
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/codegen/Encoding.java11
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/node/ConFldOffNode.java3
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/node/ExternOffsetNode.java19
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/node/LoadNode.java10
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/node/MemOpNode.java2
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/node/Node.java3
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/TMPARM.java4
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/TMPRISC.java9
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TMPX86.java5
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/type/Field.java46
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/type/TypeStruct.java9
15 files changed, 151 insertions, 51 deletions
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/Parser.java b/chapter25/src/main/java/com/seaofnodes/simple/Parser.java
index af5af514..19894357 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/Parser.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/Parser.java
@@ -346,9 +346,11 @@ public class Parser {
Var v = _scope.var(lex + nvar++);
// Field with sharper type
Field old = tself.field(v._name);
- Field fld = old == null
+ Field fld = v._extern!=0
+ ? Field.make(v._name,v.type(),v._extern,false,true)
+ : old == null
? Field.make(v._name,v.type(),_code.alias(_ref._cname),v._final)
- : Field.make(v._name,v.type(), old._alias, old._final);
+ : old.makeFrom(v.type());
tself = tself.addOrUpdate(fld);
}
TYPES.put(typeName,tself);
@@ -426,7 +428,7 @@ public class Parser {
if( vidx == -1 )
continue;
Var v = _scope.var(vidx);
- if( v._fref ) continue; // Forward refs not declared here
+ if( v._fref || v._extern!=0 ) continue; // C storage has no Simple initializer
// NewNode's TOP field is the zero-code poison for a required
// field. Leave it alone unless the declaration body supplied a
// value; otherwise a user constructor must initialize it.
@@ -970,6 +972,9 @@ public class Parser {
if( def==null )
throw error("Undefined name '" + name + "'");
+ if( def._extern!=0 ) // Parse the C binding as a memory lvalue
+ { pos(old); return parseExpression(); }
+
// TOP fields are for late-initialized fields; these have never
// been written to, and this must be the final write. Other writes
// outside the constructor need to check the final bit.
@@ -1101,6 +1106,14 @@ public class Parser {
Node expr;
if( match("=") ) {
if( isExternDecl() ) {
+ if( !(t instanceof TypeFunPtr) ) {
+ Node ptr = externData(name,t);
+ // The lexical binding is a fixed address; its contents are mutable.
+ if( !_scope.define(name,t,true,ptr,loc) )
+ throw error("Redefining name '"+name+"'",loc);
+ _scope.lookup(name)._extern = _code.externAlias(name);
+ return _code.ZERO;
+ }
expr = externDecl(name,t);
t = expr._type; // Upgrade declared type to the exact extern decl type
} else if( isConstructorDecl(name) ) {
@@ -1280,7 +1293,7 @@ public class Parser {
priv.keep();
for( Field field : ts._fields ) {
Type storage = field._t.makeStorage();
- Field escaped = field._final ? field : Field.make(field._fname,storage,field._alias,true);
+ Field escaped = field._final ? field : field.makeFrom(storage).makeFrom(true);
pub.keep();
Node esc = new EscapeNode(escaped,self,priv,pub).peephole();
MemMergeNode merge = new MemMergeNode(null,pub);
@@ -1879,6 +1892,8 @@ public class Parser {
if( name!=null ) {
Var n = _scope.lookup(name);
if( n != null && !(n.type() instanceof TypeMemPtr) ) {
+ if( n._extern!=0 )
+ return parsePostfixName(_scope.in(n),name,delta);
if( n._final )
throw error("Cannot reassign final '"+n._name+"'");
Node expr = zsMask(peep(new AddNode(_scope.in(n),con(delta))),n.type());
@@ -1963,6 +1978,8 @@ public class Parser {
// Load local value
Node rvalue = _scope.in(var);
+ if( var._extern!=0 )
+ return parsePostfixName(rvalue,id,0);
if( var._fref )
return parsePostfix(rvalue);
// Required constructor fields begin as their declared pointer type
@@ -1980,7 +1997,7 @@ public class Parser {
Kind fk = _scope._kinds.at(fx);
// Access instance field from 'self'
if( kx+1 == fx && kk instanceof Kind.Func inst && FunNode.isInstance(inst._name) && fk instanceof Kind.Func method )
- return parsePostfixName(_scope.in(method._lexSize),id);
+ return parsePostfixName(_scope.in(method._lexSize),id,0);
// Check for a function-escaping variable; these require true
// closures. Final constants are OK; final vars require a hidden var
@@ -1998,7 +2015,7 @@ public class Parser {
// Get the most recent sharpen global
TypeStruct clz = (TypeStruct) TYPES.get( ((TypeMemPtr) clzptr.type())._obj._name );
TypeMemPtr clztmp = TypeMemPtr.make( (byte) 2, clz, true );
- return parsePostfixName( con( clztmp ), id );
+ return parsePostfixName( con( clztmp ), id,0 );
//return parsePostfixName(_scope.in(2),id);
}
}
@@ -2206,7 +2223,7 @@ public class Parser {
else if( match("(") ) return parsePostfix(require(functionCall(expr,defaultSelf()),")"));
else return expr; // No postfix
- return parsePostfixName(expr,name);
+ return parsePostfixName(expr,name,0);
}
private Node defaultSelf() {
@@ -2225,7 +2242,8 @@ public class Parser {
* </pre>
*/
- private Node parsePostfixName(Node expr, String name) {
+ // pre is +/-1 for a prefix update, otherwise parse a postfix operation.
+ private Node parsePostfixName(Node expr, String name, int pre) {
// Keep expr across possible updates
expr.keep();
@@ -2278,7 +2296,7 @@ public class Parser {
: fldoff(expr,name)).keep();
// Disambiguate "obj.fld==x" boolean test from "obj.fld=x" field assignment
- if( matchOpx('=','=') ) {
+ if( pre==0 && matchOpx('=','=') ) {
// Field assignment
Node val = parseAsgn().keep();
// Lift value for store
@@ -2306,20 +2324,20 @@ public class Parser {
Node load = peep(new LoadNode(loc(),name, alias, ctrl(), mem, expr, off));
// Check for assign-update, "ptr.fld += expr" or "ary[idx]++"
- char ch = _lexer.matchOperAssign();
+ char ch = pre==0 ? _lexer.matchOperAssign() : (char)pre;
if( ch!=0 ) {
if( fld!=null && decl==Type.BOTTOM )
throw error( "'" + ts._name + "' is not fully initialized, field '" + fld._fname + "' needs to be set in a constructor" );
- Node op = opAssign(ch,load, decl );
+ Node op = opAssign(ch,load, decl ).keep();
mem.keep();
Node st = new StoreNode(loc(), name, alias, decl, ctrl(), mem, expr.unkeep(), off.unkeep(), op, false).peephole().keep();
storeMem(st,alias,mem);
st.unkeep();
mem.unkeep();
- load = postfix(ch) ? load.unkeep() : op;
- // And use the original loaded value as the result
- return load;
+ op.unkeep(); // A narrow Store may have folded away the expression's conversion.
+ if( postfix(ch) ) load.unkeep();
+ return pre==0 && postfix(ch) ? load : op;
}
off.unkill();
// Might be a method call, so pass expr as 'self'
@@ -2487,6 +2505,19 @@ public class Parser {
return (ExternNode)(new ExternNode(t,ex).peephole());
}
+ // A lexical C binding has a singleton namespace with one external field.
+ // The namespace occupies no storage; its field offset supplies the address.
+ private Node externData(String name, Type t) {
+ if( !(t instanceof TypeInteger) && !(t instanceof TypeFloat) )
+ throw error("C data binding requires an integer or float type");
+ String cname = ("C:"+name).intern();
+ TypeStruct ts = TypeStruct.make(cname,false,Field.make(name,t,_code.externAlias(name),false,true));
+ Type old = TYPES.putIfAbsent(cname,ts);
+ if( old!=null && old!=ts )
+ throw error("Conflicting types for C data '"+name+"'");
+ return con(TypeMemPtr.make((byte)2,ts,true));
+ }
+
/**
* Parse integer literal
*
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/Var.java b/chapter25/src/main/java/com/seaofnodes/simple/Var.java
index 1906ce15..af371d21 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/Var.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/Var.java
@@ -19,6 +19,7 @@ public class Var {
// Parser-only definite-initialization marker. The current binding's
// nullable type carries the path-sensitive state through SSA merges.
public boolean _uninit;
+ public int _extern; // C storage alias; zero for ordinary variables
public Parser.Lexer _loc; // Source location
public Var(int idx, String name, Type type, boolean xfinal, Parser.Lexer loc ) {
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java b/chapter25/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java
index 0f38d3ff..86e83372 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java
@@ -321,6 +321,12 @@ public class CodeGen {
public final GlobalBits _aliases = new GlobalBits();
public int alias(String clz) { return _aliases.next(clz); }
+ // C storage has one global alias per symbol, including across object files.
+ public int externAlias(String name) { return _aliases.next("C:"+name,GlobalBits.RESERVED); }
+
+ // External data addresses supplied by an in-memory linker (image offsets).
+ public final HashMap<String,Integer> _externDataAddresses = new HashMap<>();
+
// Compute local function index (FIDX) from global function info. This is
// called *in order* during parsing, and that order is part of the global
// unique mapping
@@ -740,6 +746,13 @@ public class CodeGen {
}
// Produce a machine node from n; map it to flag as done so stops cycles.
+ if( n instanceof MemOpNode mem && mem.off() instanceof ExternOffsetNode off ) {
+ // External fields replace base+offset with symbol+0. Keep the ideal
+ // declaring type on the address for machine load/store width selection.
+ TypeMemPtr ptr = (TypeMemPtr)mem.ptr()._type;
+ n._inputs.set(2,new ExternNode(TypeMemPtr.make((byte)2,ptr._obj,true),off._extern));
+ n._inputs.set(3,ConstantNode.raw(TypeInteger.ZERO));
+ }
map.put(n, x=_mach.instSelect(n) );
// Carry loop-tree and pre-order info across the ideal->mach transition
if( n instanceof CFGNode ncfg && x instanceof CFGNode xcfg ) {
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/codegen/ElfWriter.java b/chapter25/src/main/java/com/seaofnodes/simple/codegen/ElfWriter.java
index a74303e6..7423edbd 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/codegen/ElfWriter.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/codegen/ElfWriter.java
@@ -316,6 +316,14 @@ public class ElfWriter {
relocations.writeReloOffPlus(offset, symidx, (byte)4/*PLT32*/);
}
+ // Native ELF output is AMD64. Data addresses use PC32, not PLT calls.
+ for( Node n : enc._externData.keySet() ) {
+ String extern = enc._externData.get(n);
+ int symidx = symbols.symbol(extern, 0, SYM_BIND_GLOBAL, SYM_TYPE_OBJECT);
+ int offset = enc.opStart(n) + enc.opLen(n) - 4;
+ relocations.writeReloOffPlus(offset, symidx, (byte)2/*PC32*/);
+ }
+
// Write relocations for the constant pool
String cpoolPrefix = _code.entryClinitName().replaceAll("[^A-Za-z0-9_$]", "_") + "$CPOOL$";
for( Encoding.Relo relo : enc._bigCons.values() ) {
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/codegen/Encoding.java b/chapter25/src/main/java/com/seaofnodes/simple/codegen/Encoding.java
index 618ec689..215fd694 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/codegen/Encoding.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/codegen/Encoding.java
@@ -187,6 +187,9 @@ public class Encoding {
return this;
}
+ public final HashMap<Node,String> _externData = new HashMap<>();
+ public void externalData(Node n, String name) { _externData.put(n,name); }
+
// Store t as a 32/64 bit constant in the code space; generate RIP-relative
// addressing to load it. Type is stored in either the .rodata or .data.
public void largeConstant( Node relo, Type t, int off, int elf ) {
@@ -581,6 +584,7 @@ public class Encoding {
int off=0; // offset in the struct
for( int fn=0; fn<ts._fields.length; fn++ ) {
Field f = ts._fields[layout[fn]];
+ if( f._extern ) continue;
int foff = ts. offset(layout[fn]);
// Pad up to field
while( off < foff ) { bits.write(0); off++; };
@@ -604,6 +608,13 @@ public class Encoding {
public static int SENTINEL_WRITE = -8;
void patchGlobalRelocations() {
+ for( Node src : _externData.keySet() ) {
+ String name = _externData.get(src);
+ Integer target = _code._externDataAddresses.get(name);
+ if( target==null ) throw new IllegalArgumentException("Unresolved C data symbol '"+name+"'");
+ int start = opStart(src);
+ ((RIPRelSize)src).patch(this,start,opLen(src),target-start);
+ }
for( Node src : _externals.keySet() ) {
int start = opStart(src);
String dst = _externals.get(src);
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/ConFldOffNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/ConFldOffNode.java
index c7ee1359..468721a4 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/node/ConFldOffNode.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/node/ConFldOffNode.java
@@ -52,6 +52,9 @@ public class ConFldOffNode extends ConstantNode {
public Node asOffset() {
int fldx = _fname==" len" ? _ts._fields.length : _ts.find(_fname);
if( fldx == -1 ) return null;
+ // " len" selects the terminal size slot, not a Field.
+ if( fldx < _ts._fields.length && _ts._fields[fldx]._extern )
+ return new ExternOffsetNode(_fname).peephole();
return ConstantNode.make(TypeInteger.constant(_ts.offset(fldx))).peephole();
}
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/ExternOffsetNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/ExternOffsetNode.java
new file mode 100644
index 00000000..409ce5d1
--- /dev/null
+++ b/chapter25/src/main/java/com/seaofnodes/simple/node/ExternOffsetNode.java
@@ -0,0 +1,19 @@
+package com.seaofnodes.simple.node;
+
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeInteger;
+import com.seaofnodes.simple.util.BAOS;
+import java.util.HashMap;
+import java.util.IdentityHashMap;
+
+// A field address supplied by the C linker, not a displacement from the base.
+// The class base remains available for ideal type/alias analysis. Instruction
+// selection replaces it with this symbol's address and a zero displacement.
+public class ExternOffsetNode extends ExternNode {
+ public ExternOffsetNode(String name) { super(TypeInteger.BOT,name); }
+ @Override public Tag serialTag() { return Tag.ExternOffset; }
+ @Override public Node copy() { return new ExternOffsetNode(_extern); }
+ @Override public void packed(BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types, IdentityHashMap<Node,Integer> nodes) {
+ baos.packed2(strs.get(_extern));
+ }
+}
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/LoadNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/LoadNode.java
index bd80a74b..193fbe17 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/node/LoadNode.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/node/LoadNode.java
@@ -92,8 +92,7 @@ public class LoadNode extends MemOpNode {
// Loads into structs do not need a ctrl edge, as null-ptr checking is
// baked into the type system. Loads into arrays DO need the ctrl
// edge, at least until proper range-checking is in place.
- if( in(0)!=null && ptr._type instanceof TypeMemPtr tmp &&
- !tmp._obj.isAry() ) {
+ if( in(0)!=null && ptr._type instanceof TypeMemPtr tmp && !tmp._obj.isAry() ) {
setDef(0,null);
return this;
}
@@ -113,6 +112,10 @@ public class LoadNode extends MemOpNode {
if( _alias == 1 )
return null;
+ // External storage is shared even through different class namespaces.
+ boolean external = ptr._type instanceof TypeMemPtr tmp &&
+ (fld=tmp._obj.field(_name)) != null && fld._extern;
+
// Simple Load-after-Store on same address.
if( mem instanceof StoreNode st &&
ptr == st.nnptr() &&
@@ -154,7 +157,7 @@ public class LoadNode extends MemOpNode {
return extend(castRO(st.val())); // Proved equal
// Can we prove unequal? Offsets do not overlap?
if( !off()._type.join(st.off()._type).isHigh() && // Offsets overlap
- !neverAlias(ptr,st.ptr()) ) { // And might alias
+ (external || !neverAlias(ptr,st.ptr())) ) { // And might alias
addDep( off()); // Offsets can fold, proving unequal
addDep(st.off()); // Offsets can fold, proving unequal
break outer; // Cannot tell, stop trying
@@ -175,6 +178,7 @@ public class LoadNode extends MemOpNode {
case ProjNode mproj: // Memory projection
switch( mproj.in(0) ) {
case NewNode nnn1:
+ if( external ) break outer; // Class allocation does not initialize C storage
// Direct load from fresh zero/default-filled allocation.
Type decl = declaredType();
assert decl!=Type.BOTTOM;
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/MemOpNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/MemOpNode.java
index 083889c5..441edc3e 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/node/MemOpNode.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/node/MemOpNode.java
@@ -99,7 +99,7 @@ public abstract class MemOpNode extends TypeNode {
@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 _con.log_size(); }
+
@Override
public boolean eq(Node n) {
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/Node.java b/chapter25/src/main/java/com/seaofnodes/simple/node/Node.java
index e77b0c0a..8ca4662b 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/node/Node.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/node/Node.java
@@ -84,7 +84,7 @@ public abstract class Node implements Cloneable {
New,Never,Not,Or,Parm,Phi,Proj,
ReadOnly,Return,Region,RoundF32,
Sar,Shl,Shr,Start,Stop,Store,Sub,ToFloat,
- XCtrl,Xor,StartCU,StopCU,Guard,MemPhi,BulkMemPhi,PtrToInt;
+ XCtrl,Xor,StartCU,StopCU,Guard,MemPhi,BulkMemPhi,PtrToInt,ExternOffset;
public static final Tag[] VALS = values();
public Node make( BAOS bais, String[] strs, Type[] types, GlobalBits fileAliases, GlobalBits aliases ) {
return switch(this) {
@@ -124,6 +124,7 @@ public abstract class Node implements Cloneable {
case Con -> ConstantNode.make(bais ,types);
case Escape-> EscapeNode.make(bais ,types);
case Extern-> ExternNode.make(bais,strs,types);
+ case ExternOffset-> new ExternOffsetNode(strs[bais.packed2()]);
case FunPtr-> FunPtrNode.make(bais, types);
case Fun -> FunNode.make(bais,strs,types);
case Load -> new LoadNode(bais,strs,types,fileAliases,aliases);
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/TMPARM.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/TMPARM.java
index f86d85dc..8209eb4b 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/TMPARM.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/TMPARM.java
@@ -17,8 +17,8 @@ public class TMPARM extends ConstantNode implements MachNode, RIPRelSize {
@Override public boolean isClone() { return true; }
@Override public TMPARM copy() { return new TMPARM(this,_ext); }
@Override public void encoding( Encoding enc ) {
- if( _ext!=null ) throw Utils.TODO();
- enc.largeConstant(this,((TypeMemPtr)_con)._obj,0,-1);
+ if( _ext!=null ) enc.externalData(this,_ext);
+ else 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));
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/TMPRISC.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/TMPRISC.java
index aa001e1e..45c3eed2 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/TMPRISC.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/TMPRISC.java
@@ -6,7 +6,6 @@ import com.seaofnodes.simple.node.MachNode;
import com.seaofnodes.simple.node.Node;
import com.seaofnodes.simple.type.TypeMemPtr;
import com.seaofnodes.simple.util.SB;
-import com.seaofnodes.simple.util.Utils;
public class TMPRISC extends ConstantNode implements MachNode, RIPRelSize {
final String _ext;
@@ -17,8 +16,8 @@ public class TMPRISC extends ConstantNode implements MachNode, RIPRelSize {
@Override public boolean isClone() { return true; }
@Override public TMPRISC copy() { return new TMPRISC(this,_ext); }
@Override public void encoding( Encoding enc ) {
- if( _ext!=null ) throw Utils.TODO();
- enc.largeConstant(this,((TypeMemPtr)_con)._obj,0,-1);
+ if( _ext!=null ) enc.externalData(this,_ext);
+ else 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));
@@ -32,8 +31,8 @@ public class TMPRISC extends ConstantNode implements MachNode, RIPRelSize {
@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 , riscv.u_type(riscv.OP_AUIPC, dst, (delta+0x800)>>12));
+ // ADDI's low 12 bits are signed; round the AUIPC page accordingly.
enc.patch4(opStart+4, riscv.i_type(riscv.OP_IMM, dst, 0, dst, delta & 0xFFF));
}
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TMPX86.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TMPX86.java
index cc8741b3..fbd79dd7 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TMPX86.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TMPX86.java
@@ -4,7 +4,6 @@ import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.TypeMemPtr;
import com.seaofnodes.simple.util.SB;
-import com.seaofnodes.simple.util.Utils;
public class TMPX86 extends ConstantNode implements MachNode, RIPRelSize{
private byte _opLen;
@@ -17,12 +16,12 @@ public class TMPX86 extends ConstantNode implements MachNode, RIPRelSize{
@Override public Node copy() { return new TMPX86(this,_ext); }
@Override public void encoding( Encoding enc ) {
- if( _ext!=null ) throw Utils.TODO();
short dst = enc.reg(this);
// 0 or 1 for REX depending on the dst.
_opLen = (byte)(6+ 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, _opLen-4, 2/*ELF encoding PC32*/);
+ if( _ext!=null ) enc.externalData(this,_ext);
+ else enc.largeConstant(this,((TypeMemPtr)_con)._obj, _opLen-4, 2/*ELF encoding PC32*/);
}
// Delta is from opcode start
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/type/Field.java b/chapter25/src/main/java/com/seaofnodes/simple/type/Field.java
index ed505b19..beaa719c 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/type/Field.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/type/Field.java
@@ -1,7 +1,6 @@
package com.seaofnodes.simple.type;
import com.seaofnodes.simple.util.Ary;
-import com.seaofnodes.simple.util.AryInt;
import com.seaofnodes.simple.util.BAOS;
import com.seaofnodes.simple.util.SB;
import java.util.*;
@@ -22,23 +21,26 @@ public class Field extends Type {
public int _alias;
// Field is only written in the constructor
public boolean _final;
+ // Native C storage, outside the containing object's layout.
+ public boolean _extern;
private static final Ary<Field> FREE = new Ary<>(Field.class);
- private Field(String fname, Type type, int alias, boolean xfinal ) { super(TFLD); init(fname,type,alias,xfinal); }
- private Field init(String fname, Type type, int alias, boolean xfinal ) {
+ private Field(String fname, Type type, int alias, boolean xfinal, boolean external ) { super(TFLD); init(fname,type,alias,xfinal,external); }
+ private Field init(String fname, Type type, int alias, boolean xfinal, boolean external ) {
_fname = fname;
_t = type;
_alias = alias;
_final = xfinal;
+ _extern = external;
return this;
}
// Return a filled-in Field; either from free list or alloc new.
- private static Field malloc(String fname, Type type, int alias, boolean xfinal ) {
- return FREE.isEmpty() ? new Field(fname,type,alias,xfinal) : FREE.pop().init(fname,type,alias,xfinal);
+ private static Field malloc(String fname, Type type, int alias, boolean xfinal, boolean external ) {
+ return FREE.isEmpty() ? new Field(fname,type,alias,xfinal,external) : FREE.pop().init(fname,type,alias,xfinal,external);
}
// Malloc-from
- public Field malloc( ) { return malloc(_fname,null,_alias,_final); }
+ public Field malloc( ) { return malloc(_fname,null,_alias,_final,_extern); }
@Override Field free(Type t) {
Field f = (Field)t;
assert !f.isFree() && !f._terned;
@@ -52,16 +54,19 @@ public class Field extends Type {
// Make and intern with listed fields
public static Field make( String fname, Type type, int alias, boolean xfinal ) {
- Field f = malloc(fname,type,alias,xfinal);
+ return make(fname,type,alias,xfinal,false);
+ }
+ public static Field make( String fname, Type type, int alias, boolean xfinal, boolean external ) {
+ Field f = malloc(fname,type,alias,xfinal,external);
Field f2 = f.intern();
if( f2==f ) return f;
return VISIT.isEmpty() ? f2.free(f) : f2.delayFree(f);
}
public Field makeFrom( Type type ) {
- return type == _t ? this : make(_fname,type,_alias,_final);
+ return type == _t ? this : make(_fname,type,_alias,_final,_extern);
}
public Field makeFrom( boolean xfinal ) {
- return xfinal == _final ? this : make(_fname,_t,_alias,xfinal);
+ return xfinal == _final ? this : make(_fname,_t,_alias,xfinal,_extern);
}
// Cycle-making-breaking
@@ -71,7 +76,8 @@ public class Field extends Type {
public static final Field TEST = make("test",Type.NIL,-2,false);
public static final Field TEST2 = make("test",Type.NIL,-2,true);
public static final Field FLT = make("flt ",TypeFloat.F32,-2,false);
- public static void gather(ArrayList<Type> ts) { ts.add(TEST); ts.add(TEST2); ts.add(FLT); }
+ public static final Field EXT = make("test",TypeInteger.I32,-2,false,true);
+ public static void gather(ArrayList<Type> ts) { ts.add(TEST); ts.add(TEST2); ts.add(FLT); ts.add(EXT); }
@Override Field xmeet( Type that ) {
Field fld = (Field)that; // Invariant
@@ -80,16 +86,17 @@ public class Field extends Type {
if( fld ==BOT.dual() ) return this;
if( _fname!=fld._fname ) { assert !_fname.equals(fld._fname); return BOT; }
assert _alias==fld._alias;
- return make(_fname, _t.meet(fld._t ), _alias, _final | fld._final);
+ if( _extern!=fld._extern ) return BOT;
+ return make(_fname, _t.meet(fld._t ), _alias, _final | fld._final,_extern);
}
@Override
- Field xdual() { return malloc(_fname, _t.dual(),_alias,!_final); }
+ Field xdual() { return malloc(_fname, _t.dual(),_alias,!_final,_extern); }
@Override Field rdual() {
if( _dual!=null ) return dual();
assert !_terned;
- Field d = malloc(_fname,null,_alias,!_final);
+ Field d = malloc(_fname,null,_alias,!_final,_extern);
(_dual = d)._dual = this; // Cross link duals
d._t = _t._terned ? _t.dual() : _t.rdual();
return d;
@@ -97,7 +104,7 @@ public class Field extends Type {
@Override boolean _isConstant() { return _t._isConstant(); }
@Override boolean _isFinal() { return _final && _t._isFinal(); }
- @Override Field _makeRO() { return _final ? this : make(_fname, _t._makeRO(),_alias,true); }
+ @Override Field _makeRO() { return _final ? this : make(_fname, _t._makeRO(),_alias,true,_extern); }
@Override Field _close( String name, HashMap<String, Type> TYPES ) { return makeFrom( _t._close(name, TYPES )); }
@Override Type _upgradeType(HashMap<String,Type> TYPES) {
@@ -105,10 +112,10 @@ public class Field extends Type {
}
// Override in subclasses
- int hash() { return _fname.hashCode() ^ _t.hashCode() ^ _alias ^ (_final ? 1024 : 0); }
+ int hash() { return _fname.hashCode() ^ _t.hashCode() ^ _alias ^ (_final ? 1024 : 0) ^ (_extern ? 2048 : 0); }
private boolean static_eq( Field f ) {
- return _fname.equals(f._fname) && _alias==f._alias && _final==f._final && _t !=null && f._t !=null;
+ return _fname.equals(f._fname) && _alias==f._alias && _final==f._final && _extern==f._extern && _t !=null && f._t !=null;
}
@Override boolean eq( Type t ) {
Field f = (Field)t;
@@ -123,22 +130,23 @@ public class Field extends Type {
@Override public int nkids() { return 1; }
@Override public Type at( int idx ) { return _t; }
@Override public void set( int idx, Type t ) { _t = t; }
- // Tags: final/!final; +alias+name
+ // Tags: final/!final; +alias+name+external-storage flag
@Override int TAGOFF() { return 2; }
@Override public void packed( BAOS baos, HashMap<String,Integer> strs ) {
baos.write(TAGOFFS[_type] + (_final ? 1 : 0));
baos.packed2(_alias);
baos.packed2(strs.get(_fname));
+ baos.write(_extern ? 1 : 0);
}
static Field packed( int tag, BAOS bais, String[] strs ) {
int alias = bais.packed2();
String fname = strs[bais.packed2()];
- return malloc(fname,null,alias,(tag&1)==1);
+ return malloc(fname,null,alias,(tag&1)==1,bais.read()!=0);
}
@Override
public SB _print( SB sb, BitSet visit, boolean html ) {
- sb.p(_final?"":"!").p(_fname).p(":").p(_alias).p(" : ");
+ sb.p(_extern ? "extern C " : "").p(_final?"":"!").p(_fname).p(":").p(_alias).p(" : ");
return _t ==null ? sb.p("---") : _t.print(sb,visit,html);
}
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/type/TypeStruct.java b/chapter25/src/main/java/com/seaofnodes/simple/type/TypeStruct.java
index 0d6700b9..82c37a12 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/type/TypeStruct.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/type/TypeStruct.java
@@ -154,6 +154,7 @@ public class TypeStruct extends Type {
public TypeStruct makeInit() {
Field[] fs = new Field[_fields.length];
for( int i=0; i<_fields.length; i++ ) {
+ if( _fields[i]._extern ) { fs[i] = _fields[i]; continue; }
Type zero = _fields[i]._t.makeZero();
fs[i] = _fields[i].makeFrom(_fields[i]._t != Type.BOTTOM && zero.isa(_fields[i]._t) ? zero : Type.TOP);
}
@@ -336,7 +337,7 @@ public class TypeStruct extends Type {
int len = Math.min(_fields.length,that._fields.length);
for( int i=0; i<len; i++ ) {
Field f0 = _fields[i], f1 = that._fields[i];
- if( !f0._fname.equals(f1._fname) || f0._alias != f1._alias )
+ if( !f0._fname.equals(f1._fname) || f0._alias != f1._alias || f0._extern != f1._extern )
return BOT;
}
@@ -465,7 +466,7 @@ public class TypeStruct extends Type {
@Override public int alignment() {
int align = 0;
for( Field f : _fields )
- align = Math.max(align, f._t.alignment());
+ if( !f._extern ) align = Math.max(align, f._t.alignment());
return align;
}
@@ -614,7 +615,8 @@ public class TypeStruct extends Type {
int[] cnts = new int[5]; // Count of fields at log field size
int flen = _fields.length;
for( int i=0; i<flen; i++ )
- cnts[_fields[i]._t.log_size()]++; // Log size is 0(byte), 1(i16/u16), 2(i32/f32), 3(i64/dbl)
+ if( !_fields[i]._extern )
+ cnts[_fields[i]._t.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];
@@ -625,6 +627,7 @@ public class TypeStruct extends Type {
// Assign offsets to all fields.
// Really a hidden radix sort.
for( int i=0; i<flen; i++ ) {
+ if( _fields[i]._extern ) { _offs[idx++] = -1; continue; }
int log = _fields[i]._t.log_size();
_offs[idx++] = offs[log]; // Field offset
offs[log] += 1<<log; // Next field offset at same alignment