summaryrefslogtreecommitdiff
path: root/src/main/java/com
diff options
context:
space:
mode:
authorCliff Click <cliffc@acm.org>2026-08-10 11:11:18 -0700
committerCliff Click <cliffc@acm.org>2026-10-24 15:56:44 -0700
commit7914681413f3e3562733786aa02b1fc7adfbe9b0 (patch)
treea33a763882a3b65712835c71bdabb9873ceee55c /src/main/java/com
parent741d78b6e840c88b7b183010c812ad982b342da1 (diff)
downloadSimple-7914681413f3e3562733786aa02b1fc7adfbe9b0.tar.gz
Simple-7914681413f3e3562733786aa02b1fc7adfbe9b0.zip
Chapter 28b: Separate Compilationlinear-chapter28blinear
Co-authored-by: Hsiang-Ying Fu <66062782+fuhsnn@users.noreply.github.com> Co-authored-by: fuhsnn <66062782+fuhsnn@users.noreply.github.com> Co-authored-by: Symious <14933944+symious@users.noreply.github.com> Co-authored-by: Jun Kawasaki <jun784@gmail.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Diffstat (limited to 'src/main/java/com')
-rw-r--r--src/main/java/com/seaofnodes/simple/Parser.java27
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/CodeGen.java193
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/CompUnit.java74
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/ElfReader.java236
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/ElfWriter.java9
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/FinishCalls.java57
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/GlobalBits.java177
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/InstSelect.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/Iter.java32
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/LocalIds.java33
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/Module.java98
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/Opto.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/ParseAll.java181
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/Serialize.java687
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/Unlink.java112
-rw-r--r--src/main/java/com/seaofnodes/simple/node/AddNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/AndNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ArithNode.java5
-rw-r--r--src/main/java/com/seaofnodes/simple/node/BoolNode.java15
-rw-r--r--src/main/java/com/seaofnodes/simple/node/BulkMemPhiNode.java24
-rw-r--r--src/main/java/com/seaofnodes/simple/node/CLInitNode.java7
-rw-r--r--src/main/java/com/seaofnodes/simple/node/CProjNode.java14
-rw-r--r--src/main/java/com/seaofnodes/simple/node/CallEndNode.java17
-rw-r--r--src/main/java/com/seaofnodes/simple/node/CallNode.java13
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ConFldOffNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ConstantNode.java7
-rw-r--r--src/main/java/com/seaofnodes/simple/node/CtrlNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/DivNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/EscapeNode.java7
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ExternNode.java15
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ExternOffsetNode.java7
-rw-r--r--src/main/java/com/seaofnodes/simple/node/FldOffNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/FunNode.java23
-rw-r--r--src/main/java/com/seaofnodes/simple/node/FunPtrNode.java10
-rw-r--r--src/main/java/com/seaofnodes/simple/node/GuardNode.java6
-rw-r--r--src/main/java/com/seaofnodes/simple/node/IfNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/LoadNode.java8
-rw-r--r--src/main/java/com/seaofnodes/simple/node/LoopNode.java4
-rw-r--r--src/main/java/com/seaofnodes/simple/node/MemMergeNode.java5
-rw-r--r--src/main/java/com/seaofnodes/simple/node/MemOpNode.java19
-rw-r--r--src/main/java/com/seaofnodes/simple/node/MemPhiNode.java18
-rw-r--r--src/main/java/com/seaofnodes/simple/node/MinusNode.java6
-rw-r--r--src/main/java/com/seaofnodes/simple/node/MulNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/NeverNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/NewNode.java10
-rw-r--r--src/main/java/com/seaofnodes/simple/node/Node.java80
-rw-r--r--src/main/java/com/seaofnodes/simple/node/NotNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/OrNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ParmNode.java16
-rw-r--r--src/main/java/com/seaofnodes/simple/node/PhiNode.java14
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ProjNode.java14
-rw-r--r--src/main/java/com/seaofnodes/simple/node/PtrToIntNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ReadOnlyNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/RegionNode.java5
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ReturnNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/RoundF32Node.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/SarNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ShlNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ShrNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/StartCUNode.java15
-rw-r--r--src/main/java/com/seaofnodes/simple/node/StartNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/StopCUNode.java9
-rw-r--r--src/main/java/com/seaofnodes/simple/node/StopNode.java10
-rw-r--r--src/main/java/com/seaofnodes/simple/node/StoreNode.java17
-rw-r--r--src/main/java/com/seaofnodes/simple/node/SubNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ToFloatNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ToIntegerNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/TypeNode.java1
-rw-r--r--src/main/java/com/seaofnodes/simple/node/XCtrlNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/XorNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/print/ASMPrinter.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/type/Field.java14
-rw-r--r--src/main/java/com/seaofnodes/simple/type/Type.java118
-rw-r--r--src/main/java/com/seaofnodes/simple/type/TypeConAry.java14
-rw-r--r--src/main/java/com/seaofnodes/simple/type/TypeConAryB.java1
-rw-r--r--src/main/java/com/seaofnodes/simple/type/TypeFloat.java21
-rw-r--r--src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java28
-rw-r--r--src/main/java/com/seaofnodes/simple/type/TypeInteger.java32
-rw-r--r--src/main/java/com/seaofnodes/simple/type/TypeMem.java26
-rw-r--r--src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java14
-rw-r--r--src/main/java/com/seaofnodes/simple/type/TypePtr.java9
-rw-r--r--src/main/java/com/seaofnodes/simple/type/TypeRPC.java20
-rw-r--r--src/main/java/com/seaofnodes/simple/type/TypeScalar.java12
-rw-r--r--src/main/java/com/seaofnodes/simple/type/TypeStruct.java17
-rw-r--r--src/main/java/com/seaofnodes/simple/type/TypeTuple.java14
-rw-r--r--src/main/java/com/seaofnodes/simple/type/XInt.java24
87 files changed, 2531 insertions, 226 deletions
diff --git a/src/main/java/com/seaofnodes/simple/Parser.java b/src/main/java/com/seaofnodes/simple/Parser.java
index c3ad0284..87a3570a 100644
--- a/src/main/java/com/seaofnodes/simple/Parser.java
+++ b/src/main/java/com/seaofnodes/simple/Parser.java
@@ -4,6 +4,7 @@ import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.*;
import com.seaofnodes.simple.util.Ary;
+import com.seaofnodes.simple.util.BAOS;
import com.seaofnodes.simple.node.ScopeNode.Kind;
import static com.seaofnodes.simple.util.Utils.TODO;
@@ -2908,6 +2909,32 @@ public class Parser {
}
+ public static void gather(Lexer loc, HashMap<String,Integer> strs) {
+ if( loc==null || loc._cu==null ) return;
+ Serialize.gather(strs,loc._cu._cname);
+ String file=loc._cu.sourceFile();
+ if( file!=null ) Serialize.gather(strs,file);
+ }
+
+ public static void packed(Lexer loc, BAOS out, HashMap<String,Integer> strs) {
+ if( loc==null || loc._cu==null ) { out.packed4(0); return; }
+ out.packed4(loc._position+1);
+ out.packed4(strs.get(loc._cu._cname));
+ String file=loc._cu.sourceFile();
+ out.packed4(file==null ? 0 : strs.get(file));
+ }
+
+ public static Lexer unpack(BAOS in, String[] strs) {
+ int pos=in.packed4();
+ if( pos==0 ) return null;
+ String owner=strs[in.packed4()];
+ String file=strs[in.packed4()];
+ CompUnit cu=CodeGen.CODE._compunits.get(owner.replace('.','/'));
+ assert cu!=null : owner;
+ if( !file.isEmpty() ) cu._sourceFile=file;
+ return new Lexer(cu,pos-1);
+ }
+
/**
* Copy a lexer from a lexer
*/
diff --git a/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java b/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java
index e6bace8c..8302ede6 100644
--- a/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java
+++ b/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java
@@ -29,8 +29,10 @@ public class CodeGen {
IterPostOpto, // Peepholes and inlining using SCCP results
FinalizeIR, // Field layout, offset cleanup, function/CU roots
TypeCheck, // Last check for bad programs
+ Serialize, // Save ideal IR, including opaque class initialization
Lower, // Expand checked class initialization to ordinary IR
LoopTree, // Build a loop tree; break infinite loops
+ Unlink, // Unlink call sites before machine code generation
Select, // Convert to target hardware nodes
Schedule, // Global schedule (code motion) nodes
LocalSched, // Local schedule
@@ -64,7 +66,12 @@ public class CodeGen {
public final String _modDir;
// Module Build Root
public final String _buildDir;
- // Slashed path from source root to the entry file, without ".smp".
+ // Search path from CWD for `.o` files containing external symbols and types.
+ // This can contain archives, zips and other .o file containers.
+ public final Ary<String> _externPaths;
+
+ // Dotted path from module root to file containing the source, sans ".smp".
+ // e.g. module root: "sys", nested source name "sys.io".
public final String _srcName;
// Only available for tests, otherwise source comes from the CompUnit
@@ -73,8 +80,12 @@ public class CodeGen {
// Current Working Directory; default module base
private final String _cwd;
- // Source files discovered in this invocation. All live initializers
- // participate in cycle checking, including fully inlined bodies.
+ // Compilation Units used in this compile; typically one per source/object
+ // file, although you can nest structs and get more than one. These are all
+ // required to compile, and are expected to have their source timestamps
+ // ahead of their obj file. Some of these may entirely be dead or inlined
+ // in this CompUnit and thus not needed in any linking stage. All the
+ // alive ones (inlined away or not) participate in class init cycle checks.
public HashMap<String,CompUnit> _compunits;
// Test shortcut for only one compilation unit
@@ -88,7 +99,7 @@ public class CodeGen {
// True when fidx is published through a public class or instance field.
public boolean publicFIDX(int fidx) {
- if( fidx < LocalIds.RESERVED ) return false;
+ if( fidx < GlobalBits.RESERVED ) return false;
// A public class initializer is itself an external root; unlike
// constructors and methods, it is not reached through a class field.
FunNode fun = _linker.atX(fidx);
@@ -142,7 +153,7 @@ public class CodeGen {
void freezePublicInterface() {
assert _publicFIDXs==null && _publicAliases==null;
int[] fidxs = XInt.EMPTY;
- for( int fidx=LocalIds.RESERVED; fidx<_linker._len; fidx++ )
+ for( int fidx=GlobalBits.RESERVED; fidx<_linker._len; fidx++ )
if( publicFIDX(fidx) )
fidxs = XInt.make(fidxs,fidx);
int[] aliases = XInt.EMPTY;
@@ -185,15 +196,15 @@ public class CodeGen {
// Test setup; no module nor file; can alter seed & argument; can re-run same CodeGen
public CodeGen( String src, Type arg ) {
- this(null,null,null, src, 126L, true, arg);
+ this(null,null,null,null, src, 126L, true, arg);
}
// Test setup; no module nor file; can alter seed & argument; can re-run same CodeGen
public CodeGen( String src, long workListSeed, boolean resetTypes ) {
- this(null,null,null, src, workListSeed, resetTypes, TypeInteger.BOT);
+ this(null,null,null,null, src, workListSeed, resetTypes, TypeInteger.BOT);
}
// Generic CodeGen, including full module setup
- public CodeGen( String modDir, String buildDir,
+ public CodeGen( String modDir, String buildDir, Ary<String> externPaths,
String srcName, String src, long workListSeed, boolean resetTypes, Type arg ) {
// Public singleton to avoid passing about this state to a huge count
// of places. Probably becomes a TLS at some point.
@@ -203,6 +214,7 @@ public class CodeGen {
_cwd = System.getProperty("user.dir")+"/";
_modDir = modDir == null ? _cwd : modDir;
_buildDir = buildDir == null ? _cwd : buildDir;
+ _externPaths = externPaths;
_srcName = srcName;
_callingConv = null; // Calling convention
// Source code from test strings, not files
@@ -225,7 +237,9 @@ public class CodeGen {
}
- // Run the requested phases. ELF output contains machine code and data.
+ // Run requested phases. File export must request Serialize before Lower;
+ // requesting Export up front does that automatically. Encoding alone does
+ // not retain reloadable ideal IR.
// No code emission, just IR generation
public CodeGen driver( Phase phase ) { return driver(phase,null,null,false,false,0); }
@@ -245,7 +259,8 @@ public class CodeGen {
// The Generic driver
public CodeGen driver( Phase target, String cpu, String callingConv, boolean inMemory, boolean emitEntrySymbol, int dump ) {
- // Parse the entry source and discover all referenced source files.
+ // Parse a Simple file, and for mentioned external symbols either load
+ // from .o or recursively parse
step(target, Phase.Parse, this::parse, dump);
// Run peepholes to a fixed point
step(target, Phase.Iter, this::iter, dump);
@@ -257,12 +272,16 @@ public class CodeGen {
step(target, Phase.FinalizeIR, this::finalizeIR, dump);
// Final error check - bad programs fail here
step(target, Phase.TypeCheck, this::typeCheck, dump);
+ // Save reloadable ideal IR for a later seperate compilation
+ step(target, Phase.Serialize, () -> Serialize.forTarget(this,target,inMemory), dump);
// Replace "fat" nodes with their exploded explicit IR; this can
// introduce CFG diamonds
step(target, Phase.Lower, this::lower, dump);
// Compute the loop-tree, used for approximating exec frequencies for
// scheduling and register allocation
step(target, Phase.LoopTree, this::loopTree, dump);
+ // Drop imports for machine selection, retain them for ideal evaluation.
+ step(target, Phase.Unlink, () -> unlink(cpu!=null), dump);
// Instruction selection
step(target, Phase.Select, () -> instSelect(cpu,callingConv), dump);
// Global Code Motion
@@ -273,7 +292,7 @@ public class CodeGen {
step(target, Phase.RegAlloc, this::regAlloc, dump);
// Encode machine instructions into bits
step(target, Phase.Encoding, this::encode, dump);
- // Export machine code and data into one ELF file
+ // Export bits (and serialized IR) into ELF files
step(target, Phase.Export, () -> exportELF(inMemory,emitEntrySymbol), dump);
return this;
}
@@ -329,13 +348,28 @@ public class CodeGen {
}
- // Allocate dense alias/function/call IDs within this invocation.
- // Class slots let a forward reference reserve an initializer's identity.
- public final LocalIds _aliases = new LocalIds();
+ // These next fields all 2-way map some *global* program feature to a
+ // local dense index, suitable for packing in BitSets. The mapping takes a
+ // global value - always a {source file/class} name, and an order number
+ // counting up per feature in the same file. The dense local index can be
+ // different in different compilations.
+
+ // The global info allows loading the same remote class info from different
+ // paths and aligning them. Example: Compiling A loads pre-compiled B and
+ // C, both of which load pre-compiled D. The 2 different D loads unify via
+ // this global info.
+
+ // These mappings are all trivial identities when compiling one file; they
+ // only become complex when loading separately compiled code.
+
+ // 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
+ 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,LocalIds.RESERVED); }
+ 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<>();
@@ -343,14 +377,14 @@ public class CodeGen {
// 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
- public final LocalIds _fidxs = new LocalIds();
+ public final GlobalBits _fidxs = new GlobalBits();
// Return local index for specific global file & order. Used to find fidxs for e.g. FREF class <init> fcns
public int fidx( String clz, int order ) { return _fidxs.next(clz, order); }
// New fidx in the current file
public int fidx( String clz ) { return fidx(clz, -1); }
// Compute local RPC index from global RPC info, one per call
- public final LocalIds _rpcs = new LocalIds();
+ public final GlobalBits _rpcs = new GlobalBits();
public int rpc( String clz ) { return _rpcs.next(clz); }
@@ -407,7 +441,6 @@ public class CodeGen {
// TFPs all have exact single fidxs and their return is wiped to BOTTOM (so
// the return is not part of the match).
public final Ary<FunNode> _linker = new Ary<>(FunNode.class);
- public final Ary<CallNode> _calls = new Ary<>(CallNode.class);
// Extern function declarations; input is the FIDX assigned to the name.
final HashMap<Integer,String> _externFunc = new HashMap<>();
@@ -430,6 +463,8 @@ public class CodeGen {
// ---------------------------
// Parser object
+ public final Ary<CallNode> _calls = new Ary<>(CallNode.class);
+
public final Parser P;
// Parse ASCII text into Sea-of-Nodes IR
@@ -483,17 +518,20 @@ public class CodeGen {
// ---------------------------
// TypeCheck queued surviving CLInits during its existing error walk.
public CodeGen lower() {
- return phase(Phase.Lower, _phase==Phase.TypeCheck, () -> _iter.iterate(this));
+ return phase(Phase.Lower, (_phase==Phase.TypeCheck || _phase==Phase.Serialize), () -> _iter.iterate(this));
}
// ---------------------------
// Build the loop tree; break never-exit loops
public CodeGen loopTree() {
- return phase(Phase.LoopTree, _phase==Phase.Lower, () -> { _start.buildLoopTree(_linker,_stop); FinishCalls.run(this); });
+ return phase(Phase.LoopTree, _phase==Phase.Lower, () -> _start.buildLoopTree(_linker,_stop));
}
// ---------------------------
-
+ public BAOS _serial;
+ public void serialize() {
+ phase(Phase.Serialize, _phase==Phase.TypeCheck, () -> Serialize.serialize(this));
+ }
// ---------------------------
// Code generation CPU target
@@ -522,7 +560,12 @@ public class CodeGen {
}
// ---------------------------
-
+ public void unlink() {
+ unlink(true);
+ }
+ private void unlink(boolean removeImports) {
+ phase(Phase.Unlink, _phase==Phase.LoopTree, () -> Unlink.run(this,removeImports));
+ }
// ---------------------------
// Control Flow Graph in Reverse Post Order.
@@ -530,7 +573,7 @@ public class CodeGen {
// Global schedule (code motion) nodes
public CodeGen GCM() {
- return phase(Phase.Schedule, (_phase==Phase.LoopTree || _phase==Phase.Select), () -> GlobalCodeMotion.buildCFG(this));
+ return phase(Phase.Schedule, (_phase==Phase.Unlink || _phase==Phase.Select), () -> GlobalCodeMotion.buildCFG(this));
}
// ---------------------------
@@ -576,6 +619,110 @@ public class CodeGen {
// ---------------------------
+ // Search a external path list, each path is recursively searched for .o
+ // files, which are partially loaded and searched for public symbols.
+
+ // This is a state machine which lazily searches down the set of search
+ // paths until the requested module is class is found.
+
+ // Map from external Strings to either partially read Simple ElfFile or ExternNode
+ public final HashMap<String,Object> _externSymbols = new HashMap<>();
+
+ // State machine elements; the outermost element is _externPaths
+ private int _extPathIdx; // Search index into the extern path list
+ private final Ary<File> _files = new Ary<>(File.class); // Files in the current extern path being searched
+ private int _extFileIdx; // Index into _files
+
+ public ElfReader findExternalSimple( String name ) {
+ return findExternalSymbol(name) instanceof ElfReader elf ? elf : null;
+ }
+
+ public ExternNode findExternal( String name ) {
+ return findExternalSymbol(name) instanceof ExternNode extern ? extern : null;
+ }
+
+ // Search the search-path for the name; return an ElfReader if found in a
+ // Simple-made ELF; return an ExternNode if from another ELF, or null if
+ // not found.
+ private Object findExternalSymbol( String name ) {
+ // State Machine!
+
+ while( true ) {
+ // Check for an immediate hit
+ switch( _externSymbols.get(name) ) {
+
+ case ExternNode extern:
+ // Name maps to an ExternNode
+ return extern;
+
+ case ElfReader elf:
+ return elf;
+
+ case null:
+ // Name is unknown. Advance the file-system search, pulling
+ // out new ELF files from the search directory list and loading
+ // their published symbols.
+
+ // The last directory was mined out, so get the next search
+ // directory and find all ELF-like files.
+ if( _extFileIdx >= _files._len ) {
+ if( _externPaths==null || _extPathIdx >= _externPaths._len )
+ return null; // A true miss in the whole search path
+ loadExtPath(); // Load next batch file of files
+ break;
+ }
+ // Load and parse an ELF header
+ ElfReader elf = getElf(_files.at(_extFileIdx++));
+ if( elf == null ) break; // Not an ELF
+ // Load public symbols from the ELF
+ elf.loadPublicSymbols();
+ if( elf._strs==null ) break; // No .simple section with strings
+ // Put public symbol in global table; first names shadow all others
+ // Map from symbol string to ELFReader; most lookups
+ // will miss and no need to unpack ElfReader types
+ _externSymbols.putIfAbsent(elf._strs[1],elf);
+
+ // Publish definitions only; referenced classes belong to other objects.
+ for( String fname : elf._units )
+ _externSymbols.putIfAbsent(Parser.addClzPrefix(fname.replace('/','.')),elf);
+ break;
+
+ default: throw Utils.TODO("should not reach here");
+ }
+ }
+ }
+
+ // Load all the .o files in the next external path.
+ private void loadExtPath() {
+ _files.clear();
+ _extFileIdx = 0;
+ String extpath = _externPaths.at(_extPathIdx++);
+ File f = new File(extpath);
+ if( !f.isAbsolute() )
+ f = new File(_cwd+extpath);
+ fillExtFiles(f);
+ }
+
+ // Recursive search (TODO: gzip, archives) and gather all .o files.
+ private void fillExtFiles(File dir) {
+ if( dir.isDirectory() )
+ for( File f : dir.listFiles() )
+ fillExtFiles(f);
+ else if( dir.getName().endsWith(".o") )
+ _files.add(dir);
+ }
+
+ // Map from object file names to ElfReaders
+ public final HashMap<String,ElfReader> _elfs = new HashMap<>();
+ ElfReader getElf(File f) {
+ ElfReader elf = _elfs.get(f.getPath());
+ if( elf != null )
+ return elf;
+ _elfs.put(f.getPath(), elf = ElfReader.load(f,null));
+ return elf;
+ }
+
+ // ---------------------------
public boolean _asmLittle=true;
public String asm() { return asm(new SB()).toString(); }
SB asm(SB sb) {
diff --git a/src/main/java/com/seaofnodes/simple/codegen/CompUnit.java b/src/main/java/com/seaofnodes/simple/codegen/CompUnit.java
index c8b0a82c..71126795 100644
--- a/src/main/java/com/seaofnodes/simple/codegen/CompUnit.java
+++ b/src/main/java/com/seaofnodes/simple/codegen/CompUnit.java
@@ -12,23 +12,30 @@ import java.nio.file.Path;
import java.util.*;
import static com.seaofnodes.simple.codegen.CodeGen.CODE;
-// A source file and its class. Parent links follow the source directory tree.
+// A compilation-unit, which is an external reference and source and object
+// files. These are mirrored over a file system tree (so form a tree), both
+// the source tree and build/object tree.
public class CompUnit {
public final CompUnit _par; // Parent, e.g. A/B
final String _fname; // Full slashed file name starting from module root, e.g. A/B/C
final public String _cname; // Full dotted class name starting from module root, e.g. A.B.C
final String _name; // Base name, e.g. C
+ final ExternNode _ext; // Found in external libs; e.g. libc
final File _smp; // Simple source file, e.g. module_root/A/B/C.smp
+ public final File _obj; // ELF output file, e.g. build/A/B/C.o
final public String _src; // Source code, from file or test case
+ // Original source path from an imported diagnostic location. Does not
+ // affect discovery/rebuild decisions, which use _smp and _src.
+ public String _sourceFile;
public String sourceFile() {
- return _smp!=null ? _smp.getAbsolutePath() : null;
+ return _smp!=null ? _smp.getAbsolutePath() : _sourceFile;
}
public TypeStruct _clz; // The one TypeStruct being published
// _deps records units required for types, code, or class references,
- // without requiring their initializers to run. These source
+ // without requiring their initializers to run. These discovery/rebuild
// dependencies may form a cycle.
Ary<CompUnit> _deps;
@@ -45,17 +52,16 @@ public class CompUnit {
_fname= fname;
_cname= cname;
_name = name;
- _smp = source(fname);
+ _ext = null;
+ _smp = new File( CODE. _modDir + "/" + fname + ".smp");
+ _obj = new File( CODE._buildDir + "/" + fname + ".o" );
- _src = _smp.exists() ? Files.readString(_smp.toPath()) : null;
- }
-
- // The bundled library is ordinary source. A local sys tree can replace it;
- // this chapter never loads Simple code from an object file.
- static File source(String fname) {
- File file=new File(CODE._modDir,fname+".smp");
- if( file.exists() || !(fname.equals("sys") || fname.startsWith("sys/")) ) return file;
- return new File("src/main/smp",fname+".smp");
+ boolean mustCompile = _smp.exists() && // Has source to compile
+ ( fname.equals(CODE._srcName) || // Explicit input is a compilation request
+ !_obj.exists() || // No object file
+ _smp.lastModified() > _obj.lastModified() || // Out-of-date with source?
+ checkDependentObjs() ); // Out-of-date with other objs?
+ _src = mustCompile ? new String(Files.readAllBytes(_smp.toPath())) : null;
}
// Test case. No source file. Source code passed in as a String. Fake
@@ -65,11 +71,49 @@ public class CompUnit {
_name = fname;
_fname= fname;
_cname= fname;
+ _ext = null;
_smp = null;
+ _obj = null;
_src = src;
}
- // Record source discovery without adding an initialization edge.
+ // External CompUnit, pulled in from an external library like libc
+ CompUnit( ExternNode ext, String symbol ) {
+ _par = null;
+ _name = symbol;
+ _fname= _name;
+ _cname= null;
+ _ext = ext;
+ _smp = null;
+ _obj = null;
+ _src = null;
+ }
+
+ // External Simple object file, or namespace parent, found on an external search path.
+ CompUnit( File obj, CompUnit par, String fname, String cname, String name ) {
+ _par = par;
+ _name = name;
+ _fname= fname;
+ _cname= cname;
+ _ext = null;
+ _smp = null;
+ _obj = obj;
+ _src = null;
+ }
+
+ // Return true if _obj is out-of-date relative to dependents.
+ private boolean checkDependentObjs() {
+ ElfReader elf = CODE.getElf(_obj);
+ elf.loadPublicSymbols();
+ for( String dep : elf._deps ) {
+ File obj = new File(CODE._buildDir+"/"+dep+".o");
+ if( obj.lastModified() > _obj.lastModified() ) // Out-of-date with deps?
+ return true;
+ }
+ return false;
+ }
+
+ // Add a discovered dependency, for later writing out in an ELF file, ".simple" section
void addDep(CompUnit dep) {
if( _deps == null ) _deps = new Ary<>(CompUnit.class);
_deps.add(dep);
@@ -91,7 +135,9 @@ public class CompUnit {
@Override public String toString() {
String x = "{"+_cname;
if( _smp!=null && _smp.exists()) x += ",hasSMP";
+ if( _obj!=null && _obj.exists()) x += ",hasObj";
if( _src!=null ) x += ",hasSrc";
+ if( _ext != null ) x += ",isExt";
x += "}";
return x;
}
diff --git a/src/main/java/com/seaofnodes/simple/codegen/ElfReader.java b/src/main/java/com/seaofnodes/simple/codegen/ElfReader.java
new file mode 100644
index 00000000..10720300
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/codegen/ElfReader.java
@@ -0,0 +1,236 @@
+package com.seaofnodes.simple.codegen;
+
+import com.seaofnodes.simple.node.Node;
+import com.seaofnodes.simple.type.TypeMemPtr;
+import com.seaofnodes.simple.type.TypeStruct;
+import com.seaofnodes.simple.util.Ary;
+import com.seaofnodes.simple.util.BAOS;
+import com.seaofnodes.simple.util.Utils;
+import java.io.*;
+import java.nio.file.Files;
+import java.util.HashMap;
+
+public class ElfReader {
+
+ public final File _file;
+ public final byte[] _buf;
+ int _x; // Cursor into buffer
+ // ELF Header
+ public final ElfHeader _header;
+ // All sections
+ public final Section[] _sections;
+ // Simple section, if available
+ public final Section _simple;
+ // Byte array in/out stream - used as a reader
+ public final BAOS _bais;
+ // Compilation unit; written into FunNodes
+ public final CompUnit _compunit;
+
+ // Strings from the Simple section, either small public set or whole set
+ public String[] _strs;
+ // Dependent file names
+ public String[] _deps;
+ // Compilation units defined by this object, independent of init analysis.
+ public String[] _units;
+ // Only published symbol this file
+ public TypeStruct _clz;
+ // Canonical named types, distinct from optimized same-named graph states.
+ public TypeStruct[] _pubTypes;
+ // Nodes read in this Elf
+ public Ary<Node> _nodes;
+
+ // Load an Elf file (mostly lazily)
+ public static ElfReader load( File f, CompUnit cu ) {
+ try { return new ElfReader(f,cu); }
+ catch( IOException ioe ) { return null; }
+ }
+
+ // Load the Simple section from the ELF file
+ private ElfReader( File f, CompUnit cu ) throws IOException {
+ _file = f;
+ _compunit = cu;
+ _buf = Files.readAllBytes(f.toPath());
+ _header = new ElfHeader();
+ // No attempt is made to parse the program header table.
+ _sections = new Section[_header._shnum];
+ for( int i=0; i<_header._shnum; i++ )
+ _sections[i] = new Section(i);
+
+ // Load section names
+ Section secstr = _sections[_header._shstrndx];
+ for( Section sec : _sections )
+ if( sec._type != 0 )
+ sec._name = secstr.str(sec._noff);
+
+ Section simple = null;
+ for( Section sec : _sections )
+ if( ".simple".equals(sec._name) )
+ simple = sec;
+ _simple = simple;
+ _bais = new BAOS(_buf, (int)_simple._offset);
+ }
+
+ // For Serialize self-test
+ ElfReader( BAOS bais ) {
+ _file = null;
+ _compunit = null;
+ _buf = bais.buf();
+ _header = null;
+ _sections = null;
+ _simple = null;
+ _bais = bais;
+ }
+
+ // Load the public symbols and dependent objs, but not the whole IR
+ void loadPublicSymbols() {
+ // Check already loaded and cached
+ if( _strs == null && _simple != null )
+ // Read only the published public strings, for speed purposes when
+ // scanning a large count of ELF files but getting hits in only a
+ // few of them. i.e. optimizing (prematurely?) for the traditional
+ // case of linking a small file against a large library
+ Serialize.read_public_strs(this);
+ }
+
+ // Loads the entire IR
+ public TypeStruct loadSimple(CodeGen code) {
+ Serialize.readAll(code, this, code._aliases, code._fidxs, code._rpcs);
+ return _clz;
+ }
+
+ // Loads strings, mappings, and types, but not Nodes. Used after the
+ // public-string scan finds a matching Simple ELF, so parsing can resolve
+ // imported field/array aliases before the full graph is linked in later.
+ public TypeStruct loadPublicTypes(CodeGen code) {
+ Serialize.readTypes(code, this, code._aliases, code._fidxs, code._rpcs);
+ return _clz;
+ }
+
+ int u1() { return _buf[_x++]&0xFF; }
+ int magic() { // Big endian
+ return (u1() << 24) | (u1() << 16) | (u1() << 8) | u1();
+ }
+ int u2() { return u1() | (u1()<<8); } // Little endian
+ int u4() { // Little endian
+ return u1() | (u1() << 8) | (u1() << 16) | (u1() << 24);
+ }
+ long u8() { return u4() | ((long)u4()<<32); }
+ void skip(int skip) { _x += skip; }
+
+
+ public class ElfHeader {
+ final int _magic;
+ final boolean _format32; // Format32 or format64
+ final byte _osabi;
+ final boolean _little; // True if little endian, false if big endian
+ final char _type;
+ final char _machine;
+ final long _entry; // Process entry point
+ final long _phoff; // Program header table offset, from file start
+ final long _shoff; // Section header table offset, from file start
+ final int _flags; // Flags
+ final int _phnum; // Number of program header entries
+ final int _shentsize;
+ final int _shnum; // Number of section header entries
+ final int _shstrndx; // Section header index contains section names
+
+
+ ElfHeader() throws IOException {
+ _x = 0;
+ if( (_magic = magic()) != 0x7F454C46 ) bad("magic number",_magic);
+ byte EI_CLASS = (byte)u1();
+ if( EI_CLASS != 1 && EI_CLASS != 2 ) bad("EI_CLASS",EI_CLASS);
+ _format32 = (EI_CLASS==1);
+ byte EI_DATA = (byte)u1();
+ if( EI_DATA != 1 && EI_DATA != 2 ) bad("EI_DATA",EI_DATA);
+ _little = EI_DATA == 1;
+ if( u1() != 1 ) bad("version",1);
+ if( (_osabi=(byte)u1()) != 0 ) bad("EI_VERSION",_osabi);
+ byte abiversion = (byte)u1();
+ if( abiversion != 0 ) bad("EI_ABIVERSION",abiversion);
+ skip(7); // Pad
+
+ // Endian matters now
+ if( !_little ) throw new IOException("TODO: Big endian");
+ if( (_type = (char)u2()) != 1 ) bad("ET_REL",_type);
+ if( (_machine = (char)u2()) != 0x3E ) bad("AMD x86-64",_machine);
+ int ver = u4();
+ if( ver != 1 ) bad("Version",ver);
+ // Format32 matters now
+ if( _format32 ) bad("Format",1);
+ _entry = u8(); // Process entry point
+ _phoff = u8(); // Program header table offset
+ _shoff = u8(); // Section header table offset
+ _flags = u4(); // Flags
+ int ehsize = u2();
+ if( ehsize != 0x40 ) bad("EHSIZE",ehsize);
+ int phentsize = u2();
+ if( phentsize != 0x38 && phentsize != 0 ) bad("PHENTSIZE",phentsize);
+ _phnum = u2();
+ _shentsize = u2();
+ if( _shentsize != 0x40 ) bad("SHENTSIZE",_shentsize);
+ _shnum = u2();
+ // String section
+ _shstrndx = u2();
+ assert _x == ehsize;
+ }
+ }
+
+ public class Section {
+ final int _noff; // Offset in string section for name
+ final int _type; // Section type
+ final long _flags;
+ final long _addr; // Offset in virtual mem, if loaded
+ final long _offset; // Offset in file
+ final long _size; // Size in bytes
+ final int _link; // Associated section index, varies
+ final int _info; // Varies
+ final byte _align; // Log2 alignment
+ final int _esize; // Element size, if non-zero
+ String _name;
+
+ Section(int sidx) throws IOException {
+ _x = (int)(_header._shoff + (sidx * _header._shentsize));
+ _noff = u4();
+ _type = u4();
+ _flags = u8();
+ _addr = u8();
+ _offset= u8();
+ _size = u8();
+ _link = u4();
+ _info = u4();
+ long align = u8();
+ if( align==0 ) align=1;
+ _align = (byte)Long.numberOfTrailingZeros(align);
+ if( (1L << _align ) != align )
+ throw new IOException("Bad section alignment: "+align);
+ _esize = (int)u8();
+ }
+
+ String str(int off) {
+ assert _type == 3/*SHT_STRTAB*/; // String table section header
+ int start = (int)(_offset+off);
+ int x = start;
+ while( _buf[x++] !=0 ) ;
+ return new String(_buf,start,x-start-1);
+ }
+ }
+
+ private static void bad(String msg, int val) throws IOException {
+ throw new IOException("Bad "+msg+": "+val);
+ }
+
+
+
+ //public TypeMemPtr lookup( String name ) {
+ // if( _simple == null ) return null;
+ // BAOS baos = new BAOS(_buf, (int)_simple._offset);
+ // Ary<String> strs = Serialize.read_strs(baos);
+ //
+ //
+ //
+ // Ary<Node> nodes = Serialize.read_types(baos);
+ //
+ // throw Utils.TODO("Elf load symbol "+name);
+ //}
+}
diff --git a/src/main/java/com/seaofnodes/simple/codegen/ElfWriter.java b/src/main/java/com/seaofnodes/simple/codegen/ElfWriter.java
index c52209dd..5a280723 100644
--- a/src/main/java/com/seaofnodes/simple/codegen/ElfWriter.java
+++ b/src/main/java/com/seaofnodes/simple/codegen/ElfWriter.java
@@ -27,6 +27,7 @@ public class ElfWriter {
static final int SHT_SYMTAB = 2;
static final int SHT_STRTAB = 3;
static final int SHT_RELA = 4;
+ static final int SHT_SIMPLE = 10; // Simple section
final String _name;
final int _type, _entsize, _index, _name_pos, _flags;
@@ -275,6 +276,9 @@ public class ElfWriter {
public static final int SECTION_HDR_SIZE = 64;
// ------------------------------------------------------------------------
+ public class SimpleSection extends DataSection {
+ SimpleSection(BAOS serial) { super(".simple", SHT_SIMPLE, serial, 0); }
+ }
// ------------------------------------------------------------------------
@@ -300,6 +304,8 @@ public class ElfWriter {
}
public ElfWriter export(boolean emitEntrySymbol) {
+ if( _code._serial==null )
+ throw new IllegalStateException("ELF export needs ideal IR: request Export from the driver, or Serialize before Lower");
// Sections are created in the order they are emitted.
_sections = new Ary<>(Section.class);
@@ -311,7 +317,7 @@ public class ElfWriter {
_strtab = new StringSection();
// place all the symbols
- int nlocals = 1/*null*/+9/*number of sections*/;
+ int nlocals = 1/*null*/+9/*number of sections*/+1/*Simple section*/;
SymbolSection symbols = new SymbolSection(nlocals);
// we've already constructed these entire sections in the encoding phase
@@ -392,6 +398,7 @@ public class ElfWriter {
}
// Create a "Simple" section for Simple types and ir
+ SimpleSection simple = new SimpleSection(_code._serial);
// Write the local section header symbols
assert nlocals == 1+_sections._len;
diff --git a/src/main/java/com/seaofnodes/simple/codegen/FinishCalls.java b/src/main/java/com/seaofnodes/simple/codegen/FinishCalls.java
deleted file mode 100644
index 7ba696d3..00000000
--- a/src/main/java/com/seaofnodes/simple/codegen/FinishCalls.java
+++ /dev/null
@@ -1,57 +0,0 @@
-package com.seaofnodes.simple.codegen;
-
-import com.seaofnodes.simple.node.*;
-
-// Detach analysis call edges after building the loop tree. Every live body
-// belongs to this invocation; preserve it for scheduling and emission.
-abstract class FinishCalls {
-
-
- static void run(CodeGen code) {
-
- // The remaining passes assume all calls are unlinked; i.e. we are throwing
- // away the Call Graph here. Functions only reachable from internal calls
- // need to be re-hooked to stop/start less they go dead.
- for( FunNode fun : code._linker ) {
- // Already linked to start, not going dead
- if( fun==null || fun.isDead() )
- continue;
-
- // Insert the post-Opto code-generation hook. Unlike an Opto-time
- // Start input, this no longer carries unknown-caller semantics.
- ReturnNode ret = fun.ret();
- StartNode start = fun._compunit._start;
- assert start!=null;
- if( fun.nIns() <= 1 || fun.in(1) != start ) {
- fun.insertDef(1,start);
- for( Node use : fun._outputs )
- if( use instanceof ParmNode parm )
- parm.insertDef(1,ConstantNode.raw(parm._type));
- if( fun._compunit._stop._inputs.find(ret) == -1 )
- fun._compunit._stop.addDef(ret);
- }
- // Unlink from Call
- for( int i=2; i<fun.nIns(); i++ )
- ((CallNode)fun.in(i)).unlink(fun,i--);
- if( fun.rpc()==null ) { // Ensure valid RPC
- // First make sure a valid RPC; single-call into multi-fun will have each
- // function having a single call site, so the RPC becomes a constant and
- // folds - but since multiple targets, the Call never inlines. Recreate a
- // valid RPC so codegen (and Eval2) understands the calling convention.
- ParmNode rpc = new ParmNode( "$rpc", 0, ret.rpc()._type, fun );
- rpc.addDef(ret.rpc());
- ret.setDef( 3, rpc.init() );
- }
- }
-
- code._stop.walk(n -> {
- if( n instanceof CallEndNode cend )
- for( int i=1; i<cend.nIns(); i++ )
- if( cend.in(i) instanceof ReturnNode )
- cend.delDef(i--);
- if( n instanceof CallNode call )
- call.unlink_all();
- return null;
- });
- }
-}
diff --git a/src/main/java/com/seaofnodes/simple/codegen/GlobalBits.java b/src/main/java/com/seaofnodes/simple/codegen/GlobalBits.java
new file mode 100644
index 00000000..bc9c9d39
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/codegen/GlobalBits.java
@@ -0,0 +1,177 @@
+package com.seaofnodes.simple.codegen;
+
+import com.seaofnodes.simple.util.*;
+import java.util.HashMap;
+import java.util.Arrays;
+import java.util.Objects;
+
+
+// This utility 2-way maps some *global* program feature to a local dense
+// index, suitable for packing in BitSets. The mapping takes a global value -
+// always a {source file/class} name and an order number counting up per
+// feature in the same file. The dense local index can be different in
+// different compilations.
+
+// The global info allows loading the same remote class info from different
+// paths and aligning them. Example: Compiling A loads pre-compiled B and C,
+// both of which load pre-compiled D. The 2 different D loads unify via this
+// global info.
+
+// These mappings are all trivial identities when compiling one file; they
+// only become complex when loading seperately compiled code.
+
+public class GlobalBits {
+ public static final int RESERVED = 2;
+ private final HashMap<String,int[]> _clzOrder2Local = new HashMap<>();
+ private final Ary<String> _local2Clz = new Ary<>(String.class);
+ private final AryInt _local2Order = new AryInt();
+ // Local dense index
+ private int _local;
+
+ GlobalBits() {
+ _local = RESERVED;
+ // Reserved null-class bits are global identities and are never
+ // remapped.
+ int[] xs = new int[RESERVED];
+ xs[0] = RESERVED; // Per-class next order counter
+ for( int i=0; i<RESERVED; i++ ) {
+ if( i > 0 ) xs[i] = i;
+ _local2Clz .setX(i,null);
+ _local2Order.setX(i,i);
+ }
+ _clzOrder2Local.put(null,xs);
+ }
+
+ // Get a next local index for this clz
+ int next( String clz ) { return next(clz,-1); }
+
+ // Given a clz and order, return an existing local index or make a new
+ // mapping and return that.
+ int next(String clz, int order ) {
+ // Get the per-clz Order->Local mapping
+ int[] xs = _clzOrder2Local.get(clz);
+ if( xs == null )
+ xs = new int[]{RESERVED,1}; // Make, if it does not exist
+ // Caller does not have an order in mind, just take the next one stored
+ // in xs[0]
+ if( order == -1 )
+ order = xs[0]++;
+ // Order exists from prior compilation, but not recorded locally
+ else if( order >= xs[0] )
+ xs[0] = order+1;
+ // Grow as needed
+ while( xs.length <= order )
+ xs = Arrays.copyOf(xs,xs.length*2);
+ // Have a local mapping already?
+ if( xs[order] != 0 )
+ return xs[order];
+ int local = _local++;
+ bind(local,clz,order);
+ return local; // The dense local index
+ }
+
+ // Bind a specific dense local index to {clz,order}. Used by both fresh
+ // allocation and deserialization, where local indexes must not compact
+ // across holes because XInt bitsets are serialized against this space.
+ private void bind( int local, String clz, int order ) {
+ if( _local <= local )
+ _local = local+1;
+ _local2Clz .setX(local,clz );
+ _local2Order.setX(local,order);
+
+ // Inline-only fidx holes are represented as {null,0}. They need the
+ // reverse mapping above, but no forward map because xs[0] is the
+ // per-class next-order counter.
+ if( clz==null && order==0 )
+ return;
+
+ int[] xs = _clzOrder2Local.get(clz);
+ if( xs == null )
+ xs = new int[]{RESERVED,1};
+ if( order >= xs[0] )
+ xs[0] = order+1;
+ while( xs.length <= order )
+ xs = Arrays.copyOf(xs,xs.length*2);
+ _clzOrder2Local.put(clz,xs);
+ xs[order] = local;
+ }
+
+ // A file-local function that will inline, and never write to disk
+ public int nextInline( ) {
+ int local = _local++;
+ _local2Clz .setX(local,null);
+ _local2Order.setX(local,0);
+ return local;
+ }
+ // Get a next local index for the same clz as this old index
+ public int next( int old ) { return next(_local2Clz.at(old)); }
+
+ // Map a file-local dense index to this compilation unit's local dense index.
+ public int map( GlobalBits fileBits, int fileLocal ) {
+ if( fileLocal >= fileBits._local2Clz.size() )
+ throw new ArrayIndexOutOfBoundsException(""+fileLocal+" >= "+fileBits._local2Clz.size());
+ return findOrNext(fileBits._local2Clz.at(fileLocal), fileBits._local2Order.at(fileLocal));
+ }
+
+ // Map a global {clz,order} to a local one; keep any existing mapping or
+ // make a new one as needed.
+ private int findOrNext(String clz, int order ) {
+ // Reserved null-class identities are not represented in xs[order],
+ // because xs[0] is the next-order counter.
+ if( clz==null && order < _local2Clz.size() && _local2Order.at(order)==order && _local2Clz.at(order)==null )
+ return order;
+ int[] xs = _clzOrder2Local.get(clz);
+ if( xs != null && order < xs.length ) {
+ int local = xs[order];
+ if( local < _local2Clz.size() && _local2Order.at(local)==order && Objects.equals(_local2Clz.at(local),clz) )
+ return local;
+ }
+ return next(clz,order);
+ }
+
+ // Stable native symbol identity, independent of this compilation's dense IDs.
+ String owner(int local) { return _local2Clz.at(local); }
+ String symbol(int local) {
+ return _local2Clz.at(local)+"$F$"+_local2Order.at(local);
+ }
+
+ // Write out to BAOS enough bits to unwind the local back to global index
+ void packed( BAOS baos, HashMap<String,Integer> strs ) {
+ // Unwind a local index to the clz.
+ baos.packed2(_local2Clz.size());
+ for( int i=RESERVED; i<_local2Clz.size(); i++ ) {
+ String clz = _local2Clz.at(i);
+ baos.packed2(clz==null ? 0 : strs.get(clz));
+ baos.packed2( _local2Order.at(i) );
+ }
+ }
+
+ void gather( HashMap<String,Integer> strs ) {
+ for( int i=RESERVED; i<_local2Clz.size(); i++ )
+ Serialize.gather(strs,_local2Clz.at(i));
+ }
+
+ // Read in the BAIS bits needed to make a file-local -> global mapping
+ static GlobalBits packed( BAOS bais, String[] strs ) {
+ var gb = new GlobalBits();
+ int n = bais.packed2();
+ for( int i=RESERVED; i<n; i++ ) {
+ int sx = bais.packed2();
+ String clz = strs[sx];
+ if( clz.isEmpty() ) clz = null;
+ int order = bais.packed2();
+ gb.bind(i,clz,order);
+ }
+ return gb;
+ }
+
+ @Override public String toString() {
+ SB sb = new SB().p('[');
+ for( int i=0; i<_local2Clz.size(); i++ ) {
+ if( i > 0 ) sb.p(',');
+ sb.p(i).p(" <-> ").p(_local2Clz.at(i)).p('#').p(_local2Order.at(i));
+ }
+ return sb.p(']').toString();
+ }
+
+}
diff --git a/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java b/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java
index 4477025b..09eb8d33 100644
--- a/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java
+++ b/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java
@@ -60,7 +60,7 @@ public abstract class GlobalCodeMotion {
if( use==null || globals.containsKey(use) ) continue;
FunNode fun = useFun(use);
if( fun==null ) continue; // Global metadata has no function owner.
- assert CodeGen.CODE._mach==null || CodeGen.CODE.owns(fun);
+ assert CodeGen.CODE._mach==null || CodeGen.CODE.owns(fun); // Ideal evaluation includes imported bodies.
var local = copies.computeIfAbsent(fun,f -> new IdentityHashMap<>());
Node copy = cloneGlobal(con,fun,globals,local);
for( int i=0; i<use.nIns(); i++ )
diff --git a/src/main/java/com/seaofnodes/simple/codegen/InstSelect.java b/src/main/java/com/seaofnodes/simple/codegen/InstSelect.java
index db35d524..5fc002a1 100644
--- a/src/main/java/com/seaofnodes/simple/codegen/InstSelect.java
+++ b/src/main/java/com/seaofnodes/simple/codegen/InstSelect.java
@@ -14,7 +14,7 @@ class InstSelect {
static CodeGen select(CodeGen code, String cpu, String callingConv, String base) {
// Ideal evaluation schedules the ideal graph without a CPU port.
if( cpu==null ) return code;
- return code.phase(CodeGen.Phase.Select, code._phase==CodeGen.Phase.LoopTree,
+ return code.phase(CodeGen.Phase.Select, code._phase==CodeGen.Phase.Unlink,
() -> new InstSelect(code).run(cpu,callingConv,base));
}
diff --git a/src/main/java/com/seaofnodes/simple/codegen/Iter.java b/src/main/java/com/seaofnodes/simple/codegen/Iter.java
index 1ffd9737..c5120370 100644
--- a/src/main/java/com/seaofnodes/simple/codegen/Iter.java
+++ b/src/main/java/com/seaofnodes/simple/codegen/Iter.java
@@ -3,15 +3,19 @@ package com.seaofnodes.simple.codegen;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.*;
-// Establish a pessimistic fixed point after parsing. Later Iter runs
+// Establish a pessimistic fixed point after parsing/loading. Later Iter runs
// reuse the worklist and types produced by the preceding phase.
abstract class Iter {
static void run(CodeGen code) {
- // Close parser-in-progress states. Seed a fresh pessimistic solve;
- // exposure sets start at FULL and can only shrink during Iter.
+ // The phase shift closes all parser-in-progress states and freezes
+ // unknown external inputs. Imported nodes carry post-Opto escape
+ // precision, but Iter is a fresh pessimistic solve: memory escape
+ // summaries must restart at FULL so newly discovered private escapes
+ // can only be removed, never added. Seed the complete graph once.
+ boolean hasImports = code._externPaths != null;
code._start.walk(n -> {
if( !(n instanceof ConstantNode) )
- n._type = iterEscapeReset(n._type);
+ n._type = hasImports ? iterImportReset(n._type) : iterEscapeReset(n._type);
code.add(n);
return null;
});
@@ -34,5 +38,23 @@ abstract class Iter {
return t;
}
-
+ private static Type iterImportReset(Type t) {
+ if( t.isHigh() )
+ return t.dual();
+ if( t instanceof TypeMem mem )
+ return TypeMem.make(mem._alias,iterImportReset(mem._t),mem._one,mem._clz,mem._final,XInt.FULL,XInt.FULL);
+ if( t instanceof TypeMemPtr )
+ return t.makeStorage();
+ if( t instanceof TypeStruct )
+ return t.makeStorage();
+ if( t instanceof TypeTuple tt ) {
+ TypeTuple rez = tt;
+ for( int i=0; i<tt._types.length; i++ ) {
+ Type tx = iterImportReset(tt._types[i]);
+ if( tx != tt._types[i] ) rez = rez.makeFrom(i,tx);
+ }
+ return rez;
+ }
+ return t;
+ }
}
diff --git a/src/main/java/com/seaofnodes/simple/codegen/LocalIds.java b/src/main/java/com/seaofnodes/simple/codegen/LocalIds.java
deleted file mode 100644
index 462248dd..00000000
--- a/src/main/java/com/seaofnodes/simple/codegen/LocalIds.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package com.seaofnodes.simple.codegen;
-
-import java.util.HashMap;
-import java.util.Arrays;
-
-// Dense IDs for this invocation. A class slot can be reserved before its file
-// is parsed; every later reference to that slot gets the same ID.
-public class LocalIds {
- public static final int RESERVED=2;
- private int _next=RESERVED;
- private final com.seaofnodes.simple.util.Ary<String> _owners=new com.seaofnodes.simple.util.Ary<>(String.class);
- private final HashMap<String,int[]> _slots=new HashMap<>();
-
- public int next(String owner) { return next(owner,-1); }
- public int next(String owner, int slot) {
- int[] ids=_slots.get(owner);
- if( ids==null ) ids=new int[]{RESERVED,1};
- if( slot==-1 ) slot=ids[0]++;
- else if( slot>=ids[0] ) ids[0]=slot+1;
- if( slot>=ids.length ) ids=Arrays.copyOf(ids,Math.max(slot+1,ids.length*2));
- if( ids[slot]==0 ) { ids[slot]=_next++; _owners.setX(ids[slot],owner); }
- _slots.put(owner,ids);
- return ids[slot];
- }
- public String owner(int id) { return _owners.atX(id); }
- public String symbol(int id) { return owner(id)+".$fun"+id; }
- public int nextInline() { return _next++; }
- public int next(int old) {
- int id=_next++;
- _owners.setX(id,owner(old));
- return id;
- }
-}
diff --git a/src/main/java/com/seaofnodes/simple/codegen/Module.java b/src/main/java/com/seaofnodes/simple/codegen/Module.java
new file mode 100644
index 00000000..a5d85dbc
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/codegen/Module.java
@@ -0,0 +1,98 @@
+package com.seaofnodes.simple.codegen;
+
+
+/***
+ Modules!?!?!?
+
+ Goal: under no circumstances can you load from an uninitialized variable
+ (although you can see default inits for fields that have defaults).
+ Goal: nested tree-structured name spaces, to control complexity.
+ Goal: field decl order within a file is fairly flexible.
+
+ A Module a root-level SMP file, and the full file path must be provided on
+ the command line. All other Simple structs are relative to this file.
+
+ Example: "simple /user/Dave/hello.smp"
+
+ Defines a module "hello" in directory "/user/Dave", compiles and produces
+ a `hello` or `hello.exe` output file, and runs it.
+
+ A SMP file has some <finit> file-init code which is static-global.
+ This code is executed on "first touch" of the file (defined below).
+
+ Example: hello.smp contains:
+ sys.io.p("Hello World!");
+ On first-touch, will print "Hello World!".
+
+ A SMP file is a lexical scope, and code inside it can reference those names.
+
+ Example: hello.smp contains:
+ val greet = "Hello World!"; // Declare file-scoped variable
+ sys.io.p(greet); // Reference file-scoped variable
+
+ Static code is NOT allowed to have forward-references AND be executed
+ before the <finit> finishes initializing all variables. After var
+ initialization, a singleton instance of the implied file struct is made,
+ and then <finit> code can call anything.
+
+ Example:
+ sys.io.p(greet); // Illegal forward reference
+ val greet = "Oops!";
+
+ Example:
+ val delay = { -> greet }; // Legal forward ref, but cannot execute (yet)
+ val greet = "Hello World!"; // Ref is resolved
+ sys.io.p(delay()); // Allowed to call delay()
+ val nprimes = { N ->
+ // compute number of primes < N
+ }
+ // Can otherwise execute any code in a <finit>
+ sys.io.p(nprimes(100));
+
+ Inside a directory, you can have sibling SMP files:
+ File hello.smp contains:
+ sys.io.p(I9A.GREETING); // File hello references sibling file I9A static globals
+ File I9A.smp contains:
+ val GREETING = translate("Hello World!");
+ val translate = { str msg -> ... };
+
+ <finit> code cannot reference sibling files (nor child files, see below)
+ until all static variables are initialized. This prevents cyclic
+ initialization bugs, where file A uses vars from file B and vice-versa.
+
+ File Chicken.smp contains:
+ val lay = Egg.hatch; // Illegal, defining var 'lay' after calling sibling Egg
+ File Egg.smp contains:
+ val hatch = Chicken.lay; // Illegal
+
+ You can make cross references, if they get delayed until after all fields
+ are declared.
+
+ File Chicken.smp contains:
+ int lay = 2;
+ lay = Egg.egg;
+
+ File Egg.smp contains:
+ int egg = 3;
+ egg = Chicken.lay;
+
+ The problem here, of course, is that you need to touch one of Chicken or
+ Egg first.
+
+
+ Inside a SMP file, you can define structs; structs are also a lexical
+ scope. Bare code in the struct is part of the struct's <init> and only
+ runs when an instance is created. Structs do not have static <clinit> (nor
+ <finit>). Structs <init> cannot have any forward references, and locally
+ declared functions cannot have both forward references and be executed (or
+ passed along to a possibly-executing function).
+
+ In the same directory as a SMP file, Simple allows other directories with
+ names matching SMP files. Inside these dirs can be more SMP files that are
+ lexically nested, similar to embedded structs.
+
+
+*/
+
+public abstract class Module {
+}
diff --git a/src/main/java/com/seaofnodes/simple/codegen/Opto.java b/src/main/java/com/seaofnodes/simple/codegen/Opto.java
index 4db277df..3b3f9f19 100644
--- a/src/main/java/com/seaofnodes/simple/codegen/Opto.java
+++ b/src/main/java/com/seaofnodes/simple/codegen/Opto.java
@@ -74,7 +74,7 @@ abstract public class Opto {
// Start is a semantic unknown-caller edge. Public functions
// keep it; closed-world functions must be reached by a
// concrete Call edge discovered during SCCP. Initializers,
- // including private ones, are called by opaque CLInit
+ // including private/imported ones, are called by opaque CLInit
// obligations whose Call edges are introduced only in Lower.
if( !fun.isClz() &&
!code.publicFIDX(fun.sig().fidx()) &&
diff --git a/src/main/java/com/seaofnodes/simple/codegen/ParseAll.java b/src/main/java/com/seaofnodes/simple/codegen/ParseAll.java
index 171d9923..c55f9a90 100644
--- a/src/main/java/com/seaofnodes/simple/codegen/ParseAll.java
+++ b/src/main/java/com/seaofnodes/simple/codegen/ParseAll.java
@@ -28,21 +28,34 @@ public abstract class ParseAll {
// Worklist of symbols to search and files to parse
private static final Ary<CompUnit> WORK = new Ary<>(CompUnit.class);
+ private static final Ary<CompUnit> NEEDS_LOAD = new Ary<>(CompUnit.class);
// Return a compilation unit (cached or new). If new, it does not necessarily
// need to be compiled.
public static CompUnit makeCUnit( CodeGen code, CompUnit par, String name ) throws IOException {
- int idx=name.indexOf('/');
- if( idx>=0 ) return makeCUnit(code,makeCUnit(code,par,name.substring(0,idx)),name.substring(idx+1));
- String fname=par==null ? name : par._fname+"/"+name;
- CompUnit cu=code._compunits.get(fname);
- if( cu==null ) {
- String cname=par==null ? name : par._cname+"."+name;
- code._compunits.put(fname,cu=new CompUnit(par,fname,cname,name));
- }
- return cu;
+ return makeCUnit(code,par,name,null,false);
+ }
+
+ private static CompUnit makeCUnit( CodeGen code, CompUnit par, String name, File obj ) throws IOException {
+ return makeCUnit(code,par,name,obj,obj!=null);
}
+ private static CompUnit makeCUnit( CodeGen code, CompUnit par, String name, File obj, boolean external ) throws IOException {
+ // Slashed file name? Move leading parts into parent CUs.
+ int idx = name.indexOf("/");
+ if( idx >= 0 ) {
+ String head = name.substring(0,idx);
+ CompUnit sub = makeCUnit(code,par,head,external ? externalObj(code,par,head) : null,external);
+ return makeCUnit(code,sub,name.substring(idx+1).intern(),obj,external);
+ }
+
+ String fname = par==null ? name : (par._fname + "/" + name).intern();
+ CompUnit cunit = code._compunits.get(fname);
+ if( cunit != null ) return cunit;
+ String cname = par==null ? name : (par._cname + "." + name).intern();
+ code._compunits.put(fname, cunit = external ? new CompUnit(obj,par,fname,cname,name) : new CompUnit(par,fname,cname,name));
+ return cunit;
+ }
// Parse a top-level string, used for e.g. testing
static void parseSource( CodeGen code, String src ) {
@@ -69,22 +82,29 @@ public abstract class ParseAll {
}
private static void enqueue(CompUnit cu) {
- if( cu._par!=null ) enqueue(cu._par);
+ if( cu._par!=null && (cu._par._src!=null || (cu._par._obj!=null && cu._par._obj.exists())) ) enqueue(cu._par);
if( cu._start==null && WORK.find(cu)==-1 ) WORK.add(cu);
}
private static void parseAll(CodeGen code, CompUnit cunit) {
WORK.clear(); // Cleanup from prior tests
+ NEEDS_LOAD.clear();
// Prime the pump with the first CompUnit
enqueue(cunit);
// Work through all unparsed compilation units and parse
while( !WORK.isEmpty() ) {
CompUnit cu = WORK.remove(0);
- if( cu._src==null )
- throw new RuntimeException("Missing source file '"+cu._fname+".smp'");
- parseOne(code,cu);
+ // No source code? Means we do not need to parse, but instead need to load the symbols as-if we parsed
+ if( cu._src==null || cu._src.isEmpty() )
+ loadDeps(code,cu); // Load recursive dependents
+ else
+ parseOne( code, cu ); // Parse
}
+ // Load all pre-compiled CompUnits
+ for( CompUnit cu : NEEDS_LOAD )
+ loadCompUnit(code,cu);
+
// Many types will have been declared cyclically either internally or
// across multiple compilation units.
@@ -97,7 +117,8 @@ public abstract class ParseAll {
for( TypeStruct ts : tss )
Parser.TYPES.put(ts._name,ts);
for( CompUnit cu : code._compunits.values() )
- cu._clz = (TypeStruct)Parser.TYPES.get(Parser.addClzPrefix(cu._cname));
+ if( cu._ext==null )
+ cu._clz = (TypeStruct)Parser.TYPES.get(Parser.addClzPrefix(cu._cname));
// Walk over all Nodes, and upgrade the internal constants to the
// closed-over types.
@@ -135,6 +156,9 @@ public abstract class ParseAll {
// Adds sort by NIDs, which just got shuffled
if( n instanceof AddNode add )
code.add(add);
+ // All imported bodies are loaded; link them before Iter checks types.
+ if( n instanceof CallNode call && !NEEDS_LOAD.isEmpty() )
+ code.add(call).idealize();
return null;
} );
@@ -157,6 +181,8 @@ public abstract class ParseAll {
// Search the module for the fref
try {
CompUnit xcunit = findCUnitModule(code,cunit,fref._name);
+ if( xcunit == null )
+ xcunit = findCUnitExternalSimple(code,fref._name);
if( xcunit != null ) {
// Replace the FRef with the discovered class name.
fref._name = Parser.addClzPrefix(xcunit._cname);
@@ -200,9 +226,88 @@ public abstract class ParseAll {
if( xcunit != null )
return xcunit;
}
- return null;
+ return findCUnitExternalSimple(code,typeName);
+ }
+
+ // Load the public symbols for this CompUnit, and add dependent CompUnits to the NEEDS_LOAD list.
+ private static void loadDeps(CodeGen code, CompUnit cunit) {
+ // Load IR after parsing
+ if( NEEDS_LOAD.find(cunit) != -1 )
+ return; // Already on the to-be-loaded list?
+ for( CompUnit loaded : NEEDS_LOAD )
+ if( loaded._obj.equals(cunit._obj) ) return; // Another class in the same object.
+ try {
+ NEEDS_LOAD.add(cunit);
+ // Load dependent classes
+ ElfReader elf = ElfReader.load(cunit._obj,cunit);
+ elf.loadPublicSymbols();
+ // Register bundled definitions before resolving dependencies or IR.
+ for( String fname : elf._units )
+ makeCUnit(code,null,fname,cunit._obj);
+ for( String fname : elf._deps ) {
+ // Check CompUnit for being up to date
+ CompUnit sub = code._compunits.get(fname);
+ if( sub==null ) sub=makeCUnit(code,null,fname,depObj(code,cunit,fname));
+ // If not seen before or out of date, parse the new nested cunit
+ if( sub._clz == null && WORK.find(sub)==-1 && NEEDS_LOAD.find(sub)==-1 )
+ enqueue(sub);
+ }
+ } catch( IOException ioe ) {
+ throw new RuntimeException(ioe);
+ }
+ }
+
+ private static File depObj(CodeGen code, CompUnit cunit, String fname) {
+ if( cunit._smp != null )
+ return null;
+ ElfReader elf = code.findExternalSimple(Parser.addClzPrefix(fname.replace('/','.')));
+ if( elf == null )
+ throw new RuntimeException("Cannot find external Simple dependency '"+fname+"'");
+ return elf._file;
+ }
+
+ private static File externalObj(CodeGen code, CompUnit par, String name) {
+ String cname = par==null ? name : par._cname + "." + name;
+ ElfReader elf = code.findExternalSimple(Parser.addClzPrefix(cname));
+ return elf == null ? null : elf._file;
}
+ private static void loadCompUnit(CodeGen code, CompUnit cunit) {
+ // Expecting a previously compiled object file
+ assert cunit._obj != null;
+ // Extract published symbols and put them in the internal tables;
+ // cross-references will pick them up there.
+ ElfReader elf = ElfReader.load(cunit._obj,cunit);
+ elf.loadSimple(code);
+ cunit._clz = (TypeStruct)Parser.TYPES.get(Parser.addClzPrefix(cunit._cname));
+ StartNode lstart = (StartNode)elf._nodes.at(0);
+ StopNode lstop = ( StopNode)elf._nodes.last();
+
+ // Add all new loaded functions to the linker table
+ for( Node n : lstop._inputs ) {
+ StopCUNode stop = (StopCUNode)n;
+ StartCUNode start = stop.start();
+ CompUnit cu = code._compunits.get(start._fname);
+ cu._start = start;
+ cu._stop = stop ;
+
+ for( Node ret : stop._inputs ) {
+ FunNode fun = ((ReturnNode)ret).fun();
+ code.link(fun);
+ }
+ }
+
+ // Hook the new code into the existing code graph
+ for( Node lstopcu : lstop._inputs )
+ code._stop.addDef(lstopcu);
+ lstart.subsume(code._start);
+
+ // Add the new top-level loaded class
+ Parser.TYPES.put(cunit._clz._name,cunit._clz);
+ Field inst = cunit._clz.field(cunit._name);
+ if( inst != null && inst._t instanceof TypeFunPtr tfp && tfp._ret instanceof TypeMem mem )
+ Parser.TYPES.put(cunit._cname,mem._t);
+ }
// Symbol not found in source code (or we would not be here).
// Search the module for the fref
@@ -219,7 +324,7 @@ public abstract class ParseAll {
CompUnit local = findCUnitModule(code,cu,symbol);
if( local != null )
return local;
- return null;
+ return findCUnitExternalSimple(code,symbol);
} catch( IOException ioe ) {
throw new RuntimeException(ioe);
}
@@ -235,12 +340,8 @@ public abstract class ParseAll {
// However if A/B contains "Y", the search would be for A.B.Y then A.Y both of
// which do not exist.
private static CompUnit findCUnitModule( CodeGen code, CompUnit cunit, String symbol ) throws IOException {
- if( cunit == null ) {
- if( !symbol.equals("sys") || !CompUnit.source("sys").exists() ) return null;
- CompUnit sys=makeCUnit(code,null,"sys");
- enqueue(sys);
- return sys;
- }
+ if( cunit == null ) // Ran off top of module scope?
+ return null; // Failed search
// Check self
if( cunit._name.equals(symbol) )
@@ -256,10 +357,11 @@ public abstract class ParseAll {
String pre = symbol.substring(0,dotIdx);
String rest = symbol.substring(dotIdx+1 );
String fname = cunit._fname + "/" + pre;
- File f = CompUnit.source(fname);
+ File f = new File( code._modDir + "/" + fname + ".smp" );
if( f.exists() ) {
CompUnit sub = makeCUnit(code,cunit,pre);
- // enqueue also parses parents: their initialization must precede a child.
+ // Note that "sub" itself is not put on the to-be-parsed-list. We're
+ // referring to a child class and do not need to parse the middles.
CompUnit dcunit = findCUnitModule(code,sub,rest);
if( dcunit != null )
return dcunit; // Nested dot search found cunit
@@ -268,7 +370,7 @@ public abstract class ParseAll {
// Look in mod / fname / name / symbol.smp
String fname = cunit._fname + "/" + symbol;
- File f = CompUnit.source(fname);
+ File f = new File( code._modDir + "/" + fname + ".smp" );
if( f.exists() ) {
CompUnit sub = makeCUnit(code,cunit,symbol);
// If not seen before or out of date, parse the new nested cunit
@@ -281,5 +383,34 @@ public abstract class ParseAll {
return findCUnitModule( code, cunit._par, symbol );
}
+ // Search external Simple objects only. C linkage uses explicit "C"
+ // declarations, which produce ExternNodes and are resolved by the linker.
+ public static CompUnit findCUnitExternalSimple( CodeGen code, String symbol ) {
+ String clzName = symbol.startsWith(Parser.CLZ) ? symbol : Parser.addClzPrefix(symbol);
+ String cname = clzName.substring(Parser.CLZ.length());
+ String fname = cname.replace('.','/');
+ CompUnit cunit = code._compunits.get(fname);
+ if( cunit != null )
+ return cunit;
+
+ ElfReader elf = code.findExternalSimple(clzName);
+ if( elf == null )
+ return null;
+
+ try {
+ cunit = makeCUnit(code,null,fname,elf._file);
+ } catch( IOException ioe ) {
+ throw new RuntimeException(ioe);
+ }
+ ElfReader typedElf = ElfReader.load(cunit._obj,cunit);
+ if( typedElf != null ) typedElf.loadPublicTypes(code);
+ TypeStruct clz = (TypeStruct)Parser.TYPES.get(clzName);
+ if( clz == null )
+ Parser.TYPES.put(clzName,clz = TypeStruct.make(clzName,true));
+ cunit._clz = clz;
+ if( WORK.find(cunit) == -1 && NEEDS_LOAD.find(cunit) == -1 )
+ enqueue(cunit);
+ return cunit;
+ }
}
diff --git a/src/main/java/com/seaofnodes/simple/codegen/Serialize.java b/src/main/java/com/seaofnodes/simple/codegen/Serialize.java
new file mode 100644
index 00000000..5a74ded2
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/codegen/Serialize.java
@@ -0,0 +1,687 @@
+package com.seaofnodes.simple.codegen;
+
+import com.seaofnodes.simple.Parser;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.*;
+import com.seaofnodes.simple.util.*;
+import java.util.*;
+
+abstract public class Serialize {
+
+ // Encoding/emulation need only machine code. File export also needs ideal
+ // IR for a later compiler to import; an explicit Serialize request stops here.
+ static void forTarget(CodeGen code, CodeGen.Phase target, boolean inMemory) {
+ if( target==CodeGen.Phase.Serialize || (target.ordinal()>=CodeGen.Phase.Export.ordinal() && !inMemory) )
+ code.serialize();
+ }
+
+ static void serialize( CodeGen code ) {
+
+ // Get all Nodes in a sane order
+ Ary<Node> nodes = nodeOrder(code);
+
+ // Array of dependent file names
+ HashSet<String> xdeps = new HashSet<>();
+ for( CompUnit cu : code._compunits.values() ) {
+ if( cu._deps != null )
+ for( CompUnit cudep : cu._deps )
+ xdeps.add(cudep._fname);
+ }
+ String[] deps = xdeps.toArray(new String[xdeps.size()]);
+ Arrays.sort(deps);
+ Ary<String> owned = new Ary<>(String.class);
+ for( CompUnit cu : code._compunits.values() )
+ if( cu._src!=null ) owned.add(cu._fname);
+ String[] units = owned.asAry();
+ Arrays.sort(units);
+
+ // Top-level clazz being reported
+ String topName = code._srcName == null ? "Test" : code._srcName;
+ CompUnit top = code._compunits.get(topName);
+ TypeStruct clz = top._clz;
+
+ // Preserve which same-named structs are the source-level declarations.
+ // Optimized graph types can carry sharper field states (e.g. len==0),
+ // and cannot be used to reconstruct this mapping during loading.
+ TreeMap<String,TypeStruct> named = new TreeMap<>();
+ for( Type type : Parser.TYPES.values() )
+ if( type instanceof TypeStruct ts )
+ named.put(ts._name,ts);
+ TypeStruct[] pubTypes = named.values().toArray(new TypeStruct[0]);
+
+ // Compress into bytes
+ BAOS baos = write(nodes, clz, pubTypes, deps, units, code._aliases, code._fidxs, code._rpcs, code._externFunc);
+
+ // --- Expensive bijection assert
+ if( false ) {
+ // Inflate into POJOs; renumbers everything
+ ElfReader elf = new ElfReader(new BAOS(baos.toByteArray()));
+ readAll(code,elf, code._aliases, code._fidxs, code._rpcs);
+ BAOS baos2 = write(elf._nodes,elf._clz,elf._pubTypes,elf._deps,elf._units, code._aliases, code._fidxs, code._rpcs, code._externFunc);
+
+ // Bi-jection
+ for( int i=0; i<baos.size(); i++ )
+ assert baos.buf()[i]==baos2.buf()[i] : "bijection mismatch at "+i+": "+(baos.buf()[i]&0xFF)+" != "+(baos2.buf()[i]&0xFF);
+ assert baos.size()==baos2.size() : "bijection size mismatch: "+baos.size()+" != "+baos2.size();
+ }
+ // --- Expensive bijection assert
+
+ // Record serialized IR for later ELF writing
+ code._serial = baos;
+ }
+
+ // --------------------------------------------------
+ static BAOS write(Ary<Node> nodes, TypeStruct clz, TypeStruct[] pubTypes, String[] depobjs, String[] units, GlobalBits aliases, GlobalBits fidxs, GlobalBits rpcs, HashMap<Integer,String> externFunc) {
+ // Initialize the mapping from bits/tags to types
+ Type.TAGOFFS();
+
+ IdentityHashMap<Node,Integer> anodes = new IdentityHashMap<>();
+ for( Node n : nodes ) anodes.put(n,anodes.size()+1); // 0 is null.
+ // The saved wrappers summarize this object's units only. Keep the
+ // live whole-program graph intact for subsequent lowering.
+ TypeTuple stopType = TypeTuple.STOP_HIGH;
+ for( Node n : nodes.last()._inputs )
+ if( anodes.containsKey(n) ) stopType = (TypeTuple)stopType.meet(n._type);
+ Type startType = TypeTuple.make(Type.CONTROL,stopType.mem(),((StartNode)nodes.at(0)).arg());
+
+ BAOS baos = new BAOS();
+ // A - Print the C0DE header. The format is not versioned yet.
+ baos.write('C').write('0').write('D').write('E');
+
+ // Count unique Types
+ var types = new HashMap<Type,Integer>();
+ // Count published types *first*, so can align with published strings
+ types.put(Type.TOP,0);
+ types.put( clz, types.size() );
+ // Have to visit the innards specially, since they did not get the
+ // normal recursive visit when first touching a type, because the
+ // published types are packed early to align with their string names so
+ // lookup in ElfReader can be fast.
+ for( int i=0; i<clz.nkids(); i++ )
+ clz.at(i).gather(types);
+ // Gather types from all sources
+ for( Node n : nodes ) {
+ n._type.gather(types);
+ if( n instanceof CallEndNode cend ) cend._rpc.gather(types);
+ if( n instanceof FunNode fun ) fun.sig().gather(types);
+ if( n instanceof ParmNode parm ) parm._declaredType.gather(types);
+ if( n instanceof TypeNode con ) con._con .gather(types);
+ }
+ stopType.gather(types);
+ startType.gather(types);
+ // A defined class remains importable even when its initializer body dies.
+ // Nested structs also need their class declaration: <new> and <ctor>
+ // can be absent from graph types even while their function bodies live.
+ // Keep the nearest file owner, so an imported child is not re-exported
+ // merely because its parent was compiled here.
+ for( TypeStruct ts : pubTypes )
+ if( ownedClass(ts,units) )
+ ts.gather(types);
+
+ // Only record canonical declarations already reachable from this
+ // object's graph. Pulling unrelated parser types into this closure
+ // can include open or partially built types from other units.
+ Ary<TypeStruct> writtenPubs = new Ary<>(TypeStruct.class);
+ for( TypeStruct ts : pubTypes )
+ if( types.containsKey(ts) )
+ writtenPubs.add(ts);
+ // Radix sort by Type ID#
+ Type[] atypes = new Type[types.size()];
+ for( Type t : types.keySet() )
+ atypes[types.get(t)] = t;
+
+ // Count all unique Strings
+ var strs = new HashMap<String,Integer>();
+ strs.put("",0); // Null string is always 0
+ // Find and count *published* Strings first
+ gather(strs,clz._name);
+ // Find and count dependent object file string names
+ if( depobjs != null )
+ for( String fname : depobjs )
+ gather(strs,fname);
+ for( String fname : units ) gather(strs,fname);
+ // Count strings from all types
+ for( Type t : atypes ) {
+ if( t instanceof Field fld ) gather(strs,fld._fname);
+ if( t instanceof TypeStruct ts ) gather(strs,ts . _name);
+ }
+ // Count strings used by local->global bit mappings.
+ aliases.gather(strs);
+ fidxs .gather(strs);
+ rpcs .gather(strs);
+ for( String extern : externFunc.values() )
+ gather(strs,extern);
+ // Count strings from nodes
+ for( Node n : nodes )
+ n.gather(strs);
+ // Radix sort by string ID#
+ String[] astrs = new String[strs.size()];
+ for( String s : strs.keySet() )
+ astrs[strs.get(s)] = s;
+
+ // Check that published strings and types align
+ assert astrs[1] == ((TypeStruct)atypes[1])._name;
+ if( depobjs != null )
+ for( int i=0; i<depobjs.length; i++ )
+ assert astrs[1+i+1] == depobjs[i];
+
+
+ // B - Write unique strings
+ baos.packed4(depobjs==null ? 0 : depobjs.length);
+ baos.packed4(strs.size());
+ // Write in ID# order; the first depobjs.length of these are dependent strings
+ for( String s : astrs )
+ baos.packedS(s);
+
+ // Defined units are loader metadata, not initialization dependencies.
+ baos.packed4(units.length);
+ for( String fname : units ) baos.packed4(strs.get(fname));
+
+ // B.2 - Write the local->global mappings
+ aliases.packed(baos,strs);
+ fidxs .packed(baos,strs);
+ rpcs .packed(baos,strs);
+ ArrayList<Integer> externFidxs = new ArrayList<>(externFunc.keySet());
+ Collections.sort(externFidxs);
+ baos.packed2(externFidxs.size());
+ for( int fidx : externFidxs ) {
+ baos.packed2(fidx);
+ baos.packed2(strs.get(externFunc.get(fidx)));
+ }
+
+ // C - Write unique Types
+ baos.packed4(atypes.length);
+ baos.packed2(writtenPubs._len);
+ for( TypeStruct ts : writtenPubs )
+ baos.packed4(types.get(ts));
+ // Write Types in ID# order, no children
+ for( Type t : atypes )
+ t.packed(baos,strs);
+ // Write Types in ID# order, only child IDs
+ for( Type t : atypes ) {
+ int nkids = t.nkids();
+ for( int i=0; i<nkids; i++ )
+ baos.packed4(types.get(t.at(i)));
+ }
+
+
+ // D - Write the nodes. Since this is expected to be the bulk of the
+ // data, we might want to explore various options.
+
+ // How many nodes?
+ baos.packed4(nodes._len);
+
+ // First cut: 1 byte opcode, optional per-node info, includes variable nIns
+ for( Node n : nodes ) {
+ baos.packed1(n.serialTag().ordinal());
+ baos.packed2(types.get(n==nodes.at(0) ? startType : n==nodes.last() ? stopType : n._type));
+ n.packed(baos,strs,types,anodes);
+ }
+ // Write out the input indices packed.
+ for( Node n : nodes ) {
+ for( int j=0; j<n.nIns(); j++ ) {
+ if( !writeInput(n,j,anodes) ) continue;
+ Node def = n.in(j);
+ Integer idx = def==null ? Integer.valueOf(0) : anodes.get(def);
+ // Imported function constants retain their symbol, not a body.
+ if( idx==null && n instanceof FunPtrNode && j==1 ) idx=0;
+ assert idx != null : "Missing serialized input for "+n+" input "+j+" def "+def;
+ baos.packed2(idx);
+ }
+ if( n instanceof FunNode fun )
+ baos.packed2(anodes.get(fun.ret()));
+ }
+
+ return baos;
+ }
+
+ // Omit cross-object call-graph links without unlinking the live IR.
+ // A parameter follows exactly the same retained callers as its function.
+ private static boolean writeInput(Node n, int i, IdentityHashMap<Node,Integer> nodes) {
+ if( n instanceof ParmNode parm ) n=parm.fun();
+ if( (n instanceof FunNode || n instanceof CallEndNode) && i>0 )
+ return nodes.containsKey(n.in(i));
+ if( n instanceof StopNode ) return nodes.containsKey(n.in(i));
+ return true;
+ }
+
+ public static int nIns(Node n, IdentityHashMap<Node,Integer> nodes) {
+ int len=0;
+ for( int i=0; i<n.nIns(); i++ ) if( writeInput(n,i,nodes) ) len++;
+ return len;
+ }
+ public static <T> void gather( HashMap<T,Integer> strs, T s) {
+ if( s!=null && !strs.containsKey(s) )
+ strs.put(s,strs.size());
+ }
+ public static void gather( AryInt is, int s) {
+ if( is.atX(s)==0 ) {
+ int cnt=0;
+ for( int i=1; i<is._len; i++ )
+ if( is._es[i] != 0 )
+ cnt++;
+ is.setX(s,cnt+1);
+ }
+ }
+
+ // Returned array of *public* strings only.
+ private static boolean ownedClass(TypeStruct ts, String[] units) {
+ if( !Parser.startsClzPrefix(ts._name) ) return false;
+ String name=ts._name.substring(Parser.CLZ.length()).replace('.','/');
+ while( true ) {
+ if( CodeGen.CODE._compunits.containsKey(name) )
+ return Arrays.binarySearch(units,name)>=0;
+ int slash=name.lastIndexOf('/');
+ if( slash<0 ) return false;
+ name=name.substring(0,slash);
+ }
+ }
+
+ static void read_public_strs(ElfReader elf) {
+ BAOS bais = elf._bais;
+ Type.TAGOFFS();
+
+ // A - Read a header
+ if( bais.read()!='C' || bais.read()!='0' || bais.read()!='D' || bais.read()!='E' )
+ throw new IllegalArgumentException("Unsupported serialized IR format; rebuild the object file");
+
+ // B - Packed read of #strings, then strings
+ int ndependents = bais.packed4(); // Number of dependent object files
+ int nstrs = bais.packed4(); // Total number of strings
+ String[] strs = new String[nstrs];
+ for( int i=0; i<nstrs; i++ )
+ strs[i] = bais.packedS();
+ elf._strs = strs;
+
+ // Collect dependent file names
+ elf._deps = new String[ndependents];
+ for( int i=0; i<ndependents; i++ )
+ elf._deps[i] = strs[1+i+1];
+ elf._units = new String[bais.packed4()];
+ for( int i=0; i<elf._units.length; i++ )
+ elf._units[i] = strs[bais.packed4()];
+ }
+
+ // --------------------------------------------------
+ record TypeRead(Type[] types, String[] strs, GlobalBits fileAliases, GlobalBits fileFidxs, GlobalBits fileRpcs) {}
+
+ static TypeRead readTypes( CodeGen code, ElfReader elf, GlobalBits aliases, GlobalBits fidxs, GlobalBits rpcs ) {
+ BAOS bais = elf._bais;
+ // Initialize the mapping from bits/tags to types
+ Type.TAGOFFS();
+
+ // If not read the strings already, read them now
+ String[] strs = elf._strs;
+ if( strs == null ) {
+ // A - Read a header
+ if( bais.read() != 'C' || bais.read() != '0' || bais.read() != 'D' || bais.read() != 'E' )
+ throw new IllegalArgumentException( "Unsupported serialized IR format; rebuild the object file" );
+
+ // B - Packed read of #strings, then strings
+ int ndependents = bais.packed4(); // Number of dependent object files
+ int nstrs = bais.packed4(); // Total number of strings
+ elf._strs = strs = new String[nstrs];
+ for( int i = 0; i < nstrs; i++ )
+ strs[i] = bais.packedS();
+
+ // Collect dependent file names
+ elf._deps = new String[ndependents];
+ for( int i = 0; i < ndependents; i++ )
+ elf._deps[i] = strs[1 + i + 1];
+ elf._units = new String[bais.packed4()];
+ for( int i=0; i<elf._units.length; i++ )
+ elf._units[i] = strs[bais.packed4()];
+ }
+
+ // B.2 - Read the local->global mappings. Further down we will map
+ // Elf file-local -> global, then global -> CodeGen.CODE local
+ GlobalBits fileAliases = GlobalBits.packed(bais,strs);
+ GlobalBits fileFidxs = GlobalBits.packed(bais,strs);
+ GlobalBits fileRpcs = GlobalBits.packed(bais,strs);
+ int nexterns = bais.packed2();
+ for( int i=0; i<nexterns; i++ ) {
+ int fileFidx = bais.packed2();
+ String extern = strs[bais.packed2()];
+ int fidx = fileFidx < GlobalBits.RESERVED ? fileFidx : fidxs.map(fileFidxs,fileFidx);
+ String old = code.externFunc(fidx);
+ if( old == null )
+ code.externFunc(fidx,extern);
+ else
+ assert old.equals(extern);
+ }
+
+
+ // C - Packed read of #types, then types
+ int ntypes = bais.packed4();
+ int npubTypes = bais.packed2();
+ int[] pubTypeIds = new int[npubTypes];
+ for( int i=0; i<npubTypes; i++ )
+ pubTypeIds[i] = bais.packed4();
+ Type[] types = Type.packed(bais,strs,ntypes, Parser.TYPES,
+ fileAliases,aliases,
+ fileFidxs ,fidxs,
+ fileRpcs ,rpcs);
+
+ // aliases.set(0,0);
+ // Check and collect first published symbols map to TypeStructs
+ elf._clz = (TypeStruct)types[1/*skip zero*/];
+ assert elf._clz._name==strs[1/*skip null ptr*/];
+ for( Type type : types )
+ if( type instanceof TypeStruct ts )
+ Parser.TYPES.put(ts._name,ts);
+ for( Type type : types ) {
+ if( !(type instanceof TypeStruct ts) || !Parser.startsClzPrefix(ts._name) )
+ continue;
+ String cname = ts._name.substring(Parser.CLZ.length());
+ int dot = cname.lastIndexOf('.');
+ String name = dot == -1 ? cname : cname.substring(dot+1);
+ Field inst = ts.field(name.intern());
+ if( inst != null && inst._t instanceof TypeFunPtr tfp && tfp._ret instanceof TypeMem mem && mem._t instanceof TypeStruct its )
+ Parser.TYPES.put(cname,its);
+ }
+ elf._pubTypes = new TypeStruct[npubTypes];
+ for( int i=0; i<npubTypes; i++ ) {
+ TypeStruct ts = elf._pubTypes[i] = (TypeStruct)types[pubTypeIds[i]];
+ Parser.TYPES.put(ts._name,ts);
+ }
+ return new TypeRead(types,strs,fileAliases,fileFidxs,fileRpcs);
+ }
+
+ static void readAll( CodeGen code, ElfReader elf, GlobalBits aliases, GlobalBits fidxs, GlobalBits rpcs ) {
+ BAOS bais = elf._bais;
+ TypeRead tread = readTypes(code, elf, aliases, fidxs, rpcs);
+ Type[] types = tread.types();
+ String[] strs = tread.strs();
+ GlobalBits fileAliases = tread.fileAliases();
+
+ // D - Read the nodes
+
+ // Number of nodes
+ int num = bais.packed4();
+ // Packed array of nodes
+ Ary<Node> nodes = new Ary<>(Node.class);
+ CompUnit cu = null;
+ for( int i=0; i<num; i++ ) {
+ Node.Tag tag = Node.Tag.VALS[bais.packed1()];
+ Type t = types[bais.packed2()];
+ Node n = tag.make(bais,strs,types,fileAliases,aliases);
+ n._type = t;
+ nodes.push(n);
+ // A little extra for FunNodes
+ if( n instanceof StartCUNode startcu ) {
+ cu = code._compunits.get(startcu._fname==null ? (code._srcName==null ? "Test" : code._srcName) : startcu._fname);
+ assert cu != null;
+ }
+ if( n instanceof StopCUNode stopcu ) {
+ cu = null;
+ }
+ if( n instanceof FunNode fun ) {
+ assert cu != null;
+ fun._compunit = cu;
+ }
+ if( n instanceof ExternNode ext && ext._con instanceof TypeFunPtr tfp && tfp.isConstant() ) {
+ String old = code.externFunc(tfp.fidx());
+ if( old == null )
+ code.externFunc(tfp.fidx(), ext._extern);
+ else
+ assert old.equals(ext._extern);
+ }
+ }
+
+ // Node edges
+ for( Node n : nodes ) {
+ int len = n.nIns();
+ for( int i=0; i<len; i++ ) {
+ int idx = bais.packed2();
+ Node def = idx==0 ? null : nodes.at(idx-1);
+ // MemMerge precise aliases are encoded by their input slot.
+ // Remap populated file-local slots into this compilation's
+ // local alias numbering; null holes carry no identity.
+ int local = i;
+ if( n instanceof MemMergeNode && i >= GlobalBits.RESERVED ) {
+ if( def == null ) continue;
+ local = aliases.map(fileAliases,i);
+ }
+ n.setDefX(local,def);
+ // Call's output[0] is always the CallEnd
+ if( n instanceof CallEndNode cend && i==0 ) {
+ var outs = cend.call()._outputs;
+ outs.swap(0,outs._len-1);
+ }
+ }
+ // A little extra for FunNodes
+ if( n instanceof FunNode fun ) {
+ // Return is hooked like an edge
+ ReturnNode ret = (ReturnNode)nodes.at(bais.packed2()-1);
+ fun.setRet(ret);
+ ret._fun = fun;
+ }
+ }
+ // Add edge from Start to Stop
+ StartNode start = (StartNode)nodes.at(0);
+ StopNode stop = (StopNode)nodes.last();
+ start.setDef(1,stop);
+
+ assert check(code,nodes);
+ elf._nodes = nodes;
+ }
+
+ // Check loaded types and nodes are consistent
+ private static boolean check( CodeGen code, Ary<Node> nodes ) {
+ // Loaded nodes are complete, unlike nodes still being built by the
+ // parser. Recompute them with post-parse semantics. The compilation-
+ // unit Stop is the exception: its escape type depends on the complete
+ // program's public interface, which ParseAll freezes only after every
+ // compilation unit has been loaded.
+ boolean good = true;
+ CodeGen.Phase phase = code._phase;
+ code._phase = CodeGen.Phase.Opto;
+ try {
+ for( Node n : nodes ) {
+ if( n instanceof StopCUNode && !code.publicInterfaceFrozen() )
+ continue;
+ // Cross-object calls are relinked only after every body has
+ // loaded. Their saved return types cannot be recomputed yet.
+ if( n instanceof CallEndNode end && end.call().fptr()._type instanceof TypeFunPtr fp &&
+ end.nIns()<=fp.nfcns() ) continue;
+ Type nval = n.compute();
+ if( nval != n._type ) {
+ System.err.println("Node "+n+" read in as "+n._type+" but computes as "+nval);
+ good = false;
+ }
+ }
+ } finally {
+ code._phase = phase;
+ }
+ return good;
+ }
+
+ // --------------------------------------------------
+ public static Ary<Node> nodeOrder( CodeGen code, CompUnit cu ) {
+ BitSet visit = code.visit();
+ Ary<Node> nodes = new Ary<>(Node.class);
+ visit.set(code._stop._nid);
+ // Walk all listed functions, in this single compilation unit
+ for( FunNode fun : code._linker )
+ if( fun!=null && !fun.isDead() && fun._compunit == cu )
+ _funWalk(fun,nodes,visit);
+
+ // Just constants used by the listed functions
+ Ary<Node> cons = new Ary<>(Node.class);
+ cons.add(code._start);
+ for( Node n : code._start._outputs ) {
+ if( n instanceof ConstantNode con && !visit.get(con._nid) ) {
+ for( Node use : con.outs() ) {
+ // PtrToInt is another zero-code value rooted at a global
+ // constant. Its array-body Add is the value used by the
+ // function, so include that extra link in the ownership
+ // test as well.
+ if( use instanceof PtrToIntNode ptr )
+ for( Node add : ptr.outs() ) {
+ for( Node addUse : add.outs() )
+ conUsed(add,addUse,visit,cons);
+ conUsed(ptr,add,visit,cons);
+ }
+ // Constant used by somebody, so flag for output
+ conUsed(con,use,visit,cons);
+ }
+ }
+ }
+ // StartCU and memory projection
+ cons.add(cu._start);
+ for( Node n : cu._start._outputs )
+ if( n instanceof ProjNode ) cons.add(n);
+ // All the innards of functions
+ cons.addAll(nodes);
+ // StopCU, Stop
+ cons.add(cu._stop);
+ cons.add(code._stop);
+ visit.clear();
+ return cons;
+ }
+
+ private static void conUsed( Node con, Node use, BitSet visit, Ary<Node> cons ) {
+ if( use == null || !visit.get(use._nid) || visit.get(con._nid) ) return;
+ cons.add(con);
+ visit.set(con._nid);
+ }
+
+ // --------------------------------------------------
+ public static Ary<Node> nodeOrder( CodeGen code ) {
+ Ary<Node> nodes = new Ary<>(Node.class);
+ BitSet visit = code.visit();
+ nodes.add(code._start);
+
+ // All the global constants
+ for( Node n : code._start._outputs ) {
+ if( n.isConst() ) {
+ visit.set( n._nid );
+ nodes.add( n );
+ // Special for Cast:BOT
+ for( Node cast : n._outputs )
+ if( cast != null && cast.nIns() == 2 && cast.in( 0 ) == null ) {
+ visit.set( cast._nid );
+ nodes.add( cast );
+ // Array pointer conversion adds the body offset after
+ // the zero-code PtrToInt. Both nodes are global-value
+ // descendants of the constant and otherwise have no
+ // function-local root from which to walk them.
+ if( cast instanceof PtrToIntNode )
+ for( Node add : cast.outs() )
+ if( add instanceof AddNode && !visit.get(add._nid) ) {
+ visit.set(add._nid);
+ nodes.add(add);
+ }
+ }
+ }
+ }
+
+ // All the CompUnits
+ for( CompUnit cu : code._compunits.values() ) {
+ // Whole CompUnits have no users, went dead
+ if( cu._src!=null && cu._start != null && !cu._start.isDead() ) {
+ nodes.add(cu._start);
+ // A memory-free, nonreturning unit can lose its memory projection.
+ ProjNode mem=cu._start.proj(1);
+ if( mem!=null ) nodes.add(mem);
+ // All the functions, including internal ones, in comp-unit order
+ for( FunNode fun : code._linker )
+ if( fun!=null && !fun.isDead() && fun._compunit == cu )
+ _funWalk(fun,nodes,visit);
+ nodes.add(cu._stop);
+ }
+ }
+
+ nodes.add(code._stop);
+ visit.clear();
+ return nodes;
+ }
+
+ // Walk and print whole functions at a time, in CG order
+ private static void _funWalk( FunNode fun, Ary<Node> rpo, BitSet visit ) {
+ int start = rpo._len;
+ _funRPO(fun,rpo,visit);
+ // A nonreturning function still owns a Return, even before LoopTree
+ // inserts synthetic exits. No loop-depth ordering is needed on disk.
+ _funRPO(fun.ret(),rpo,visit);
+ int len = rpo._len - start;
+ for( int i=0; i< len>>1; i++ )
+ rpo.swap(start+i,start+len-1-i);
+ }
+
+ // Walk and gather RPO nodes.
+ private static void _funRPO(Node n, Ary<Node> rpo, BitSet visit ) {
+ if( visit.get(n._nid) ) return; // Been there, done that
+ visit.set(n._nid); // Stop recursion
+ // Walk outputs ordered
+ if( n instanceof CFGNode ncfg ) {
+ if( n instanceof IfNode iff ) {
+ CProjNode c0 = iff.cproj(0);
+ CProjNode c1 = iff.cproj(1);
+ _funRPO(c0,rpo,visit);
+ _funRPO(c1,rpo,visit);
+ } else if( n instanceof StopNode ) {
+ // nothing
+ } else {
+ // CFG to CFG before CFG to data
+ for( Node use : n._outputs )
+ if( use instanceof CFGNode usecfg && !ncfg.skip(usecfg) ) // Do not walk from Call to Fun
+ _funRPO(use,rpo,visit);
+ }
+ // Walk CFG to data eventually
+ for( Node use : n._outputs )
+ if( !(use instanceof CFGNode) )
+ _funRPO(use,rpo,visit);
+ } else {
+ // Do not walk from a non-CFG to a CFG; CFGs walk to CFGs to
+ // preserve CFG order. Do not walk *into* Phis; wait for the
+ // Region to walk the Phis (otherwise you visit some of the
+ // Phis ahead of others forcing the Phis to be spread around).
+ for( Node use : n._outputs ) {
+ if( use!=null && !(use instanceof CFGNode) && !(use instanceof PhiNode) )
+ _funRPO(use,rpo,visit);
+ }
+ }
+
+ // If parent is a Multi, do not add (yet).
+ // If self is a Multi, add self and children.
+ if( multiChild(n) || n instanceof CallEndNode ) {
+ // nothing; delay until Multi-head or CallNode
+ } else if( n instanceof MultiNode ) {
+ // Now lump all multi/projections together
+ printMulti(n,rpo);
+ } else if( n instanceof CallNode call ) {
+ printMulti(call.cend(),rpo);
+ rpo.add(call);
+ } else if( n instanceof RegionNode ) {
+ // Now lump all Phis together
+ int old = rpo._len;
+ for( Node use : n._outputs )
+ if( use instanceof PhiNode )
+ rpo.add(use);
+ // Sort by label? Want Phis before control outputs
+ Arrays.sort( rpo._es, old, rpo._len, (x,y) -> y.label().compareTo(x.label()) );
+ rpo.add(n);
+ } else if( n instanceof StopNode ) {
+ // nothing
+ } else {
+ rpo.add(n); // Post-order add
+ }
+ }
+
+ private static boolean multiChild(Node n) {
+ return n instanceof Proj || n instanceof PhiNode;
+ }
+
+ private static void printMulti(Node n, Ary<Node> rpo) {
+ // Now lump all multi/projections together
+ for( Node use : n._outputs )
+ rpo.add(use);
+ // Sort by projection order
+ if( !(n instanceof StartNode) )
+ Arrays.sort( rpo._es,rpo._len-n.nOuts(),rpo._len,
+ (x,y) -> x instanceof ProjNode px && y instanceof ProjNode py ? (py.idx() - px.idx()) : (y._nid - x._nid) );
+ rpo.add(n);
+ }
+
+}
diff --git a/src/main/java/com/seaofnodes/simple/codegen/Unlink.java b/src/main/java/com/seaofnodes/simple/codegen/Unlink.java
new file mode 100644
index 00000000..fb1c4a6f
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/codegen/Unlink.java
@@ -0,0 +1,112 @@
+package com.seaofnodes.simple.codegen;
+
+import com.seaofnodes.simple.node.*;
+
+// Discard call-graph edges after LoopTree. Machine selection keeps only this
+// object's bodies; ideal evaluation keeps imports so their calls can execute.
+abstract class Unlink {
+ private static void unlinkImports(CodeGen code) {
+ // Imported function constants keep their symbolic identity, not an
+ // embedded copy of the other object's Return and body.
+ for( Node n : code._start._outputs.asAry() )
+ if( n instanceof FunPtrNode ptr && ptr.ret()!=null && !code.owns(ptr.fun()) ) {
+ ptr.liftType(ptr._type);
+ ptr.setDef(1,null);
+ }
+ for( FunNode fun : code._linker ) {
+ if( fun==null || fun.isDead() || code.owns(fun) )
+ continue;
+ for( int i=1; i<fun.nIns(); i++ )
+ if( fun.in(i) instanceof CallNode call )
+ call.unlink(fun,i--);
+ }
+ for( int i=0; i<code._stop.nIns(); i++ ) {
+ StopCUNode stop=(StopCUNode)code._stop.in(i);
+ CompUnit cu=code._compunits.get(stop.start()._fname);
+ if( cu._src==null ) code._stop.delDef(i--);
+ }
+ // The whole-object wrappers now summarize only the emitted units.
+ code._stop ._type=code._stop .compute();
+ code._start._type=code._start.compute();
+ }
+
+ static void run(CodeGen code, boolean removeImports) {
+ if( removeImports ) unlinkImports(code);
+
+ // The remaining passes assume all calls are unlinked; i.e. we are throwing
+ // away the Call Graph here. Functions only reachable from internal calls
+ // need to be re-hooked to stop/start less they go dead.
+ for( FunNode fun : code._linker ) {
+ // Already linked to start, not going dead
+ if( fun==null || fun.isDead() )
+ continue;
+
+ // Imported functions are not emitted by this object. Ideal
+ // evaluation retains them so their calls can actually execute.
+ if( removeImports && !code.owns(fun) ) {
+ while( fun.nIns() > 1 ) {
+ if( fun.in(1) instanceof CallNode call )
+ call.unlink(fun,1);
+ else
+ fun.removeDeadPath(1);
+ }
+ StartCUNode start = fun._compunit._start;
+ StopCUNode stop = fun._compunit._stop;
+ // Unhook the Stop->Return edge also
+ ReturnNode ret = fun.ret();
+ int idx = stop._inputs.find(ret);
+ if( idx != -1 ) {
+ stop._inputs.del(idx);
+ ret.delUse(stop);
+ }
+ //
+ if( stop.nIns()==0 ) {
+ idx = code._stop._inputs.find(stop);
+ if( idx != -1 )
+ code._stop.delDef(idx);
+ if( start.nIns() > 0 && start.in(0) != null )
+ start.setDef(0,null);
+ if( start.nIns() > 1 && start.in(1) != null )
+ start.setDef(1,null);
+ }
+ continue;
+ }
+
+ // Insert the post-Opto code-generation hook. Unlike an Opto-time
+ // Start input, this no longer carries unknown-caller semantics.
+ ReturnNode ret = fun.ret();
+ StartNode start = fun._compunit._start;
+ assert start!=null;
+ if( fun.nIns() <= 1 || fun.in(1) != start ) {
+ fun.insertDef(1,start);
+ for( Node use : fun._outputs )
+ if( use instanceof ParmNode parm )
+ parm.insertDef(1,ConstantNode.raw(parm._type));
+ if( fun._compunit._stop._inputs.find(ret) == -1 )
+ fun._compunit._stop.addDef(ret);
+ }
+ // Unlink from Call
+ for( int i=2; i<fun.nIns(); i++ )
+ ((CallNode)fun.in(i)).unlink(fun,i--);
+ if( fun.rpc()==null ) { // Ensure valid RPC
+ // First make sure a valid RPC; single-call into multi-fun will have each
+ // function having a single call site, so the RPC becomes a constant and
+ // folds - but since multiple targets, the Call never inlines. Recreate a
+ // valid RPC so codegen (and Eval2) understands the calling convention.
+ ParmNode rpc = new ParmNode( "$rpc", 0, ret.rpc()._type, fun );
+ rpc.addDef(ret.rpc());
+ ret.setDef( 3, rpc.init() );
+ }
+ }
+
+ code._stop.walk(n -> {
+ if( n instanceof CallEndNode cend )
+ for( int i=1; i<cend.nIns(); i++ )
+ if( cend.in(i) instanceof ReturnNode )
+ cend.delDef(i--);
+ if( n instanceof CallNode call )
+ call.unlink_all();
+ return null;
+ });
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/AddNode.java b/src/main/java/com/seaofnodes/simple/node/AddNode.java
index 8835208b..a29ba73b 100644
--- a/src/main/java/com/seaofnodes/simple/node/AddNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/AddNode.java
@@ -7,7 +7,7 @@ public class AddNode extends ArithNode {
@Override boolean allowFloat() { return true; }
public AddNode(Node lhs, Node rhs) { super(null, lhs, rhs); }
public AddNode(Node lhs, Node rhs, byte mode) { super(null, lhs, rhs, mode); }
-
+ @Override public Tag serialTag() { return Tag.Add; }
@Override public String label() { return "Add"; }
@Override public String op() { return "+"; }
diff --git a/src/main/java/com/seaofnodes/simple/node/AndNode.java b/src/main/java/com/seaofnodes/simple/node/AndNode.java
index a737b703..f02007bc 100644
--- a/src/main/java/com/seaofnodes/simple/node/AndNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/AndNode.java
@@ -6,7 +6,7 @@ import com.seaofnodes.simple.type.TypeInteger;
public class AndNode extends ArithNode {
public AndNode(Parser.Lexer loc, Node lhs, Node rhs) { super(loc, lhs, rhs, (byte)1); }
-
+ @Override public Tag serialTag() { return Tag.And; }
@Override public String op() { return "&"; }
@Override long doOp( long x, long y ) { return x & y; }
diff --git a/src/main/java/com/seaofnodes/simple/node/ArithNode.java b/src/main/java/com/seaofnodes/simple/node/ArithNode.java
index 094b927f..92ebd587 100644
--- a/src/main/java/com/seaofnodes/simple/node/ArithNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/ArithNode.java
@@ -5,6 +5,7 @@ import com.seaofnodes.print.ExprPrinter;
import com.seaofnodes.simple.Parser;
import com.seaofnodes.simple.codegen.CodeGen;
import com.seaofnodes.simple.type.*;
+import com.seaofnodes.simple.util.BAOS;
import com.seaofnodes.simple.util.Utils;
import java.util.HashMap;
@@ -18,7 +19,9 @@ public abstract class ArithNode extends Node implements ModeNode {
public ArithNode( Parser.Lexer loc, Node lhs, Node rhs) { super(null, lhs, rhs); _loc = loc; }
public ArithNode( Parser.Lexer loc, Node lhs, Node rhs, byte mode) { this(loc,lhs,rhs); _mode=mode; }
-
+ @Override public void packed(BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types, IdentityHashMap<Node,Integer> anodes) {
+ baos.packed1(_mode);
+ }
abstract String op();
@Override protected ExprPrinter<Node> _print1(ExprPrinter<Node> p) {
diff --git a/src/main/java/com/seaofnodes/simple/node/BoolNode.java b/src/main/java/com/seaofnodes/simple/node/BoolNode.java
index dd0bc996..73e786e3 100644
--- a/src/main/java/com/seaofnodes/simple/node/BoolNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/BoolNode.java
@@ -5,6 +5,7 @@ import com.seaofnodes.print.ExprPrinter;
import com.seaofnodes.simple.Parser;
import com.seaofnodes.simple.codegen.CodeGen;
import com.seaofnodes.simple.type.*;
+import com.seaofnodes.simple.util.BAOS;
import com.seaofnodes.simple.util.Utils;
import java.util.HashMap;
import java.util.IdentityHashMap;
@@ -17,7 +18,9 @@ abstract public class BoolNode extends Node implements ModeNode {
public BoolNode(Node lhs, Node rhs) { super(null, lhs, rhs); }
public BoolNode(Node lhs, Node rhs, byte mode) { super(null, lhs, rhs); _mode = mode; }
-
+ @Override public void packed(BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types, IdentityHashMap<Node,Integer> anodes) {
+ baos.packed1(_mode);
+ }
abstract public String op(); // String opcode name
@@ -116,7 +119,7 @@ abstract public class BoolNode extends Node implements ModeNode {
public static class EQ extends BoolNode {
public EQ(Node lhs, Node rhs) { super(lhs,rhs); }
public EQ(Node lhs, Node rhs, byte mode) { super(lhs,rhs, mode); }
-
+ @Override public Tag serialTag() { return Tag.EQ; }
public String op() { return "=="; }
TypeInteger doOp(TypeInteger i1, TypeInteger i2) {
if( i1==i2 && i1.isConstant() ) return TRUE;
@@ -130,7 +133,7 @@ abstract public class BoolNode extends Node implements ModeNode {
public static class NE extends BoolNode {
public NE(Node lhs, Node rhs) { super(lhs,rhs); }
public NE(Node lhs, Node rhs, byte mode) { super(lhs,rhs,mode); }
-
+ @Override public Tag serialTag() { return Tag.NE; }
public String op() { return "!="; }
TypeInteger doOp(TypeInteger i1, TypeInteger i2) {
if( i1==i2 && i1.isConstant() ) return TRUE;
@@ -144,7 +147,7 @@ abstract public class BoolNode extends Node implements ModeNode {
public static class LT extends BoolNode {
public LT(Node lhs, Node rhs) { super(lhs,rhs); }
public LT(Node lhs, Node rhs, byte mode) { super(lhs,rhs, mode); }
-
+ @Override public Tag serialTag() { return Tag.LT; }
public String op() { return "<" ; }
TypeInteger doOp(TypeInteger i1, TypeInteger i2) {
if( i1._max < i2._min ) return TRUE;
@@ -158,7 +161,7 @@ abstract public class BoolNode extends Node implements ModeNode {
public static class LE extends BoolNode {
public LE(Node lhs, Node rhs) { super(lhs,rhs); }
public LE(Node lhs, Node rhs, byte mode) { super(lhs,rhs,mode); }
-
+ @Override public Tag serialTag() { return Tag.LE; }
public String op() { return "<="; }
TypeInteger doOp(TypeInteger i1, TypeInteger i2) {
if( i1._max <= i2._min ) return TRUE;
@@ -176,7 +179,7 @@ abstract public class BoolNode extends Node implements ModeNode {
super(lhs,rhs,(byte)1);
assert mode==1;
}
-
+ @Override public Tag serialTag() { return Tag.ULT; }
public String op() { return "u<" ; }
TypeInteger doOp(TypeInteger i1, TypeInteger i2) {
if( Long.compareUnsigned(i1._max,i2._min) < 0 ) return TRUE;
diff --git a/src/main/java/com/seaofnodes/simple/node/BulkMemPhiNode.java b/src/main/java/com/seaofnodes/simple/node/BulkMemPhiNode.java
index df9e4fff..c6a6ff3b 100644
--- a/src/main/java/com/seaofnodes/simple/node/BulkMemPhiNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/BulkMemPhiNode.java
@@ -1,8 +1,10 @@
package com.seaofnodes.simple.node;
import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.GlobalBits;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeMem;
+import com.seaofnodes.simple.util.BAOS;
import com.seaofnodes.simple.util.Utils;
import java.util.BitSet;
@@ -44,6 +46,28 @@ public class BulkMemPhiNode extends PhiNode {
public boolean isSplit(int alias) { return _aliases.get(alias); }
+ @Override public Tag serialTag() { return Tag.BulkMemPhi; }
+
+ @Override
+ public void packed(BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types,
+ IdentityHashMap<Node,Integer> anodes) {
+ super.packed(baos,strs,types,anodes);
+ baos.packed2(_aliases.cardinality());
+ for( int alias = _aliases.nextSetBit(0); alias >= 0; alias = _aliases.nextSetBit(alias+1) )
+ baos.packed2(alias);
+ }
+
+ static Node make(BAOS bais, String[] strs, Type[] types, GlobalBits fileAliases, GlobalBits aliases) {
+ Node[] ins = new Node[bais.packed1()];
+ String label = strs[bais.packed2()];
+ BitSet bits = new BitSet();
+ for( int i=bais.packed2(); i>0; i-- ) {
+ int alias = bais.packed2();
+ if( alias >= GlobalBits.RESERVED ) alias = aliases.map(fileAliases,alias);
+ bits.set(alias);
+ }
+ return new BulkMemPhiNode(label,bits.isEmpty() ? EMPTY : bits,ins);
+ }
@Override
public Type compute() {
diff --git a/src/main/java/com/seaofnodes/simple/node/CLInitNode.java b/src/main/java/com/seaofnodes/simple/node/CLInitNode.java
index 0a0de3cf..7bc17c75 100644
--- a/src/main/java/com/seaofnodes/simple/node/CLInitNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/CLInitNode.java
@@ -5,6 +5,7 @@ import com.seaofnodes.simple.Parser;
import com.seaofnodes.simple.codegen.CodeGen;
import com.seaofnodes.simple.codegen.CompUnit;
import com.seaofnodes.simple.type.*;
+import com.seaofnodes.simple.util.BAOS;
import java.util.*;
// Track class initializers - the code just after this point expects the named
@@ -22,6 +23,12 @@ public class CLInitNode extends CFGNode implements MultiNode {
_loc = loc;
}
+ CLInitNode(BAOS bais, String[] strs) { super(null,null,null,null); _loc=Parser.Lexer.unpack(bais,strs); }
+ @Override public Tag serialTag() { return Tag.CLInit; }
+ @Override public void packed( BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types, IdentityHashMap<Node, Integer> anodes ) {
+ Parser.Lexer.packed(_loc,baos,strs);
+ }
+ @Override public void gather(HashMap<String,Integer> strs) { Parser.Lexer.gather(_loc,strs); }
public CLInitNode(CLInitNode init) {
super(init);
diff --git a/src/main/java/com/seaofnodes/simple/node/CProjNode.java b/src/main/java/com/seaofnodes/simple/node/CProjNode.java
index 2bd48d92..073e4658 100644
--- a/src/main/java/com/seaofnodes/simple/node/CProjNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/CProjNode.java
@@ -3,8 +3,10 @@ package com.seaofnodes.simple.node;
import com.seaofnodes.print.ExprPrinter;
import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.Serialize;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeTuple;
+import com.seaofnodes.simple.util.BAOS;
import java.util.HashMap;
import java.util.IdentityHashMap;
@@ -23,7 +25,14 @@ public class CProjNode extends CFGNode implements Proj {
_label = label;
}
public CProjNode(CProjNode c) { super(c); _idx = c._idx; _label = c._label; }
-
+ @Override public Tag serialTag() { return Tag.CProj; }
+ @Override public void packed( BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types, IdentityHashMap<Node, Integer> anodes ) {
+ baos.packed1(_idx);
+ baos.packed2(_label==null ? 0 : strs.get(_label));
+ }
+ static Node make( BAOS bais, String[] strs ) {
+ return new CProjNode(null, bais.packed1(), strs[bais.packed2()] );
+ }
@Override public String label() { return _label; }
@@ -70,4 +79,7 @@ public class CProjNode extends CFGNode implements Proj {
@Override public int idx() { return _idx; }
+ @Override public void gather( HashMap<String,Integer> strs ) {
+ Serialize.gather(strs,_label);
+ }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/CallEndNode.java b/src/main/java/com/seaofnodes/simple/node/CallEndNode.java
index 4422265f..fc6effdc 100644
--- a/src/main/java/com/seaofnodes/simple/node/CallEndNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/CallEndNode.java
@@ -5,6 +5,7 @@ import com.seaofnodes.print.ExprPrinter;
import com.seaofnodes.simple.Parser;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.type.*;
+import com.seaofnodes.simple.util.BAOS;
import java.util.HashMap;
import java.util.IdentityHashMap;
@@ -37,7 +38,21 @@ public class CallEndNode extends CFGNode implements MultiNode {
super(new Node[nIns]);
_rpc = rpc;
}
-
+ @Override public Tag serialTag() { return Tag.CallEnd; }
+ public void packed( BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types, IdentityHashMap<Node, Integer> anodes ) {
+ baos.packed1(Serialize.nIns(this,anodes));
+ // Linked CallEnds depend on Return types which depend on CallEnds;
+ // break the cycle
+ baos.packed2(types.get(_type));
+ baos.packed2(types.get(_rpc));
+ }
+ static Node make( BAOS bais, Type[] types ) {
+ int nIns = bais.packed1();
+ Type type = types[bais.packed2()];
+ Node cend = new CallEndNode(nIns,(TypeRPC)types[bais.packed2()]);
+ cend._type = type;
+ return cend;
+ }
@Override public boolean blockHead() { return true; }
diff --git a/src/main/java/com/seaofnodes/simple/node/CallNode.java b/src/main/java/com/seaofnodes/simple/node/CallNode.java
index f791225a..bcb756fa 100644
--- a/src/main/java/com/seaofnodes/simple/node/CallNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/CallNode.java
@@ -9,6 +9,7 @@ import com.seaofnodes.simple.type.TypeFunPtr;
import com.seaofnodes.simple.type.TypeMem;
import com.seaofnodes.simple.type.TypeRPC;
import com.seaofnodes.simple.type.XInt;
+import com.seaofnodes.simple.util.BAOS;
import com.seaofnodes.simple.util.Utils;
import java.util.HashMap;
import java.util.IdentityHashMap;
@@ -23,7 +24,16 @@ public class CallNode extends CFGNode {
public CallNode(Parser.Lexer loc, Node... nodes) { super(nodes); _loc=loc; CodeGen.CODE._calls.add(this); }
public CallNode(CallNode call) { super(call); _loc = call._loc; }
-
+ @Override public Tag serialTag() { return Tag.Call; }
+ public void packed( BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types, IdentityHashMap<Node, Integer> anodes ) {
+ baos.packed1(nIns());
+ Parser.Lexer.packed(_loc,baos,strs);
+ }
+ @Override public void gather(HashMap<String,Integer> strs) { Parser.Lexer.gather(_loc,strs); }
+ static Node make( BAOS bais, String[] strs ) {
+ int nIns = bais.packed1();
+ return new CallNode(Parser.Lexer.unpack(bais,strs),new Node[nIns]);
+ }
@Override protected ExprPrinter<Node> _print1(ExprPrinter<Node> p) {
String fname = name();
@@ -44,6 +54,7 @@ public class CallNode extends CFGNode {
}
+
Node ctrl() { return in(0); }
Node mem () { return in(1); }
// Args mapped 1-to-1 on inputs, so conceptually start at 2
diff --git a/src/main/java/com/seaofnodes/simple/node/ConFldOffNode.java b/src/main/java/com/seaofnodes/simple/node/ConFldOffNode.java
index 0b8053e2..284220ea 100644
--- a/src/main/java/com/seaofnodes/simple/node/ConFldOffNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/ConFldOffNode.java
@@ -21,7 +21,7 @@ public class ConFldOffNode extends ConstantNode {
public TypeStruct _ts; // Struct holding field
public final String _fname; // Field name
public ConFldOffNode( TypeStruct ts, String fname ) { super(TypeInteger.BOT); _ts = ts; _fname = fname; }
-
+ @Override public Tag serialTag() { throw Utils.TODO("should not reach here"); }
@Override public String label() {
return _fname == " len" ? "sizeof("+_ts._name+")" : "#"+_fname;
diff --git a/src/main/java/com/seaofnodes/simple/node/ConstantNode.java b/src/main/java/com/seaofnodes/simple/node/ConstantNode.java
index eabf2639..0569f352 100644
--- a/src/main/java/com/seaofnodes/simple/node/ConstantNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/ConstantNode.java
@@ -5,6 +5,7 @@ import com.seaofnodes.print.ExprPrinter;
import com.seaofnodes.simple.codegen.CodeGen;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeFunPtr;
+import com.seaofnodes.simple.util.BAOS;
import java.util.HashMap;
import java.util.IdentityHashMap;
@@ -30,7 +31,11 @@ public class ConstantNode extends TypeNode {
_type = type;
}
public ConstantNode( ConstantNode con ) { super(con); }
-
+ @Override public Tag serialTag() { return Tag.Con; }
+ @Override public void packed( BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types, IdentityHashMap<Node, Integer> anodes ) {
+ baos.packed2(types.get(_con)); // NPE if fails lookup
+ }
+ static Node make( BAOS bais, Type[] types) { return raw(types[bais.packed2()]); }
// Semantic constant construction through Lower. A unique internal
// function identity carries a lifetime edge to the function's Return.
diff --git a/src/main/java/com/seaofnodes/simple/node/CtrlNode.java b/src/main/java/com/seaofnodes/simple/node/CtrlNode.java
index 813e6fcb..62140873 100644
--- a/src/main/java/com/seaofnodes/simple/node/CtrlNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/CtrlNode.java
@@ -8,7 +8,7 @@ import com.seaofnodes.simple.type.Type;
public class CtrlNode extends CFGNode {
public CtrlNode() { super(CodeGen.CODE._start); }
-
+ @Override public Tag serialTag() { throw com.seaofnodes.simple.util.Utils.TODO(); }
@Override public String label() { return "Ctrl"; }
@Override protected ExprPrinter<Node> _print1(ExprPrinter<Node> p) { return p.p("Cctrl"); }
@Override public boolean isConst() { return true; }
diff --git a/src/main/java/com/seaofnodes/simple/node/DivNode.java b/src/main/java/com/seaofnodes/simple/node/DivNode.java
index 0db2b7a2..f173f0ef 100644
--- a/src/main/java/com/seaofnodes/simple/node/DivNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/DivNode.java
@@ -9,7 +9,7 @@ public class DivNode extends ArithNode {
@Override boolean allowFloat() { return true; }
public DivNode(Node lhs, Node rhs) { super(null, lhs, rhs); }
public DivNode(Node lhs, Node rhs, byte mode) { super(null, lhs, rhs, mode); }
-
+ @Override public Tag serialTag() { return Tag.Div; }
@Override public String op() { return _mode==1 ? "//" : "/"; }
diff --git a/src/main/java/com/seaofnodes/simple/node/EscapeNode.java b/src/main/java/com/seaofnodes/simple/node/EscapeNode.java
index f7518184..3e95fffb 100644
--- a/src/main/java/com/seaofnodes/simple/node/EscapeNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/EscapeNode.java
@@ -35,6 +35,13 @@ public class EscapeNode extends TypeNode {
public Node pub () { return in(3); }
public Field fld() { return (Field)_con; }
+ @Override public Tag serialTag() { return Tag.Escape; }
+ @Override public void packed( BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types, IdentityHashMap<Node, Integer> anodes ) {
+ baos.packed2(types.get(_con));
+ }
+ static Node make( BAOS bais, Type[] types) {
+ return new EscapeNode((Field)types[bais.packed2()], null,null,null);
+ }
@Override
protected ExprPrinter<Node> _print1(ExprPrinter<Node> p) {
diff --git a/src/main/java/com/seaofnodes/simple/node/ExternNode.java b/src/main/java/com/seaofnodes/simple/node/ExternNode.java
index 2d1dc29f..978298fc 100644
--- a/src/main/java/com/seaofnodes/simple/node/ExternNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/ExternNode.java
@@ -1,6 +1,8 @@
package com.seaofnodes.simple.node;
+import com.seaofnodes.simple.codegen.Serialize;
import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.util.BAOS;
import com.seaofnodes.simple.util.SB;
import java.util.BitSet;
@@ -17,11 +19,20 @@ public class ExternNode extends ConstantNode {
// Its lifetime and eventual relocation are carried by the extern symbol
// and CodeGen's FIDX-to-extern mapping instead.
public ExternNode(Type t, String ex) { super(t,true); _extern = ex; }
-
+ @Override public Tag serialTag() { return Tag.Extern; }
+ @Override public void packed( BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types, IdentityHashMap<Node, Integer> anodes ) {
+ baos.packed2(types.get(_con ));
+ baos.packed2( strs.get(_extern));
+ }
+ static Node make( BAOS bais, String[] strs, Type[] types) {
+ return new ExternNode(types[bais.packed2()], strs[bais.packed2()]);
+ }
@Override public String label() { return "#"+_con+":"+_extern; }
@Override public boolean eq(Node n) { return this==n; }
-
+ @Override public void gather( HashMap<String,Integer> strs ) {
+ Serialize.gather(strs,_extern);
+ }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/ExternOffsetNode.java b/src/main/java/com/seaofnodes/simple/node/ExternOffsetNode.java
index 23ed5468..409ce5d1 100644
--- a/src/main/java/com/seaofnodes/simple/node/ExternOffsetNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/ExternOffsetNode.java
@@ -2,6 +2,7 @@ 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;
@@ -10,7 +11,9 @@ import java.util.IdentityHashMap;
// 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/src/main/java/com/seaofnodes/simple/node/FldOffNode.java b/src/main/java/com/seaofnodes/simple/node/FldOffNode.java
index 535241a6..a9f8d692 100644
--- a/src/main/java/com/seaofnodes/simple/node/FldOffNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/FldOffNode.java
@@ -19,7 +19,7 @@ public class FldOffNode extends TypeNode {
public final String _fname; // Field name
public final Parser.Lexer _loc; //
public FldOffNode( Node base, String fname, Parser.Lexer loc ) { super(TypeInteger.BOT,base); _fname = fname; _loc=loc; }
-
+ @Override public Tag serialTag() { throw Utils.TODO("should not reach here"); }
@Override public String label() {
return _fname == " len" ? "sizeof()" : "#"+_fname;
diff --git a/src/main/java/com/seaofnodes/simple/node/FunNode.java b/src/main/java/com/seaofnodes/simple/node/FunNode.java
index ad79130e..edde247a 100644
--- a/src/main/java/com/seaofnodes/simple/node/FunNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/FunNode.java
@@ -3,6 +3,7 @@ package com.seaofnodes.simple.node;
import com.seaofnodes.simple.Parser;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.type.*;
+import com.seaofnodes.simple.util.BAOS;
import com.seaofnodes.simple.util.SB;
import com.seaofnodes.simple.util.Utils;
import java.util.Arrays;
@@ -41,7 +42,24 @@ public class FunNode extends RegionNode {
_compunit = fun._compunit;
_extern = fun._extern;
}
-
+ @Override public Tag serialTag() { return Tag.Fun; }
+ @Override public void packed(BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types, IdentityHashMap<Node,Integer> anodes ) {
+ assert !_folding;
+ baos.packed1(Serialize.nIns(this,anodes)); // Object-local linked calls
+ baos.packed2(types.get(_sig)); // NPE if fails lookup
+ baos.packed2(_name==null ? 0 : strs.get(_name));
+ baos.packed2(_approxUIDs);
+ // _compunit set from context on loading, do not write
+ }
+ static Node make( BAOS bais, String[] strs, Type[] types) {
+ Node[] ins = new Node[bais.packed1()];
+ TypeFunPtr sig = (TypeFunPtr)types[bais.packed2()];
+ String name = strs[bais.packed2()];
+ FunNode fun = new FunNode(null,sig,name,null,ins);
+ fun._approxUIDs = bais.packed2();
+ // _compunit set from context on loading, do not read
+ return fun;
+ }
@Override public String label() { return _name == null ? "$fun"+_sig.fidx() : _name; }
@@ -460,4 +478,7 @@ public class FunNode extends RegionNode {
return slotRotate*8;
}
+ @Override public void gather( HashMap<String,Integer> strs ) {
+ Serialize.gather(strs,_name);
+ }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/FunPtrNode.java b/src/main/java/com/seaofnodes/simple/node/FunPtrNode.java
index 3d6031b8..82053fd8 100644
--- a/src/main/java/com/seaofnodes/simple/node/FunPtrNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/FunPtrNode.java
@@ -4,6 +4,7 @@ import com.seaofnodes.print.ExprPrinter;
import com.seaofnodes.simple.codegen.CodeGen;
import com.seaofnodes.simple.type.*;
+import com.seaofnodes.simple.util.BAOS;
import java.util.HashMap;
import java.util.IdentityHashMap;
@@ -14,8 +15,13 @@ public class FunPtrNode extends TypeNode {
super(tfp,start,ret);
}
public FunPtrNode(FunPtrNode fptr) { super(fptr); }
-
-
+ @Override public Tag serialTag() { return Tag.FunPtr; }
+ @Override public void packed(BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types, IdentityHashMap<Node,Integer> anodes) {
+ baos.packed2(types.get(_con));
+ }
+ static Node make(BAOS bais, Type[] types) {
+ return new FunPtrNode((TypeFunPtr)types[bais.packed2()],null,null);
+ }
public ReturnNode ret() { return (ReturnNode)in(1); }
public FunNode fun() { return ret().fun(); }
diff --git a/src/main/java/com/seaofnodes/simple/node/GuardNode.java b/src/main/java/com/seaofnodes/simple/node/GuardNode.java
index e985da72..0ce504d9 100644
--- a/src/main/java/com/seaofnodes/simple/node/GuardNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/GuardNode.java
@@ -4,6 +4,7 @@ import com.seaofnodes.print.ExprPrinter;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeScalar;
+import com.seaofnodes.simple.util.BAOS;
import com.seaofnodes.simple.util.Utils;
import java.util.HashMap;
@@ -24,6 +25,11 @@ public class GuardNode extends Node {
_nonZero = guard._nonZero;
}
+ @Override public Tag serialTag() { return Tag.Guard; }
+ @Override public void packed(BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types, IdentityHashMap<Node,Integer> anodes) {
+ baos.write(_nonZero ? 1 : 0);
+ }
+ static Node make(BAOS bais) { return new GuardNode(bais.read()!=0,null,null); }
@Override public String label() { return _nonZero ? "(!=0)" : "(==0)"; }
diff --git a/src/main/java/com/seaofnodes/simple/node/IfNode.java b/src/main/java/com/seaofnodes/simple/node/IfNode.java
index 471a28fe..c83c81d0 100644
--- a/src/main/java/com/seaofnodes/simple/node/IfNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/IfNode.java
@@ -13,7 +13,7 @@ public class IfNode extends CFGNode implements MultiNode {
CodeGen.CODE.add(this); // Because idoms are complex, just add it
}
public IfNode(IfNode iff) { super(iff); }
-
+ @Override public Tag serialTag() { return Tag.If; }
@Override
protected ExprPrinter<Node> _print1(ExprPrinter<Node> p) {
diff --git a/src/main/java/com/seaofnodes/simple/node/LoadNode.java b/src/main/java/com/seaofnodes/simple/node/LoadNode.java
index e1a495ad..c58bba39 100644
--- a/src/main/java/com/seaofnodes/simple/node/LoadNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/LoadNode.java
@@ -4,7 +4,9 @@ import com.seaofnodes.print.ExprPrinter;
import com.seaofnodes.simple.Parser;
import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.GlobalBits;
import com.seaofnodes.simple.type.*;
+import com.seaofnodes.simple.util.BAOS;
import com.seaofnodes.simple.util.Utils;
import java.util.BitSet;
@@ -25,7 +27,11 @@ public class LoadNode extends MemOpNode {
public LoadNode(Parser.Lexer loc, String name, int alias, Node ctrl, Node mem, Node ptr, Node off) {
super(loc, name, alias, true, Type.BOTTOM, ctrl, mem, ptr, off);
}
-
+ LoadNode( BAOS bais, String[] strs, Type[] types, GlobalBits fileAliases, GlobalBits aliases ) {
+ super(bais,strs,types,fileAliases,aliases,true);
+ _con = Type.BOTTOM; // Serialized slot retained while MemOps share TypeNode
+ }
+ @Override public Tag serialTag() { return Tag.Load; }
@Override boolean canDrop(MemOpNode other, Node dep) {
return super.canDrop(other,dep) && !clobbered(dep);
diff --git a/src/main/java/com/seaofnodes/simple/node/LoopNode.java b/src/main/java/com/seaofnodes/simple/node/LoopNode.java
index 005a6624..df686bcf 100644
--- a/src/main/java/com/seaofnodes/simple/node/LoopNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/LoopNode.java
@@ -4,6 +4,7 @@ import com.seaofnodes.simple.Parser;
import com.seaofnodes.simple.codegen.CodeGen;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeMem;
+import com.seaofnodes.simple.util.BAOS;
import java.util.HashMap;
import java.util.IdentityHashMap;
@@ -11,7 +12,8 @@ import java.util.IdentityHashMap;
public class LoopNode extends RegionNode {
public LoopNode( Parser.Lexer loc, Node xctrl, Node entry ) { super(loc,xctrl,entry,null); }
public LoopNode( LoopNode loop ) { super(loop); }
-
+ @Override public Tag serialTag() { return Tag.Loop; }
+ public void packed( BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types, IdentityHashMap<Node, Integer> anodes ) { }
public CFGNode entry() { return cfg(1); }
public CFGNode back () { return cfg(2); }
diff --git a/src/main/java/com/seaofnodes/simple/node/MemMergeNode.java b/src/main/java/com/seaofnodes/simple/node/MemMergeNode.java
index cbf19c55..dfe8794d 100644
--- a/src/main/java/com/seaofnodes/simple/node/MemMergeNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/MemMergeNode.java
@@ -4,6 +4,7 @@ import com.seaofnodes.print.ExprPrinter;
import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.util.BAOS;
import com.seaofnodes.simple.type.*;
import com.seaofnodes.simple.util.Utils;
@@ -17,7 +18,9 @@ public class MemMergeNode extends Node {
public MemMergeNode( Node ...nodes) { super(nodes); _type = TypeMem.BOT; }
public MemMergeNode(MemMergeNode mem) { super(mem); }
-
+ @Override public Tag serialTag() { return Tag.MemMerge; }
+ public void packed( BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types, IdentityHashMap<Node, Integer> anodes ) { baos.packed1(nIns()); }
+ static Node make( BAOS bais ) { Node mem = new MemMergeNode(); mem.setDefX(bais.packed1()-1,null); return mem; }
@Override public String label() { return "ALLMEM"; }
@Override public boolean isMem() { return true; }
diff --git a/src/main/java/com/seaofnodes/simple/node/MemOpNode.java b/src/main/java/com/seaofnodes/simple/node/MemOpNode.java
index fa399e1d..bfbd46c3 100644
--- a/src/main/java/com/seaofnodes/simple/node/MemOpNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/MemOpNode.java
@@ -4,6 +4,7 @@ import com.seaofnodes.print.ExprPrinter;
import com.seaofnodes.simple.Parser;
import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.GlobalBits;
import com.seaofnodes.simple.type.*;
import com.seaofnodes.simple.util.*;
import java.lang.StringBuilder;
@@ -60,6 +61,24 @@ public abstract class MemOpNode extends TypeNode {
_isLoad= mop._isLoad;
}
+ MemOpNode( BAOS bais, String[] strs, Type[] types, GlobalBits fileAliases, GlobalBits aliases, boolean isLoad ) {
+ this(null,
+ strs[bais.packed2()],
+ mapAlias(bais,fileAliases,aliases),isLoad,
+ types[bais.packed2()],null,null,null,null);
+ }
+
+ private static int mapAlias( BAOS bais, GlobalBits fileAliases, GlobalBits aliases ) {
+ int alias = bais.packed2();
+ return alias < GlobalBits.RESERVED ? alias : aliases.map(fileAliases,alias);
+ }
+
+ @Override public void packed( BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types, IdentityHashMap<Node, Integer> anodes ) {
+ baos.packed2(_name==null ? 0 : strs.get(_name));
+ baos.packed2(_alias);
+ baos.packed2(types.get(_con)); // NPE if fails lookup
+ assert _isLoad == this instanceof LoadNode; // No machine ops
+ }
static String mlabel(String name) { return "[]"==name ? "ary" : ("#"==name ? "len" : name); }
String mlabel() { return mlabel(_name); }
diff --git a/src/main/java/com/seaofnodes/simple/node/MemPhiNode.java b/src/main/java/com/seaofnodes/simple/node/MemPhiNode.java
index 5ae716a7..aaaedb89 100644
--- a/src/main/java/com/seaofnodes/simple/node/MemPhiNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/MemPhiNode.java
@@ -2,6 +2,8 @@ package com.seaofnodes.simple.node;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeMem;
+import com.seaofnodes.simple.codegen.GlobalBits;
+import com.seaofnodes.simple.util.BAOS;
import java.util.HashMap;
import java.util.IdentityHashMap;
@@ -22,6 +24,22 @@ public class MemPhiNode extends PhiNode {
_alias = phi._alias;
}
+ @Override public Tag serialTag() { return Tag.MemPhi; }
+
+ @Override
+ public void packed(BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types,
+ IdentityHashMap<Node,Integer> anodes) {
+ super.packed(baos,strs,types,anodes);
+ baos.packed2(_alias);
+ }
+
+ static Node make(BAOS bais, String[] strs, Type[] types, GlobalBits fileAliases, GlobalBits aliases) {
+ Node[] ins = new Node[bais.packed1()];
+ String label = strs[bais.packed2()];
+ int alias = bais.packed2();
+ if( alias >= GlobalBits.RESERVED ) alias = aliases.map(fileAliases,alias);
+ return new MemPhiNode(label,alias,ins);
+ }
@Override
public Type compute() {
diff --git a/src/main/java/com/seaofnodes/simple/node/MinusNode.java b/src/main/java/com/seaofnodes/simple/node/MinusNode.java
index 4d7e3ddb..e84eb611 100644
--- a/src/main/java/com/seaofnodes/simple/node/MinusNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/MinusNode.java
@@ -4,6 +4,7 @@ package com.seaofnodes.simple.node;
import com.seaofnodes.simple.Parser;
import com.seaofnodes.simple.codegen.CodeGen;
import com.seaofnodes.simple.type.*;
+import com.seaofnodes.simple.util.BAOS;
import com.seaofnodes.simple.util.Utils;
import java.util.HashMap;
import java.util.IdentityHashMap;
@@ -14,7 +15,10 @@ public class MinusNode extends Node implements ModeNode {
public MinusNode(Node in) { super(null, in); }
public MinusNode(Node in, byte mode) { super(null, in); _mode = mode; }
-
+ @Override public void packed(BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types, IdentityHashMap<Node,Integer> anodes) {
+ baos.packed1(_mode);
+ }
+ @Override public Tag serialTag() { return Tag.Minus; }
@Override public byte mode() { return _mode; }
@Override public Node setMode(byte mode) {
diff --git a/src/main/java/com/seaofnodes/simple/node/MulNode.java b/src/main/java/com/seaofnodes/simple/node/MulNode.java
index 49c6432a..bb99cad5 100644
--- a/src/main/java/com/seaofnodes/simple/node/MulNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/MulNode.java
@@ -9,7 +9,7 @@ public class MulNode extends ArithNode {
@Override boolean allowFloat() { return true; }
public MulNode(Node lhs, Node rhs) { super(null, lhs, rhs); }
public MulNode(Node lhs, Node rhs, byte mode) { super(null, lhs, rhs, mode); }
-
+ @Override public Tag serialTag() { return Tag.Mul; }
@Override public String op() { return "*"; }
diff --git a/src/main/java/com/seaofnodes/simple/node/NeverNode.java b/src/main/java/com/seaofnodes/simple/node/NeverNode.java
index a7b4cf65..ba1dab8c 100644
--- a/src/main/java/com/seaofnodes/simple/node/NeverNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/NeverNode.java
@@ -12,7 +12,7 @@ import com.seaofnodes.simple.type.TypeTuple;
public class NeverNode extends IfNode {
public NeverNode(NeverNode ctrl) { super(ctrl); }
public NeverNode(float ignore, Node ctrl) { super(ctrl,null); }
-
+ @Override public Tag serialTag() { return Tag.Never; }
@Override public String label() { return "Never"; }
diff --git a/src/main/java/com/seaofnodes/simple/node/NewNode.java b/src/main/java/com/seaofnodes/simple/node/NewNode.java
index 5cbe1b61..fe80f690 100644
--- a/src/main/java/com/seaofnodes/simple/node/NewNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/NewNode.java
@@ -4,6 +4,7 @@ import com.seaofnodes.print.ExprPrinter;
import com.seaofnodes.simple.Parser;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.type.*;
+import com.seaofnodes.simple.util.BAOS;
import com.seaofnodes.simple.util.SB;
import java.util.HashMap;
import java.util.IdentityHashMap;
@@ -22,7 +23,14 @@ public class NewNode extends Node implements MultiNode {
// --- Serialization
public NewNode(NewNode nnn) { super(nnn); _ts = nnn._ts; }
-
+ @Override public Tag serialTag() { return Tag.New; }
+ @Override public void packed( BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types, IdentityHashMap<Node, Integer> anodes ) {
+ baos.packed2(types.get(_ts));
+ }
+ static Node make( BAOS bais, Type[] types) {
+ TypeStruct ts = (TypeStruct)types[bais.packed2()];
+ return new NewNode(ts,null,null);
+ }
@Override public String label() { return "new_"+(_ts.isAry() ? "ary_"+_ts._fields[1]._t.str() : _ts.str()); }
diff --git a/src/main/java/com/seaofnodes/simple/node/Node.java b/src/main/java/com/seaofnodes/simple/node/Node.java
index de39ca80..8718f351 100644
--- a/src/main/java/com/seaofnodes/simple/node/Node.java
+++ b/src/main/java/com/seaofnodes/simple/node/Node.java
@@ -5,6 +5,7 @@ import com.seaofnodes.print.BaseNode;
import com.seaofnodes.simple.IterPeeps;
import com.seaofnodes.simple.Parser;
import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.GlobalBits;
import com.seaofnodes.simple.print.IRPrinter;
import com.seaofnodes.simple.type.*;
import com.seaofnodes.simple.util.*;
@@ -67,8 +68,84 @@ public abstract class Node extends BaseNode<Node> implements Cloneable {
_hash = 0;
}
+ // Disk/serialized opcode tags
+ public enum Tag {
+ Add,And,BulkMemPhi,EQ,NE,LT,LE,ULT,
+ CallEnd,Call,ConFldOff,Con,CLInit,CProj,
+ Div,Escape,Extern,ExternOffset,Fun,FunPtr,Guard,
+ If,Load,Loop,MemMerge,MemPhi,Minus,Mul,
+ New,Never,Not,Or,Parm,Phi,Proj,PtrToInt,
+ ReadOnly,Return,Region,RoundF32,
+ Sar,Shl,Shr,Start,StartCU,StopCU,Stop,Store,Sub,
+ ToInteger,ToFloat,XCtrl,Xor;
+ public static final Tag[] VALS = values();
+ public Node make( BAOS bais, String[] strs, Type[] types, GlobalBits fileAliases, GlobalBits aliases ) {
+ return switch(this) {
+ case Add -> new AddNode(null,null,(byte)bais.packed1());
+ case And -> { int mode=bais.packed1(); assert mode==1; yield new AndNode(null,null,null); }
+ case Div -> new DivNode(null,null,(byte)bais.packed1());
+ case Guard -> GuardNode.make(bais);
+ case If -> new IfNode(null,null);
+ case Loop -> new LoopNode(null,null,null);
+ case Minus -> new MinusNode(null,(byte)bais.packed1());
+ case Mul -> new MulNode(null,null,(byte)bais.packed1());
+ case Never -> new NeverNode(1.2f, null);
+ case Not -> new NotNode(null);
+ case Or -> { int mode=bais.packed1(); assert mode==1; yield new OrNode(null,null,null); }
+ case ReadOnly-> new ReadOnlyNode((Node)null);
+ case Return ->new ReturnNode(null,null,null,null,null);
+ case RoundF32-> new RoundF32Node(null);
+ case PtrToInt-> new PtrToIntNode((Node)null);
+ case Sar -> { int mode=bais.packed1(); assert mode==1; yield new SarNode(null,null,null); }
+ case Shl -> { int mode=bais.packed1(); assert mode==1; yield new ShlNode(null,null,null); }
+ case Shr -> { int mode=bais.packed1(); assert mode==1; yield new ShrNode(null,null,null); }
+ case Start -> new StartNode(null,null,TypeInteger.BOT);
+ case Sub -> new SubNode(null,null,(byte)bais.packed1());
+ case ToFloat-> new ToFloatNode(null);
+ case ToInteger-> new ToIntegerNode(null);
+ case XCtrl -> new XCtrlNode();
+ case Xor -> { int mode=bais.packed1(); assert mode==1; yield new XorNode(null,null,null); }
+
+ case EQ -> new BoolNode.EQ (null,null,(byte)bais.packed1());
+ case NE -> new BoolNode.NE (null,null,(byte)bais.packed1());
+ case LT -> new BoolNode.LT (null,null,(byte)bais.packed1());
+ case LE -> new BoolNode.LE (null,null,(byte)bais.packed1());
+ case ULT -> new BoolNode.ULT(null,null,(byte)bais.packed1());
+
+ case Call -> CallNode.make(bais,strs);
+ case CallEnd-> CallEndNode.make(bais ,types);
+ case CLInit -> new CLInitNode(bais,strs);
+ case CProj -> CProjNode.make(bais,strs);
+ 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);
+ case MemMerge->MemMergeNode.make(bais);
+ case MemPhi-> MemPhiNode.make(bais,strs,types,fileAliases,aliases);
+ case BulkMemPhi-> BulkMemPhiNode.make(bais,strs,types,fileAliases,aliases);
+ case New -> NewNode.make(bais ,types);
+ case Parm -> ParmNode.make(bais,strs,types);
+ case Phi -> PhiNode.make(bais,strs,types);
+ case Proj -> ProjNode.make(bais,strs);
+ case Store -> new StoreNode(bais,strs,types,fileAliases,aliases);
+ case Stop -> StopNode.make(bais);
+ case StartCU-> StartCUNode.make(bais,strs,types);
+ case StopCU -> StopCUNode.make(bais);
+ case Region-> RegionNode.make(bais);
+
+ default -> throw Utils.TODO("Should not reach here");
+ };
+ }
+ };
+ public Tag serialTag() { throw Utils.TODO(); }
+ // Serialize extra data, including input counts
+ public void packed( BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types, IdentityHashMap<Node, Integer> anodes ) {}
+
// Easy reading label for debugger, e.g. "Add" or "Region" or "EQ"
- public String label() { return getClass().getSimpleName().replace("Node",""); }
+ public String label() { return serialTag().toString(); }
public String p(int depth) { return IRPrinter.prettyPrint(this,depth); }
@@ -667,6 +744,7 @@ public abstract class Node extends BaseNode<Node> implements Cloneable {
return null;
}
+ public void gather(HashMap<String,Integer> strs ) { }
// If a Type upgrade happens, put on worklist
boolean _upgradeType( HashMap<String,Type> TYPES ) { return false; }
diff --git a/src/main/java/com/seaofnodes/simple/node/NotNode.java b/src/main/java/com/seaofnodes/simple/node/NotNode.java
index e22d16b6..2328c287 100644
--- a/src/main/java/com/seaofnodes/simple/node/NotNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/NotNode.java
@@ -6,7 +6,7 @@ import com.seaofnodes.simple.util.Utils;
public class NotNode extends Node {
public NotNode(Node in) { super(null, in); }
-
+ @Override public Tag serialTag() { return Tag.Not; }
@Override protected String format() { return "(!%1)"; }
diff --git a/src/main/java/com/seaofnodes/simple/node/OrNode.java b/src/main/java/com/seaofnodes/simple/node/OrNode.java
index 263c3e87..9d50d1a9 100644
--- a/src/main/java/com/seaofnodes/simple/node/OrNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/OrNode.java
@@ -6,7 +6,7 @@ import com.seaofnodes.simple.type.TypeInteger;
public class OrNode extends ArithNode {
public OrNode( Parser.Lexer loc, Node lhs, Node rhs) { super(loc,lhs, rhs, (byte)1); }
-
+ @Override public Tag serialTag() { return Tag.Or; }
@Override public String label() { return "Or"; }
@Override public String op() { return "|"; }
diff --git a/src/main/java/com/seaofnodes/simple/node/ParmNode.java b/src/main/java/com/seaofnodes/simple/node/ParmNode.java
index 316efb93..91c471c0 100644
--- a/src/main/java/com/seaofnodes/simple/node/ParmNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/ParmNode.java
@@ -3,6 +3,8 @@ package com.seaofnodes.simple.node;
import com.seaofnodes.print.ExprPrinter;
import com.seaofnodes.simple.type.*;
+import com.seaofnodes.simple.codegen.Serialize;
+import com.seaofnodes.simple.util.BAOS;
import java.util.HashMap;
import java.util.IdentityHashMap;
@@ -20,7 +22,19 @@ public class ParmNode extends PhiNode {
_type = declaredType;
}
public ParmNode(ParmNode parm) { super(parm, parm._label ); _idx = parm._idx; _declaredType = parm._declaredType; }
-
+ @Override public Tag serialTag() { return Tag.Parm; }
+ @Override public void packed( BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types, IdentityHashMap<Node, Integer> anodes ) {
+ baos.packed1(Serialize.nIns(this,anodes)); // Object-local linked calls
+ baos.packed2(_label==null ? 0 : strs.get(_label));
+ baos.packed2(types.get(_declaredType)); // NPE if fails lookup
+ baos.packed1(_idx);
+ }
+ static Node make( BAOS bais, String[] strs, Type[] types) {
+ Node[] ins = new Node[bais.packed1()];
+ String label = strs[bais.packed2()];
+ Type minType = types[bais.packed2()];
+ return new ParmNode(label, bais.packed1(), minType, ins);
+ }
@Override public String label() { return MemOpNode.mlabel(_label); }
diff --git a/src/main/java/com/seaofnodes/simple/node/PhiNode.java b/src/main/java/com/seaofnodes/simple/node/PhiNode.java
index 61e4b468..3cf6f854 100644
--- a/src/main/java/com/seaofnodes/simple/node/PhiNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/PhiNode.java
@@ -3,6 +3,7 @@ package com.seaofnodes.simple.node;
import com.seaofnodes.print.ExprPrinter;
import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.Serialize;
import com.seaofnodes.simple.type.*;
import com.seaofnodes.simple.util.*;
import java.util.HashMap;
@@ -35,7 +36,15 @@ public class PhiNode extends Node {
while( nIns() < r.nIns() )
addDef(sample);
}
-
+ @Override public Tag serialTag() { return Tag.Phi; }
+ @Override public void packed( BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types, IdentityHashMap<Node, Integer> anodes ) {
+ baos.packed1(nIns());
+ baos.packed2(_label==null ? 0 : strs.get(_label));
+ }
+ static Node make( BAOS bais, String[] strs, Type[] types) {
+ Node[] ins = new Node[bais.packed1()];
+ return new PhiNode(strs[bais.packed2()], ins);
+ }
@Override public String label() { return "Phi_"+MemOpNode.mlabel(_label); }
@@ -272,4 +281,7 @@ public class PhiNode extends Node {
return Parser.error(sb.unchar(5).toString(),null);
}
+ @Override public void gather( HashMap<String,Integer> strs ) {
+ Serialize.gather(strs,_label);
+ }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/ProjNode.java b/src/main/java/com/seaofnodes/simple/node/ProjNode.java
index a0142379..fcc113a8 100644
--- a/src/main/java/com/seaofnodes/simple/node/ProjNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/ProjNode.java
@@ -2,9 +2,11 @@ package com.seaofnodes.simple.node;
import com.seaofnodes.print.ExprPrinter;
+import com.seaofnodes.simple.codegen.Serialize;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeMem;
import com.seaofnodes.simple.type.TypeTuple;
+import com.seaofnodes.simple.util.BAOS;
import java.util.HashMap;
import java.util.IdentityHashMap;
@@ -22,7 +24,14 @@ public class ProjNode extends Node implements Proj {
_label = label;
}
public ProjNode(ProjNode p) { super(p); _idx = p._idx; _label = p._label; }
-
+ @Override public Tag serialTag() { return Tag.Proj; }
+ @Override public void packed( BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types, IdentityHashMap<Node, Integer> anodes ) {
+ baos.packed1(_idx);
+ baos.packed2(_label==null ? 0 : strs.get(_label));
+ }
+ static Node make( BAOS bais, String[] strs ) {
+ return new ProjNode(null, bais.packed1(), strs[bais.packed2()] );
+ }
@Override public String label() { return _label; }
@@ -56,4 +65,7 @@ public class ProjNode extends Node implements Proj {
@Override public int idx() { return _idx; }
+ @Override public void gather( HashMap<String,Integer> strs ) {
+ Serialize.gather(strs,_label);
+ }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/PtrToIntNode.java b/src/main/java/com/seaofnodes/simple/node/PtrToIntNode.java
index 094734fa..d3d38060 100644
--- a/src/main/java/com/seaofnodes/simple/node/PtrToIntNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/PtrToIntNode.java
@@ -18,7 +18,7 @@ public class PtrToIntNode extends Node {
public PtrToIntNode(Node ptr) { super(null,ptr); }
public PtrToIntNode(PtrToIntNode ptr) { super(ptr); }
-
+ @Override public Tag serialTag() { return Tag.PtrToInt; }
@Override protected String format() { return "(i64*)%1"; }
@Override public Type compute() {
diff --git a/src/main/java/com/seaofnodes/simple/node/ReadOnlyNode.java b/src/main/java/com/seaofnodes/simple/node/ReadOnlyNode.java
index ab7e0ae0..0a720d5b 100644
--- a/src/main/java/com/seaofnodes/simple/node/ReadOnlyNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/ReadOnlyNode.java
@@ -10,7 +10,7 @@ import com.seaofnodes.simple.type.TypeMemPtr;
public class ReadOnlyNode extends Node {
public ReadOnlyNode( Node n ) { super(null,n); }
public ReadOnlyNode( ReadOnlyNode n ) { super(n); }
-
+ @Override public Tag serialTag() { return Tag.ReadOnly; }
@Override protected String format() { return "(const)%1"; }
@Override public Type compute() {
diff --git a/src/main/java/com/seaofnodes/simple/node/RegionNode.java b/src/main/java/com/seaofnodes/simple/node/RegionNode.java
index 923773c4..8883ba8e 100644
--- a/src/main/java/com/seaofnodes/simple/node/RegionNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/RegionNode.java
@@ -5,6 +5,7 @@ import com.seaofnodes.print.ExprPrinter;
import com.seaofnodes.simple.Parser;
import com.seaofnodes.simple.codegen.CodeGen;
import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.util.BAOS;
import java.util.*;
public class RegionNode extends CFGNode {
@@ -14,7 +15,9 @@ public class RegionNode extends CFGNode {
public RegionNode(Parser.Lexer loc, Node... nodes) { super(nodes); _loc = loc; }
public RegionNode(RegionNode r, Parser.Lexer loc) { super(r); _loc = loc; }
public RegionNode(RegionNode r) { super(r); if( r!=null ) _loc = r._loc; }
-
+ @Override public Tag serialTag() { return Tag.Region; }
+ public void packed( BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types, IdentityHashMap<Node, Integer> anodes ) { baos.packed1(nIns()); }
+ static Node make( BAOS bais ) { return new RegionNode(null,new Node[bais.packed1()]); }
@Override protected ExprPrinter<Node> _print1(ExprPrinter<Node> p) {
return p.p(label());
diff --git a/src/main/java/com/seaofnodes/simple/node/ReturnNode.java b/src/main/java/com/seaofnodes/simple/node/ReturnNode.java
index 0bb090ee..0431f7d5 100644
--- a/src/main/java/com/seaofnodes/simple/node/ReturnNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/ReturnNode.java
@@ -28,7 +28,7 @@ public class ReturnNode extends CFGNode {
_fun = fun;
}
public ReturnNode( ReturnNode ret, FunNode fun ) { super(ret); _fun = fun; }
-
+ @Override public Tag serialTag() { return Tag.Return; }
public Node ctrl() { return in(0); }
public Node mem () { return in(1); }
diff --git a/src/main/java/com/seaofnodes/simple/node/RoundF32Node.java b/src/main/java/com/seaofnodes/simple/node/RoundF32Node.java
index 02d3bbaa..65f10a79 100644
--- a/src/main/java/com/seaofnodes/simple/node/RoundF32Node.java
+++ b/src/main/java/com/seaofnodes/simple/node/RoundF32Node.java
@@ -7,7 +7,7 @@ import com.seaofnodes.simple.type.TypeFloat;
public class RoundF32Node extends Node {
public RoundF32Node(Node lhs) { super(null, lhs); }
-
+ @Override public Tag serialTag() { return Tag.RoundF32; }
@Override protected String format() { return "((f32)%1)"; }
diff --git a/src/main/java/com/seaofnodes/simple/node/SarNode.java b/src/main/java/com/seaofnodes/simple/node/SarNode.java
index fa63334b..737b531f 100644
--- a/src/main/java/com/seaofnodes/simple/node/SarNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/SarNode.java
@@ -6,7 +6,7 @@ import com.seaofnodes.simple.type.TypeInteger;
public class SarNode extends ArithNode {
public SarNode(Parser.Lexer loc, Node lhs, Node rhs) { super(loc, lhs, rhs, (byte)1); }
-
+ @Override public Tag serialTag() { return Tag.Sar; }
@Override public String op() { return ">>"; }
@Override long doOp( long x, long y ) { return x >> y; }
diff --git a/src/main/java/com/seaofnodes/simple/node/ShlNode.java b/src/main/java/com/seaofnodes/simple/node/ShlNode.java
index 380fc246..f657cf51 100644
--- a/src/main/java/com/seaofnodes/simple/node/ShlNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/ShlNode.java
@@ -7,7 +7,7 @@ import com.seaofnodes.simple.type.TypeInteger;
public class ShlNode extends ArithNode {
public ShlNode(Parser.Lexer loc, Node lhs, Node rhs) { super(loc, lhs, rhs, (byte)1); }
-
+ @Override public Tag serialTag() { return Tag.Shl; }
@Override public String op() { return "<<"; }
@Override long doOp( long x, long y ) { return x << y; }
diff --git a/src/main/java/com/seaofnodes/simple/node/ShrNode.java b/src/main/java/com/seaofnodes/simple/node/ShrNode.java
index 17170a1f..e93ea0e8 100644
--- a/src/main/java/com/seaofnodes/simple/node/ShrNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/ShrNode.java
@@ -6,7 +6,7 @@ import com.seaofnodes.simple.type.TypeInteger;
public class ShrNode extends ArithNode {
public ShrNode(Parser.Lexer loc, Node lhs, Node rhs) { super(loc, lhs, rhs, (byte)1); }
-
+ @Override public Tag serialTag() { return Tag.Shr; }
@Override public String op() { return ">>>"; }
@Override long doOp( long x, long y ) { return x >>> y; }
diff --git a/src/main/java/com/seaofnodes/simple/node/StartCUNode.java b/src/main/java/com/seaofnodes/simple/node/StartCUNode.java
index 0f7a322a..cd51f603 100644
--- a/src/main/java/com/seaofnodes/simple/node/StartCUNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/StartCUNode.java
@@ -1,7 +1,9 @@
package com.seaofnodes.simple.node;
import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.Serialize;
import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.util.BAOS;
import java.util.HashMap;
import java.util.IdentityHashMap;
@@ -11,12 +13,21 @@ public class StartCUNode extends StartNode {
public final String _fname;
public StartCUNode(StartNode start, StopCUNode stop, Type arg, String fname) { super(start,stop,arg); _fname = fname; }
public StartCUNode(StartCUNode start) { super(start); _fname = start._fname; }
-
+ @Override public Tag serialTag() { return Tag.StartCU; }
+ @Override public void packed( BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types, IdentityHashMap<Node,Integer> anodes ) {
+ baos.packed2(types.get(_arg));
+ baos.packed2(strs.get(_fname));
+ }
+ static Node make( BAOS bais, String[] strs, Type[] types ) {
+ return new StartCUNode(null,null,types[bais.packed2()],strs[bais.packed2()]);
+ }
@Override public CFGNode cfg0() { return (CFGNode)in(0); }
// IDom is cfg0() and depth is always Start+1 which is 0+1 or 1
@Override public int idepth() { return 1; }
@Override public CFGNode idom(Node dep) { return cfg0(); }
-
+ @Override public void gather( HashMap<String,Integer> strs ) {
+ Serialize.gather(strs,_fname);
+ }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/StartNode.java b/src/main/java/com/seaofnodes/simple/node/StartNode.java
index 3c605e30..495be0a2 100644
--- a/src/main/java/com/seaofnodes/simple/node/StartNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/StartNode.java
@@ -29,7 +29,7 @@ public class StartNode extends LoopNode implements MultiNode {
public StartNode(StartNode start, StopNode stop, Type arg) { super(null,start,stop); _arg = arg; }
public StartNode(StartNode start) { super(start); _arg = start==null ? null : start._arg; }
-
+ @Override public Tag serialTag() { return Tag.Start; }
public Type arg() { return _arg; }
@Override protected ExprPrinter<Node> _print1(ExprPrinter<Node> p) {
diff --git a/src/main/java/com/seaofnodes/simple/node/StopCUNode.java b/src/main/java/com/seaofnodes/simple/node/StopCUNode.java
index 53c2325c..43acf631 100644
--- a/src/main/java/com/seaofnodes/simple/node/StopCUNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/StopCUNode.java
@@ -2,6 +2,7 @@ package com.seaofnodes.simple.node;
import com.seaofnodes.simple.codegen.CodeGen;
import com.seaofnodes.simple.type.*;
+import com.seaofnodes.simple.util.BAOS;
import java.util.HashMap;
import java.util.IdentityHashMap;
@@ -10,7 +11,13 @@ import java.util.IdentityHashMap;
public class StopCUNode extends StopNode {
public StopCUNode() { super(); }
public StopCUNode(StopNode stop) { super(stop); }
-
+ @Override public Tag serialTag() { return Tag.StopCU; }
+ @Override public void packed( BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types, IdentityHashMap<Node, Integer> anodes ) { baos.packed1(nIns()); }
+ static Node make( BAOS bais ) {
+ StopCUNode stop = new StopCUNode();
+ stop.setDefX(bais.packed1()-1,null);
+ return stop;
+ }
public StartCUNode start() {
for( Node use : _outputs )
diff --git a/src/main/java/com/seaofnodes/simple/node/StopNode.java b/src/main/java/com/seaofnodes/simple/node/StopNode.java
index 7238f1f9..a7e52563 100644
--- a/src/main/java/com/seaofnodes/simple/node/StopNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/StopNode.java
@@ -3,7 +3,9 @@ package com.seaofnodes.simple.node;
import com.seaofnodes.print.ExprPrinter;
import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.Serialize;
import com.seaofnodes.simple.type.*;
+import com.seaofnodes.simple.util.BAOS;
import java.util.HashMap;
import java.util.IdentityHashMap;
@@ -16,7 +18,13 @@ public class StopNode extends CFGNode {
_type = compute();
}
public StopNode(StopNode stop) { super(stop); }
-
+ @Override public Tag serialTag() { return Tag.Stop; }
+ @Override public void packed( BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types, IdentityHashMap<Node, Integer> anodes ) { baos.packed1(Serialize.nIns(this,anodes)); }
+ static Node make( BAOS bais ) {
+ StopNode stop = new StopNode();
+ stop.setDefX(bais.packed1()-1,null);
+ return stop;
+ }
@Override
protected ExprPrinter<Node> _print1(ExprPrinter<Node> p) {
diff --git a/src/main/java/com/seaofnodes/simple/node/StoreNode.java b/src/main/java/com/seaofnodes/simple/node/StoreNode.java
index bdd07c8d..5fa0b99f 100644
--- a/src/main/java/com/seaofnodes/simple/node/StoreNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/StoreNode.java
@@ -4,7 +4,9 @@ import com.seaofnodes.print.ExprPrinter;
import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.GlobalBits;
import com.seaofnodes.simple.type.*;
+import com.seaofnodes.simple.util.BAOS;
import com.seaofnodes.simple.util.Utils;
import java.util.HashMap;
import java.util.IdentityHashMap;
@@ -35,7 +37,18 @@ public class StoreNode extends MemOpNode {
_init = init;
_size = size;
}
-
+ StoreNode( BAOS bais, String[] strs, Type[] types, GlobalBits fileAliases, GlobalBits aliases ) {
+ super(bais,strs,types,fileAliases,aliases,false);
+ addDef(null);
+ _init = bais.read() != 0;
+ _size = (byte)bais.read();
+ }
+ @Override public Tag serialTag() { return Tag.Store; }
+ @Override public void packed( BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types, IdentityHashMap<Node, Integer> anodes ) {
+ super.packed(baos,strs,types, anodes );
+ baos.write(_init ? 1 : 0);
+ baos.write(_size);
+ }
@Override boolean canDrop(MemOpNode other, Node dep) {
return super.canDrop(other,dep) && _init==((StoreNode)other)._init;
@@ -114,7 +127,7 @@ public class StoreNode extends MemOpNode {
// Freeze the semantic Store variant as soon as the pointer exposes a
// canonical field declaration. Waiting until after structural memory
// peeps can lose the declaration to a flow-sensitive constant field
- // state.
+ // state. Imported Stores already carry a serialized non-zero width.
if( _size==0 && (_size= storageType()) != 0 ) {
unlock(); // Width participates in the cached hash.
return init();
diff --git a/src/main/java/com/seaofnodes/simple/node/SubNode.java b/src/main/java/com/seaofnodes/simple/node/SubNode.java
index adad2ed5..8eb1f4cc 100644
--- a/src/main/java/com/seaofnodes/simple/node/SubNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/SubNode.java
@@ -9,7 +9,7 @@ public class SubNode extends ArithNode {
@Override boolean allowFloat() { return true; }
public SubNode(Node lhs, Node rhs) { super(null, lhs, rhs); }
public SubNode(Node lhs, Node rhs, byte mode) { super(null, lhs, rhs,mode); }
-
+ @Override public Tag serialTag() { return Tag.Sub; }
@Override public String op() { return "-"; }
diff --git a/src/main/java/com/seaofnodes/simple/node/ToFloatNode.java b/src/main/java/com/seaofnodes/simple/node/ToFloatNode.java
index 1acf55b6..151a326e 100644
--- a/src/main/java/com/seaofnodes/simple/node/ToFloatNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/ToFloatNode.java
@@ -8,7 +8,7 @@ import com.seaofnodes.simple.type.TypeInteger;
public class ToFloatNode extends Node {
public ToFloatNode(Node lhs) { super(null, lhs); }
-
+ @Override public Tag serialTag() { return Tag.ToFloat; }
@Override protected String format() { return "(flt)%1"; }
diff --git a/src/main/java/com/seaofnodes/simple/node/ToIntegerNode.java b/src/main/java/com/seaofnodes/simple/node/ToIntegerNode.java
index 9136aacc..33806300 100644
--- a/src/main/java/com/seaofnodes/simple/node/ToIntegerNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/ToIntegerNode.java
@@ -7,7 +7,7 @@ import com.seaofnodes.simple.type.TypeInteger;
// Truncate toward zero, saturating overflow; NaN converts to zero.
public class ToIntegerNode extends Node {
public ToIntegerNode(Node lhs) { super(null,lhs); }
-
+ @Override public Tag serialTag() { return Tag.ToInteger; }
@Override public String label() { return "ToInteger"; }
@Override protected String format() { return "(int)%1"; }
diff --git a/src/main/java/com/seaofnodes/simple/node/TypeNode.java b/src/main/java/com/seaofnodes/simple/node/TypeNode.java
index 970472da..3ab2d6f9 100644
--- a/src/main/java/com/seaofnodes/simple/node/TypeNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/TypeNode.java
@@ -2,6 +2,7 @@ package com.seaofnodes.simple.node;
import com.seaofnodes.simple.Parser;
import com.seaofnodes.simple.type.*;
+import com.seaofnodes.simple.util.BAOS;
import com.seaofnodes.simple.util.Utils;
import java.util.BitSet;
import java.util.HashMap;
diff --git a/src/main/java/com/seaofnodes/simple/node/XCtrlNode.java b/src/main/java/com/seaofnodes/simple/node/XCtrlNode.java
index 3792e224..32e1deb9 100644
--- a/src/main/java/com/seaofnodes/simple/node/XCtrlNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/XCtrlNode.java
@@ -8,7 +8,7 @@ import com.seaofnodes.simple.type.Type;
public class XCtrlNode extends CFGNode {
public XCtrlNode() { super(new Node[]{CodeGen.CODE._start}); }
-
+ @Override public Tag serialTag() { return Tag.XCtrl; }
@Override protected ExprPrinter<Node> _print1(ExprPrinter<Node> p) { return p.p("Xctrl"); }
@Override public boolean isConst() { return true; }
@Override public Type compute() { return Type.XCONTROL; }
diff --git a/src/main/java/com/seaofnodes/simple/node/XorNode.java b/src/main/java/com/seaofnodes/simple/node/XorNode.java
index e9cd13a5..c825f396 100644
--- a/src/main/java/com/seaofnodes/simple/node/XorNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/XorNode.java
@@ -6,7 +6,7 @@ import com.seaofnodes.simple.type.TypeInteger;
public class XorNode extends ArithNode {
public XorNode(Parser.Lexer loc, Node lhs, Node rhs) { super(loc, lhs, rhs, (byte)1); }
-
+ @Override public Tag serialTag() { return Tag.Xor; }
@Override public String op() { return "^"; }
@Override long doOp( long x, long y ) { return x ^ y; }
diff --git a/src/main/java/com/seaofnodes/simple/print/ASMPrinter.java b/src/main/java/com/seaofnodes/simple/print/ASMPrinter.java
index 58506e9c..e8218afd 100644
--- a/src/main/java/com/seaofnodes/simple/print/ASMPrinter.java
+++ b/src/main/java/com/seaofnodes/simple/print/ASMPrinter.java
@@ -7,9 +7,9 @@ import com.seaofnodes.simple.codegen.CodeGen;
import com.seaofnodes.simple.util.SB;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.*;
-import com.seaofnodes.simple.util.BAOS;
import java.util.ArrayList;
import com.seaofnodes.simple.codegen.Encoding;
+import com.seaofnodes.simple.util.BAOS;
import java.util.HashMap;
/** Machine-specific facts; traversal, columns and byte display live in print/. */
diff --git a/src/main/java/com/seaofnodes/simple/type/Field.java b/src/main/java/com/seaofnodes/simple/type/Field.java
index ec4a588d..beaa719c 100644
--- a/src/main/java/com/seaofnodes/simple/type/Field.java
+++ b/src/main/java/com/seaofnodes/simple/type/Field.java
@@ -1,6 +1,7 @@
package com.seaofnodes.simple.type;
import com.seaofnodes.simple.util.Ary;
+import com.seaofnodes.simple.util.BAOS;
import com.seaofnodes.simple.util.SB;
import java.util.*;
@@ -130,7 +131,18 @@ public class Field extends Type {
@Override public Type at( int idx ) { return _t; }
@Override public void set( int idx, Type t ) { _t = t; }
// 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,bais.read()!=0);
+ }
@Override
public SB _print( SB sb, BitSet visit, boolean html ) {
diff --git a/src/main/java/com/seaofnodes/simple/type/Type.java b/src/main/java/com/seaofnodes/simple/type/Type.java
index 1979368e..980daf0f 100644
--- a/src/main/java/com/seaofnodes/simple/type/Type.java
+++ b/src/main/java/com/seaofnodes/simple/type/Type.java
@@ -1,5 +1,6 @@
package com.seaofnodes.simple.type;
+import com.seaofnodes.simple.codegen.GlobalBits;
import com.seaofnodes.simple.util.*;
import java.util.*;
@@ -512,6 +513,123 @@ public class Type /*implements Cloneable*/ {
Type _upgradeType(HashMap<String,Type> TYPES) { assert is_nokids(); return this; }
+ // Recursively gather all types
+ public void gather(HashMap<Type,Integer> types ) {
+ if( types.containsKey(this) ) return; // TEST
+ types.put(this,types.size()); // SET
+ int nkids = nkids();
+ for( int i=0; i<nkids; i++ )
+ at(i).gather(types);
+ }
+
+ // Compute serialization byte tag compression space
+ final static int[] TAGOFFS = new int[TMAX+1];
+ public static int[] TAGOFFS() {
+ if( TAGOFFS[TMAX-1]==0 )
+ // One entry per unique tag-space, using the BOT value from that
+ // tag-space except for the first tag-space which uses 1 tag for
+ // multiple Types instead of 1 Type having multiple tags. Has to
+ // be ordered to match _type enum.
+ for( Type t : new Type[]{Type.XNIL,TypeScalar.BOT,TypePtr.PTR,TypeInteger.BOT,TypeFloat.F64,TypeConAryB.ABC,TypeRPC.BOT,TypeMemPtr.BOT,TypeFunPtr.BOT,TypeMem.BOT,Field.BOT,TypeStruct.BOT,TypeTuple.BOT} )
+ TAGOFFS[t._type+1] = TAGOFFS[t._type] + t.TAGOFF();
+ assert TAGOFFS[TMAX] <= 255;
+ return TAGOFFS;
+ }
+ // Reserve 6 tags, 0-5, for plain Types
+ int TAGOFF() { assert is_simple(); return 6; }
+
+ public void packed( BAOS baos, HashMap<String,Integer> strs ) {
+ assert is_simple();
+ baos.write(_type);
+ }
+ static Type packed( int tag ) {
+ return switch( tag ) {
+ case TBOT -> BOTTOM;
+ case TTOP -> TOP;
+ case TCTRL -> CONTROL;
+ case TXCTRL->XCONTROL;
+ case TNIL -> NIL;
+ default -> throw Utils.TODO("Should not reach here: unknown simple type tag");
+ };
+ }
+
+ // Produce a type with null child types
+ static Type packed( BAOS bais, String[] strs ) {
+ int x = bais.read();
+ int type = Arrays.binarySearch( TAGOFFS, x );
+ if( type < 0 ) type = -(type+1)-1;
+ int off = x - TAGOFFS[type];
+ return switch( type ) {
+ case 0, 1, 2, 3, 4, 5 -> Type.packed( off );
+ case TSCALAR -> TypeScalar .packed( off );
+ case TPTR -> TypePtr .packed( off );
+ case TINT -> TypeInteger.packed( off, bais );
+ case TFLT -> TypeFloat .packed( off, bais );
+ case TCONARY -> TypeConAry .packed( off, bais );
+ case TRPC -> TypeRPC .packed( off, bais );
+ case TMEMPTR -> TypeMemPtr .packed( off, bais );
+ case TFUNPTR -> TypeFunPtr .packed( off, bais );
+ case TMEM -> TypeMem .packed( off, bais );
+ case TFLD -> Field .packed( off, bais, strs );
+ case TSTRUCT -> TypeStruct .packed( off, bais, strs );
+ case TTUPLE -> TypeTuple .packed( off, bais );
+ default -> throw Utils.TODO("Should not reach here: unknown packed type tag");
+ };
+ }
+
+ // Read a packed Type array
+ public static Type[] packed( BAOS bais, String[] strs, int ntypes, HashMap<String,Type> existingTypes,
+ GlobalBits fileAliases, GlobalBits aliases,
+ GlobalBits fileFidxs , GlobalBits fidxs ,
+ GlobalBits fileRpcs , GlobalBits rpcs ) {
+ Type[] types = new Type[ntypes];
+ // Read Types in ID# order, no children
+ for( int i=0; i<ntypes; i++ )
+ types[i] = Type.packed(bais,strs);
+ // Start possibly recursive type collection
+ BOTTOM.recurOpen();
+ // Read Types in ID# order, only children
+ for( int i=0; i<ntypes; i++ ) {
+ VISIT.put(types[i]._uid,types[i]);
+ int nkids = types[i].nkids();
+ for( int j=0; j<nkids; j++ )
+ types[i].set(j,types[bais.packed4()]);
+ }
+ // Remap file-local aliases/fidxs/rpcs into this compilation unit's
+ // local dense indexes via their global identities.
+ for( int i=0; i<ntypes; i++ ) {
+ if( types[i] instanceof Field fld ) {
+ if( fld._alias >= GlobalBits.RESERVED )
+ fld._alias = aliases.map(fileAliases,fld._alias);
+ }
+ if( types[i] instanceof TypeFunPtr tfp )
+ tfp._fidxs = remapBits(fileFidxs,fidxs,tfp._fidxs,GlobalBits.RESERVED);
+ if( types[i] instanceof TypeRPC rpc )
+ rpc._rpcs = remapBits(fileRpcs,rpcs,rpc._rpcs,GlobalBits.RESERVED);
+ if( types[i] instanceof TypeMem mem ) {
+ if( mem._alias >= GlobalBits.RESERVED )
+ mem._alias = aliases.map(fileAliases,mem._alias);
+ mem._escFs = remapBits(fileFidxs ,fidxs ,mem._escFs,GlobalBits.RESERVED);
+ mem._escAs = remapBits(fileAliases,aliases,mem._escAs,GlobalBits.RESERVED);
+ }
+ }
+
+ // Intern them all at once
+ BOTTOM.recurClose(types);
+ return types;
+ }
+
+ private static int[] remapBits(GlobalBits fileBits, GlobalBits bits, int[] fileLocals, int firstMapped ) {
+ if( fileLocals == XInt.EMPTY || fileLocals == XInt.FULL )
+ return fileLocals;
+ assert !XInt.isHigh(fileLocals);
+ int[] locals = XInt.EMPTY;
+ for( int fileLocal = XInt.next(fileLocals,-1); fileLocal >=0; fileLocal = XInt.next(fileLocals,fileLocal) )
+ locals = XInt.make(locals,fileLocal < firstMapped ? fileLocal : bits.map(fileBits,fileLocal));
+ return locals;
+ }
+
+
// ----------------------------------------------------------
// Size in bits to hold an instance of this type.
diff --git a/src/main/java/com/seaofnodes/simple/type/TypeConAry.java b/src/main/java/com/seaofnodes/simple/type/TypeConAry.java
index 860cefc5..c9093e3b 100644
--- a/src/main/java/com/seaofnodes/simple/type/TypeConAry.java
+++ b/src/main/java/com/seaofnodes/simple/type/TypeConAry.java
@@ -74,7 +74,17 @@ public abstract class TypeConAry<A> extends TypeScalar {
public abstract long at8(int idx);
public abstract int len();
@Override public abstract int log_size();
-
-
public void write( BAOS baos ) { throw Utils.TODO("Should not reach here: abstract constant array cannot be written"); }
+
+ // Reserve tags for u8 array
+ @Override int TAGOFF() { return 1; }
+ @Override public void packed( BAOS baos, HashMap<String,Integer> strs ) {
+ assert log_size()==0 && !_any;
+ baos.write(TAGOFFS[TCONARY]+0);
+ baos.packed4(len());
+ baos.write((byte[])_ary);
+ }
+ static TypeConAry packed( int tag, BAOS bais ) {
+ return TypeConAryB.make(bais.read(new byte[bais.packed4()]));
+ }
}
diff --git a/src/main/java/com/seaofnodes/simple/type/TypeConAryB.java b/src/main/java/com/seaofnodes/simple/type/TypeConAryB.java
index bddcb1fb..5877c309 100644
--- a/src/main/java/com/seaofnodes/simple/type/TypeConAryB.java
+++ b/src/main/java/com/seaofnodes/simple/type/TypeConAryB.java
@@ -16,6 +16,5 @@ public class TypeConAryB extends TypeConAry<byte[]> {
@Override public String str() { return "[\""+new String(_ary)+"\"]"; }
@Override boolean eq(Type t) { return t instanceof TypeConAryB ary && _widen==ary._widen && Arrays.equals(_ary,ary._ary); }
@Override int hash() { return Arrays.hashCode(_ary); }
-
@Override public void write( BAOS baos ) { baos.write(_ary); }
}
diff --git a/src/main/java/com/seaofnodes/simple/type/TypeFloat.java b/src/main/java/com/seaofnodes/simple/type/TypeFloat.java
index 8638b8c9..d410e9cf 100644
--- a/src/main/java/com/seaofnodes/simple/type/TypeFloat.java
+++ b/src/main/java/com/seaofnodes/simple/type/TypeFloat.java
@@ -2,6 +2,7 @@ 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.Utils;
import java.util.ArrayList;
import java.util.HashMap;
@@ -98,6 +99,26 @@ public class TypeFloat extends TypeScalar {
@Override public Type makeZero() { return FZERO; }
+ // Reserve tags for F64,F32,constant
+ @Override int TAGOFF() { return 3; }
+ @Override public void packed( BAOS baos, HashMap<String,Integer> strs ) {
+ if( this==F64 ) baos.write(TAGOFFS[_type] + 0);
+ else if( this==F32 ) baos.write(TAGOFFS[_type] + 1);
+ else {
+ assert isConstant();
+ baos.write(TAGOFFS[_type] + 2);
+ baos.packed8(Double.doubleToLongBits(_con));
+ }
+ }
+
+ static Type packed( int tag, BAOS bais ) {
+ return switch( tag ) {
+ case 0 -> F64;
+ case 1 -> F32;
+ case 2 -> constant(Double.longBitsToDouble(bais.packed8()));
+ default -> throw Utils.TODO("Should not reach here: unknown float type tag");
+ };
+ }
@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 301fcc8b..56f47bff 100644
--- a/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java
+++ b/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java
@@ -75,6 +75,7 @@ public class TypeFunPtr extends TypeNil {
}
+
@Override TypeFunPtr makeFrom( byte nil ) { return nil ==_nil ? this : make( nil,_open,_sig,_ret, _fidxs); }
public TypeFunPtr makeFrom( Type ret ) { return ret ==_ret ? this : make( _nil,_open,_sig, ret, _fidxs); }
public TypeFunPtr makeFrom( int fidx ) { return make1(_nil,_open,_sig,_ret,fidx); }
@@ -236,6 +237,33 @@ public class TypeFunPtr extends TypeNil {
else _sig[idx] = t;
}
+ // Reserve tags for null/not and open/close, one fidx/bitset, 0-6 args
+ // Tags 0-5 - not,close,1 fidx, 0-5 args (+fidx)
+ // Tag 6 - not ,close+fidxs+nargs
+ // Tag 7 - null,close+fidxs+nargs
+ @Override int TAGOFF() { return 8; }
+ @Override public void packed( BAOS baos, HashMap<String,Integer> strs ) {
+ assert _open; // Open: Passing extra args is fine, and extras ignored.
+ if( _nil==2 && nargs()<6 && nfcns() == 1 && fidx() < 64 ) {
+ baos.write(TAGOFFS[_type] + nargs());
+ baos.packed2(fidx());
+ } else {
+ baos.write(TAGOFFS[_type] + 6 + (_nil==2 ? 0 : 1));
+ baos.packed1(nargs());
+ XInt.packed(baos,_fidxs);
+ }
+ }
+ static TypeFunPtr packed( int tag, BAOS bais ) {
+ if( tag < 6 ) {
+ int fidx = bais.read();
+ assert fidx < 64; // Need a larger fidx space
+ return malloc((byte)2,true,new Type[tag],null, XInt.make(fidx));
+ }
+ byte nil = (byte)(tag==6 ? 2 : 3);
+ int nargs = bais.packed1();
+ int[] fidxs = XInt.packed(bais);
+ return malloc(nil,true,new Type[nargs],null,fidxs);
+ }
@Override
int hash() {
diff --git a/src/main/java/com/seaofnodes/simple/type/TypeInteger.java b/src/main/java/com/seaofnodes/simple/type/TypeInteger.java
index a2cf08b0..a0c2266d 100644
--- a/src/main/java/com/seaofnodes/simple/type/TypeInteger.java
+++ b/src/main/java/com/seaofnodes/simple/type/TypeInteger.java
@@ -2,6 +2,7 @@ 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.Utils;
import java.util.ArrayList;
import java.util.HashMap;
@@ -176,7 +177,38 @@ public class TypeInteger extends TypeScalar {
if( _isConstant() ) return BOT;
return isHigh() ? dual() : this;
}
+ // Reserve tags for I64,I32,U32,0,constant,generic
+ @Override int TAGOFF() { return 7; }
+ @Override public void packed( BAOS baos, HashMap<String,Integer> strs ) {
+ if( this==BOT ) baos.write(TAGOFFS[_type] + 0);
+ else if( this==I32 ) baos.write(TAGOFFS[_type] + 1);
+ else if( this==U32 ) baos.write(TAGOFFS[_type] + 2);
+ else if( this==BOOL) baos.write(TAGOFFS[_type] + 3);
+ else if( this==ZERO) baos.write(TAGOFFS[_type] + 4);
+ else if( isConstant() ) {
+ baos.write(TAGOFFS[_type] + 5);
+ baos.packed8(_min);
+ } else {
+ baos.write(TAGOFFS[_type] + 6);
+ baos.packed8(_min);
+ baos.packed8(_max);
+ assert -3 <= _widen && _widen <= 3;
+ baos.packed1(_widen + 3);
+ }
+ }
+ static Type packed( int tag, BAOS bais ) {
+ return switch( tag ) {
+ case 0 -> BOT;
+ case 1 -> I32;
+ case 2 -> U32;
+ case 3 -> BOOL;
+ case 4 -> ZERO;
+ case 5 -> constant(bais.packed8());
+ case 6 -> make(bais.packed8(),bais.packed8(),(byte)(bais.packed1()-3));
+ default -> throw Utils.TODO("Should not reach here: unknown integer type tag");
+ };
+ }
@Override int hash() { return Utils.fold(_min) * Utils.fold(_max) + _widen; }
@Override public boolean eq( Type t ) {
diff --git a/src/main/java/com/seaofnodes/simple/type/TypeMem.java b/src/main/java/com/seaofnodes/simple/type/TypeMem.java
index 4234a5e5..9ae68bcf 100644
--- a/src/main/java/com/seaofnodes/simple/type/TypeMem.java
+++ b/src/main/java/com/seaofnodes/simple/type/TypeMem.java
@@ -173,7 +173,31 @@ public class TypeMem 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; }
-
+ // one tag for alias#1, one tag for generic alias.
+ // tag bit for _one, another bit for _final.
+ @Override int TAGOFF() { return 12; }
+ @Override public void packed( BAOS baos, HashMap<String,Integer> strs ) {
+ baos.write(TAGOFFS[_type]
+ + (_alias==1 ? 0 : 1)
+ + (!_final ? 0 : 2)
+ + (!_one ? 0 : (_clz ? 4 : 8)));
+ if( _alias!=1 ) {
+ // generic alias
+ assert _alias <= 255;
+ baos.packed1( _alias );
+ }
+ XInt.packed(baos,_escFs);
+ XInt.packed(baos,_escAs);
+ }
+ static TypeMem packed( int tag, BAOS bais ) {
+ int alias = (tag&1)==0 ? 1 : bais.packed1();
+ boolean xfinal= (tag&2)==2;
+ boolean one = (tag&12)!=0;
+ boolean clz = (tag&4)==4;
+ int[] escFs = XInt.packed(bais);
+ int[] escAs = XInt.packed(bais);
+ return malloc(alias,null,one,clz,xfinal,escFs,escAs);
+ }
@Override public SB _print(SB sb, BitSet visit, boolean html) {
if( this==BOT ) return sb.p("MemBot");
diff --git a/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java b/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java
index 73ca6190..175b1c63 100644
--- a/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java
+++ b/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java
@@ -3,6 +3,7 @@ package com.seaofnodes.simple.type;
import com.seaofnodes.simple.util.Ary;
import com.seaofnodes.simple.util.AryInt;
import com.seaofnodes.simple.util.SB;
+import com.seaofnodes.simple.util.BAOS;
import java.util.*;
/**
@@ -162,6 +163,19 @@ public class TypeMemPtr extends TypeNil {
@Override public Type at( int idx ) { return _obj; }
@Override public void set( int idx, Type t ) { _obj = (TypeStruct)t; }
+ // Reserve tags for null/not, one/general, public/private and read-only access
+ @Override int TAGOFF() { return 16; }
+ @Override public void packed( BAOS baos, HashMap<String,Integer> strs ) {
+ assert _nil>=2;
+ baos.write(TAGOFFS[_type]
+ + (_nil==2 ? 0 : 1)
+ + (!_one ? 0 : 2)
+ + (!_pub ? 0 : 4)
+ + (!_ro ? 0 : 8) );
+ }
+ static TypeMemPtr packed( int tag, BAOS bais ) {
+ return malloc((byte)((tag&1)+2),null,(tag&2)==2,(tag&4)==4).access((tag&8)==8);
+ }
@Override public String str() {
if( this==BOT ) return "PtrBot";
diff --git a/src/main/java/com/seaofnodes/simple/type/TypePtr.java b/src/main/java/com/seaofnodes/simple/type/TypePtr.java
index 7e9e70e4..38941ed1 100644
--- a/src/main/java/com/seaofnodes/simple/type/TypePtr.java
+++ b/src/main/java/com/seaofnodes/simple/type/TypePtr.java
@@ -3,6 +3,7 @@ package com.seaofnodes.simple.type;
import com.seaofnodes.simple.util.AryInt;
import com.seaofnodes.simple.util.SB;
import com.seaofnodes.simple.util.Utils;
+import com.seaofnodes.simple.util.BAOS;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.HashMap;
@@ -52,6 +53,14 @@ public class TypePtr extends TypeNil {
@Override TypePtr _makeStorage() { return PTR; }
+ // Reserve 2 tags, PTR and NPTR
+ @Override int TAGOFF() { return 2; }
+ @Override public void packed( BAOS baos, HashMap<String,Integer> strs ) {
+ assert _nil>=2;
+ baos.write(TAGOFFS[_type] + (_nil==2 ? 0 : 1));
+ }
+ static TypePtr packed( int tag ) { return tag==0 ? NPTR : PTR; }
+
@Override public String str() { return STRS[_nil]; }
@Override public SB _print(SB sb, BitSet visit, boolean html) { return sb.p(str()); }
}
diff --git a/src/main/java/com/seaofnodes/simple/type/TypeRPC.java b/src/main/java/com/seaofnodes/simple/type/TypeRPC.java
index 7acc99de..8a12f50a 100644
--- a/src/main/java/com/seaofnodes/simple/type/TypeRPC.java
+++ b/src/main/java/com/seaofnodes/simple/type/TypeRPC.java
@@ -1,6 +1,7 @@
package com.seaofnodes.simple.type;
import com.seaofnodes.simple.util.Ary;
+import com.seaofnodes.simple.util.BAOS;
import java.util.*;
/**
@@ -74,6 +75,25 @@ public class TypeRPC extends Type {
@Override boolean _isConstant() { return XInt.isConstant(_rpcs); }
@Override TypeRPC _makeStorage() { return this; }
+ // Reserve tags for ALL, singleton, generic
+ @Override int TAGOFF() { return 3; }
+ @Override public void packed( BAOS baos, HashMap<String,Integer> strs ) {
+ if( this==BOT ) {
+ baos.write( TAGOFFS[_type] + 0 );
+ } else if( XInt.isConstant(_rpcs) ) { // Singleton
+ baos.write(TAGOFFS[_type] + 1);
+ baos.packed2(rpc());
+ } else { // Generic
+ assert !XInt.isHigh(_rpcs); // Not serializing above-center RPC
+ baos.write(TAGOFFS[_type] + 2);
+ XInt.packed(baos,_rpcs);
+ }
+ }
+ static TypeRPC packed( int tag, BAOS bais ) {
+ if( tag==0 ) return BOT;
+ if( tag==1 ) return malloc(XInt.make(bais.packed2()));
+ return malloc(XInt.packed(bais));
+ }
@Override
int hash() { return XInt.hash(_rpcs); }
diff --git a/src/main/java/com/seaofnodes/simple/type/TypeScalar.java b/src/main/java/com/seaofnodes/simple/type/TypeScalar.java
index e369e6a5..eaba0a1c 100644
--- a/src/main/java/com/seaofnodes/simple/type/TypeScalar.java
+++ b/src/main/java/com/seaofnodes/simple/type/TypeScalar.java
@@ -1,5 +1,6 @@
package com.seaofnodes.simple.type;
+import com.seaofnodes.simple.util.BAOS;
import com.seaofnodes.simple.util.SB;
import java.util.ArrayList;
@@ -39,6 +40,17 @@ public class TypeScalar extends Type {
@Override public boolean isHigh() { return this == TOP; }
@Override Type _makeStorage() { return BOT; }
+ // Reserve tags for both lattice endpoints. Unlike most concrete scalar
+ // families, generic scalar TOP can survive into a serialized graph.
+ @Override int TAGOFF() { return 2; }
+ @Override public void packed(BAOS baos, HashMap<String,Integer> strs) {
+ assert this == BOT || this == TOP;
+ baos.write(TAGOFFS[_type] + (this == BOT ? 0 : 1));
+ }
+ static TypeScalar packed(int tag) {
+ assert tag == 0 || tag == 1;
+ return tag == 0 ? BOT : TOP;
+ }
@Override public String str() { return isHigh() ? "~scalar" : "scalar"; }
@Override SB _print(SB sb, BitSet visit, boolean html) { return sb.p(str()); }
diff --git a/src/main/java/com/seaofnodes/simple/type/TypeStruct.java b/src/main/java/com/seaofnodes/simple/type/TypeStruct.java
index 67169009..4c5ae6ea 100644
--- a/src/main/java/com/seaofnodes/simple/type/TypeStruct.java
+++ b/src/main/java/com/seaofnodes/simple/type/TypeStruct.java
@@ -528,7 +528,22 @@ public class TypeStruct extends Type {
// Tags 5 - closed(+ nflds +name)
// Tags 6-10- open +#nflds (+name)
// Tags 11 - open (+ nflds +name)
-
+ @Override int TAGOFF() { return 24; }
+ @Override public void packed( BAOS baos, HashMap<String,Integer> strs ) {
+ baos.write(TAGOFFS[_type]+ (_fref ? 12 : 0) + (_open ? 6 : 0) + Math.min(5,_fields.length));
+ if( _fields.length >= 5 )
+ baos.packed2(_fields.length);
+ baos.packed2(strs.get(_name));
+ // TODO: Write aliases summary
+ }
+ static TypeStruct packed( int tag, BAOS bais, String[] strs ) {
+ boolean fref = tag >= 12;
+ if( fref ) tag -= 12;
+ int ntag = tag >= 6 ? tag-6 : tag;
+ int nflds = ntag < 5 ? ntag : bais.packed2();
+ String name = strs[bais.packed2()];
+ return malloc(name,ntag!=tag,fref,new Field[nflds]);
+ }
@Override
SB _print(SB sb, BitSet visit, boolean html ) {
diff --git a/src/main/java/com/seaofnodes/simple/type/TypeTuple.java b/src/main/java/com/seaofnodes/simple/type/TypeTuple.java
index 0d70bdf2..075698dc 100644
--- a/src/main/java/com/seaofnodes/simple/type/TypeTuple.java
+++ b/src/main/java/com/seaofnodes/simple/type/TypeTuple.java
@@ -124,6 +124,20 @@ public class TypeTuple extends Type {
@Override public Type at( int idx ) { return _types[idx]; }
@Override public void set( int idx, Type t ) { _types[idx] = t; }
+ // Reserve tags for tuples with 2,3,generic
+ @Override int TAGOFF() { return 4; }
+ @Override public void packed( BAOS baos, HashMap<String,Integer> strs ) {
+ if( _types.length==2 ) baos.write(TAGOFFS[_type] + 0);
+ else if( _types.length==3 ) baos.write(TAGOFFS[_type] + 1);
+ else if( _types.length==4 ) baos.write(TAGOFFS[_type] + 2);
+ else baos.write(TAGOFFS[_type] + 3).packed2(_types.length);
+ }
+ static TypeTuple packed( int tag, BAOS bais ) {
+ if( tag==0 ) return malloc(new Type[2]);
+ if( tag==1 ) return malloc(new Type[3]);
+ if( tag==2 ) return malloc(new Type[4]);
+ return malloc(new Type[bais.packed2()]);
+ }
@Override SB _print(SB sb, BitSet visit, boolean html ) {
if( this==TOP ) return sb.p("[TOP]");
diff --git a/src/main/java/com/seaofnodes/simple/type/XInt.java b/src/main/java/com/seaofnodes/simple/type/XInt.java
index 126a4e64..752c1419 100644
--- a/src/main/java/com/seaofnodes/simple/type/XInt.java
+++ b/src/main/java/com/seaofnodes/simple/type/XInt.java
@@ -196,6 +196,19 @@ public abstract class XInt {
}
+ public static void packed(BAOS baos, int[] xs) {
+ baos.packed1(xs.length);
+ for( int i=1; i<xs.length; i++ )
+ baos.packed4(xs[i]);
+ }
+ public static int[] packed(BAOS bais) {
+ int len = bais.packed1();
+ int[] xs = free(len);
+ for( int i=1; i<len; i++ )
+ xs[i] = bais.packed4();
+ return intern(xs);
+ }
+
// Next set bit, or -1
// for( int bit = XInt.next(bits,0); bit >=0; bit = XInt.next(bits,bit) ) {...bit...}
public static int next(int[] xs, int x) {
@@ -208,6 +221,17 @@ public abstract class XInt {
}
// Remap all bits
+ public static int[] remap( int[] xs, AryInt map ) {
+ if( xs==FULL ) return xs;
+ assert !isHigh(xs);
+ int[] ys = free(xs.length);
+ for( int bit = next(xs,0); bit >=0; bit = next(xs,bit) ) {
+ int bat = map.at(bit);
+ ys[idx(bat)] |= mask(bat);
+ }
+ return intern(ys);
+ }
+
public static String str(int[] xs) { return str(new SB(),xs).toString(); }