diff options
| author | Cliff Click <cliffc@acm.org> | 2025-05-05 11:17:51 -0700 |
|---|---|---|
| committer | Cliff Click <cliffc@acm.org> | 2025-05-05 11:17:51 -0700 |
| commit | 809e8701e48bd92cf0570ce536dbb74b84fccd9a (patch) | |
| tree | aaede913d237391b8e32799643baf811533df746 /chapter22/src/main | |
| parent | d70aee1171779c6a416a3910426ad034cf687c28 (diff) | |
| download | Simple-809e8701e48bd92cf0570ce536dbb74b84fccd9a.tar.gz Simple-809e8701e48bd92cf0570ce536dbb74b84fccd9a.zip | |
Printing a string
Bug fix big constant ELF numbering
cpool writing handles constant arrays
casting an array to an int works, gives the array base as int; suitable for passing to FFI. Is not GC stable.
Encode `ldi` ptrs.
Types have a size, logsize, and alignment.
Explicit struct layout
Diffstat (limited to 'chapter22/src/main')
11 files changed, 161 insertions, 76 deletions
diff --git a/chapter22/src/main/java/com/seaofnodes/simple/Parser.java b/chapter22/src/main/java/com/seaofnodes/simple/Parser.java index 9d313046..ab2c775c 100644 --- a/chapter22/src/main/java/com/seaofnodes/simple/Parser.java +++ b/chapter22/src/main/java/com/seaofnodes/simple/Parser.java @@ -656,6 +656,9 @@ public class Parser { t = tmp.makeRO(); expr = peep(new ReadOnlyNode(expr)); } + // Auto-widen array to i64 + if( t == TypeInteger.BOT && expr._type instanceof TypeMemPtr tmp && tmp._obj.isAry() ) + expr = peep(new AddNode(peep(new CastNode(t,ctrl(),expr)),con(tmp._obj.aryBase()))); // Auto-widen int to float expr = widenInt( expr, t ); // Auto-narrow wide ints to narrow ints @@ -678,7 +681,10 @@ public class Parser { * exprAsgn = var '=' exprAsgn | expr */ private Node parseDeclarationStatement() { + int old = pos(); Type t = type(); + if( peek('.') ) // Ambiguity static vars: "type.var", parse as expression + { pos(old); t=null; } if( t == null ) return require(parseAsgn(),";"); @@ -1123,7 +1129,6 @@ public class Parser { return init.in(idx); } - // Expect an identifier now Var n = requireLookupId(); Node rvalue = _scope.in(n); diff --git a/chapter22/src/main/java/com/seaofnodes/simple/codegen/ElfFile.java b/chapter22/src/main/java/com/seaofnodes/simple/codegen/ElfFile.java index 746ecfed..0eaaae7b 100644 --- a/chapter22/src/main/java/com/seaofnodes/simple/codegen/ElfFile.java +++ b/chapter22/src/main/java/com/seaofnodes/simple/codegen/ElfFile.java @@ -273,8 +273,6 @@ public class ElfFile { int start_global = num+1; // Add one to skip the final .rela.text local symbol for( Symbol a: symbols._symbols ) a._index = start_global++; - int bigConIdx = start_global; - start_global += enc._bigCons.size(); // create .text relocations DataSection text_rela = new DataSection(".rela.text", 4 /* SHT_RELA */); @@ -298,10 +296,10 @@ public class ElfFile { // Write relocations for the constant pool for( Encoding.Relo relo : enc._bigCons.values() ) { - Symbol glob = new Symbol("GLOB$"+bigConIdx, rdata._index, SYM_BIND_GLOBAL, SYM_TYPE_FUNC); + Symbol glob = new Symbol("CPOOL$"+start_global, rdata._index, SYM_BIND_GLOBAL, SYM_TYPE_FUNC); glob._value = relo._target; - glob._size = 1 << relo._t.log_size(); - glob._index = bigConIdx++; + glob._size = relo._t.size(); + glob._index = start_global++; symbols.push(glob); write8(text_rela._contents, relo._opStart+relo._off); write8(text_rela._contents, ((long)glob._index << 32L) | relo._elf ); @@ -313,6 +311,7 @@ public class ElfFile { text_rela._info = text._index; pushSection(text_rela); + // Final .rela.text symbol Symbol sym = new Symbol(text_rela._name, num++, SYM_BIND_LOCAL, SYM_TYPE_SECTION); sym._name_pos = text_rela._name_pos; sym._size = text_rela.size(); diff --git a/chapter22/src/main/java/com/seaofnodes/simple/codegen/Encoding.java b/chapter22/src/main/java/com/seaofnodes/simple/codegen/Encoding.java index 693c4077..722320e8 100644 --- a/chapter22/src/main/java/com/seaofnodes/simple/codegen/Encoding.java +++ b/chapter22/src/main/java/com/seaofnodes/simple/codegen/Encoding.java @@ -51,7 +51,7 @@ public class Encoding { public final Type _t; // Constant type public final byte _off; // Offset from start of opcode public final byte _elf; // ELF relocation type, e.g. 2/PC32 - public int _target; // Where constant is finally placede + public int _target; // Where constant is finally placed public int _opStart; // Opcode start Relo( Node op, Type t, byte off, byte elf ) { _op=op; _t=t; _off=off; _elf=elf; @@ -440,28 +440,56 @@ public class Encoding { // By alignment for( int align = 4; align >= 0; align-- ) { Ary<Relo> relos = raligns[align]; - if( relos != null ) - for( Relo relo : relos ) { - // Map from relo to constant start and patch - Integer target = targets.get(relo._t); - if( target==null ) { - targets.put(relo._t,target = bits.size()); - // Put constant into code space. - if( relo._t instanceof TypeTuple tt ) // Constant tuples put all entries at same alignment - for( Type tx : tt._types ) - addN(align,tx,bits); - else if( relo._t instanceof TypeStruct ts ) { - int alignX = ts.alignment(); - throw Utils.TODO(); - } else // Simple primitive (e.g. larger int, float) - addN(align,relo._t,bits); + if( relos == null ) continue; + for( Relo relo : relos ) { + // Map from relo to constant start and patch + Integer target = targets.get(relo._t); + if( target==null ) { + targets.put(relo._t,target = bits.size()); + // Write constant into constant pool + switch( relo._t ) { + case TypeTuple tt -> cpool(align,bits,tt); + case TypeStruct ts -> cpool(bits,ts); + // Simple primitive (e.g. larger int, float) + default -> addN(align,relo._t,bits); } - relo._target = target; - relo._opStart= _opStart[relo._op._nid]; - // Go ahead and locally patch in-memory - if( patch ) - ((RIPRelSize)relo._op).patch(this, relo._opStart, _opLen[relo._op._nid], relo._target - relo._opStart); } + // Record target address and opcode start + relo._target = target; + relo._opStart= _opStart[relo._op._nid]; + // Go ahead and locally patch in-memory + if( patch ) + ((RIPRelSize)relo._op).patch(this, relo._opStart, _opLen[relo._op._nid], relo._target - relo._opStart); + } + } + } + + // Constant tuples put all entries at same alignment + private void cpool(int align, BAOS bits, TypeTuple tt) { + for( Type tx : tt._types ) + addN(align,tx,bits); + } + + // Structs use internal field layout + private void cpool( BAOS bits, TypeStruct ts ) { + // Field order by offset + int[] layout = ts.layout(); + int off=0; // offset in the struct + for( int fn=0; fn<ts._fields.length; fn++ ) { + Field f = ts._fields[layout[fn]]; + int foff = ts. offset(layout[fn]); + // Pad up to field + while( off < foff ) { bits.write(0); off++; }; + // Constant array fields are special + if( f._fname=="[]" ) { // Must be a constant array + ts._con.write(bits); // Write the constant array bits + off += ts._con.len(); + } else { + int log = f._type.log_size(); + if( f._fname=="#" ) addN(log,ts._con.len(),bits); // Must be a constant array + else addN(log,f._type ,bits); + off += 1<<log; + } } } diff --git a/chapter22/src/main/java/com/seaofnodes/simple/node/CastNode.java b/chapter22/src/main/java/com/seaofnodes/simple/node/CastNode.java index 0947bcf0..8d8f0257 100644 --- a/chapter22/src/main/java/com/seaofnodes/simple/node/CastNode.java +++ b/chapter22/src/main/java/com/seaofnodes/simple/node/CastNode.java @@ -4,6 +4,8 @@ import com.seaofnodes.simple.Parser; import com.seaofnodes.simple.Utils; import com.seaofnodes.simple.type.Type; import com.seaofnodes.simple.type.TypeInteger; +import com.seaofnodes.simple.type.TypeMemPtr; + import java.util.BitSet; // Upcast (join) the input to a t. Used after guard test to lift an input. @@ -37,6 +39,9 @@ public class CastNode extends Node { @Override public Type compute() { + // Cast array to int + if( _t == TypeInteger.BOT && in(1)._type instanceof TypeMemPtr tmp && tmp._obj.isAry() ) + return _t; return in(1)._type.join(_t); } diff --git a/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TMPX86.java b/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TMPX86.java index 00219576..70b9dbb0 100644 --- a/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TMPX86.java +++ b/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TMPX86.java @@ -6,7 +6,6 @@ import com.seaofnodes.simple.node.*; import com.seaofnodes.simple.type.TypeMemPtr; public class TMPX86 extends ConstantNode implements MachNode, RIPRelSize{ - private byte _encSize; TMPX86(ConstantNode con ) { super(con); } @Override public String op() { return "ldp"; } @Override public RegMask regmap(int i) { return null; } @@ -16,16 +15,13 @@ public class TMPX86 extends ConstantNode implements MachNode, RIPRelSize{ @Override public void encoding( Encoding enc ) { short dst = enc.reg(this); - _encSize = (byte)(x86_64_v2.rexF(dst, 0, 0, false, enc) + 1/*Op*/ + 1/*MODRM*/ + 4/*offset*/); + x86_64_v2.rexF(dst, 0, 0, true, enc); enc.add1(0x8D).add1(x86_64_v2.modrm(x86_64_v2.MOD.INDIRECT, dst, 0b101)).add4(0); - enc.largeConstant(this,((TypeMemPtr)_con)._obj,_encSize-4,2/*ELF encoding PC32*/); + enc.largeConstant(this,((TypeMemPtr)_con)._obj,7-4,2/*ELF encoding PC32*/); } // Delta is from opcode start - @Override public byte encSize(int delta) { - // TODO: 1-byte RIP offset? - return (byte)_encSize; - } + @Override public byte encSize(int delta) { return (byte)7; } // Delta is from opcode start @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { diff --git a/chapter22/src/main/java/com/seaofnodes/simple/print/ASMPrinter.java b/chapter22/src/main/java/com/seaofnodes/simple/print/ASMPrinter.java index da269d31..3b946cb5 100644 --- a/chapter22/src/main/java/com/seaofnodes/simple/print/ASMPrinter.java +++ b/chapter22/src/main/java/com/seaofnodes/simple/print/ASMPrinter.java @@ -1,9 +1,11 @@ package com.seaofnodes.simple.print; +import com.seaofnodes.simple.Ary; import com.seaofnodes.simple.SB; import com.seaofnodes.simple.Utils; import com.seaofnodes.simple.codegen.CodeGen; import com.seaofnodes.simple.codegen.Encoding; +import com.seaofnodes.simple.codegen.Encoding.Relo; import com.seaofnodes.simple.node.*; import com.seaofnodes.simple.type.*; import java.util.HashSet; @@ -25,49 +27,70 @@ public abstract class ASMPrinter { iadr++; // constant pool + iadr = (iadr+15)&-16; // pad to 16 Encoding enc = code._encoding; - if( enc!=null && !enc._bigCons.isEmpty() ) { - iadr = (iadr+15)&-16; // pad to 16 + if( enc!=null && !enc._bigCons.isEmpty() && iadr < enc._bits.size() ) { + // radix sort the big constants by alignment + Ary<Relo>[] raligns = new Ary[5]; + for( Node op : enc._bigCons.keySet() ) { + Relo relo = enc._bigCons.get(op); + int align = relo._t.alignment(); + Ary<Relo> relos = raligns[align]==null ? (raligns[align]=new Ary<>( Relo.class)) : raligns[align]; + relos.add(relo); + } + HashSet<Type> targets = new HashSet<>(); sb.p("--- Constant Pool ------").nl(); - // By log size - for( int log = 3; log >= 0; log-- ) { - for( Node op : enc._bigCons.keySet() ) { - Encoding.Relo relo = enc._bigCons.get(op); + + // By alignment + for( int align = 4; align >= 0; align-- ) { + Ary<Relo> relos = raligns[align]; + if( relos == null ) continue; + for( Relo relo : relos ) { if( targets.contains(relo._t) ) continue; targets.add(relo._t); - if( relo._t.log_size()==log ) { - sb.hex2(iadr).p(" "); - if( relo._t instanceof TypeTuple tt ) { - for( Type tx : tt._types ) { - switch( log ) { - case 0: sb.hex1(enc.read1(iadr)); break; - case 1: sb.hex2(enc.read2(iadr)); break; - case 2: sb.hex4(enc.read4(iadr)); break; - case 3: sb.hex8(enc.read8(iadr)); break; - } - iadr += (1<<log); - sb.p(" "); - } - } else { - switch( log ) { - case 0: sb.hex1(enc.read1(iadr)).fix(9-1,""); break; - case 1: sb.hex2(enc.read2(iadr)).fix(9-2,""); break; - case 2: sb.hex4(enc.read4(iadr)).fix(9-4,""); break; - case 3: sb.hex8(enc.read8(iadr)).p(" "); break; - } - iadr += (1<<log); + sb.hex2(iadr).p(" "); + switch( relo._t ) { + case TypeTuple tt -> { + for( Type tx : tt._types ) { + pN(enc,sb,iadr,align).p(" "); + iadr += (1<<align); } - relo._t.print(sb).nl(); } + + case TypeStruct ts -> { + int sz = ts.size(); + int log = 1<<align; + sz = (sz + (log -1)) & -log; // Round up final padding + for( int i=0; i<sz; i++ ) + sb.hex1(enc.read1(iadr++)); + } + + // Simple primitive (e.g. larger int, float) + default -> { + pN(enc,sb,iadr,align).fix(9-(1<<align),""); + iadr += (1<<align); + } + } + relo._t.print(sb).nl(); } } } + return sb; + } + private static SB pN( Encoding enc, SB sb, int iadr, int log ) { + switch( log ) { + case 0: sb.hex1(enc.read1(iadr)); break; + case 1: sb.hex2(enc.read2(iadr)); break; + case 2: sb.hex4(enc.read4(iadr)); break; + case 3: sb.hex8(enc.read8(iadr)); break; + } return sb; } + private static int print(int iadr, SB sb, CodeGen code, FunNode fun, int cfgidx) { FunNode old=null; if( code._encoding!=null ) { diff --git a/chapter22/src/main/java/com/seaofnodes/simple/sys/sys.smp b/chapter22/src/main/java/com/seaofnodes/simple/sys/sys.smp index 6b12ba20..67de77ef 100644 --- a/chapter22/src/main/java/com/seaofnodes/simple/sys/sys.smp +++ b/chapter22/src/main/java/com/seaofnodes/simple/sys/sys.smp @@ -6,7 +6,7 @@ struct sys { /** https://www.man7.org/linux/man-pages/man2/<libc call>.2.html */ struct libc { // fd buf len -> len - { i32 u8[~] i32 -> i32 } write = "C"; + { i32 i64 i32 -> i32 } write = "C"; // addr len prot flags fd off -> void* { i64 i64 i32 i32 i32 i32 -> i64 } mmap = "C"; // mmap flags diff --git a/chapter22/src/main/java/com/seaofnodes/simple/type/Type.java b/chapter22/src/main/java/com/seaofnodes/simple/type/Type.java index bc98f358..a44cdd61 100644 --- a/chapter22/src/main/java/com/seaofnodes/simple/type/Type.java +++ b/chapter22/src/main/java/com/seaofnodes/simple/type/Type.java @@ -223,6 +223,7 @@ public class Type { // Size 0 means this either takes no space (such as a known-zero field) // or isn't a scalar to be stored in memory. public int log_size () { return 3; } // log-size of a type; log_size for a struct is usually undefined + public int size() { return 1<<log_size(); } public int alignment() { return log_size(); } // alignment; for structs, max align of Fields // ---------------------------------------------------------- diff --git a/chapter22/src/main/java/com/seaofnodes/simple/type/TypeConAry.java b/chapter22/src/main/java/com/seaofnodes/simple/type/TypeConAry.java index b57926bb..8a19c9d7 100644 --- a/chapter22/src/main/java/com/seaofnodes/simple/type/TypeConAry.java +++ b/chapter22/src/main/java/com/seaofnodes/simple/type/TypeConAry.java @@ -3,6 +3,7 @@ package com.seaofnodes.simple.type; import com.seaofnodes.simple.SB; import com.seaofnodes.simple.Utils; import java.util.ArrayList; +import java.io.ByteArrayOutputStream; /** * Represents a constant array of primitives @@ -37,6 +38,7 @@ public class TypeConAry<A> extends Type { return BOT; } + // Meet-over-elements type public Type elem() { if( _ary==null ) return _any ? TOP : BOTTOM; @@ -50,8 +52,9 @@ public class TypeConAry<A> extends Type { return TypeInteger.make(min,max); } long at(int idx) { throw Utils.TODO(); } - int len() { throw Utils.TODO(); } + public int len() { throw Utils.TODO(); } @Override public int alignment() { return 0; } + public void write( ByteArrayOutputStream baos ) { throw Utils.TODO(); } @Override boolean eq(Type t) { TypeConAry ary = (TypeConAry)t; // Invariant diff --git a/chapter22/src/main/java/com/seaofnodes/simple/type/TypeConAryB.java b/chapter22/src/main/java/com/seaofnodes/simple/type/TypeConAryB.java index 8a212867..34475ac1 100644 --- a/chapter22/src/main/java/com/seaofnodes/simple/type/TypeConAryB.java +++ b/chapter22/src/main/java/com/seaofnodes/simple/type/TypeConAryB.java @@ -2,6 +2,7 @@ package com.seaofnodes.simple.type; import java.util.ArrayList; import java.util.Arrays; +import java.io.ByteArrayOutputStream; public class TypeConAryB extends TypeConAry<byte[]> { private TypeConAryB(byte[] bs) { super(false,bs); } @@ -11,9 +12,10 @@ public class TypeConAryB extends TypeConAry<byte[]> { static final TypeConAryB ABCD = make("abcd"); public static void gather(ArrayList<Type> ts) { ts.add(ABC); ts.add(ABCD); } @Override long at(int idx) { return _ary[idx]; } - @Override int len() { return _ary.length; } + @Override public int len() { return _ary.length; } @Override public int alignment() { return 0; } @Override public String str() { return "[\""+new String(_ary)+"\"]"; } @Override boolean eq(Type t) { return Arrays.equals(_ary,((TypeConAryB)t)._ary); } @Override int hash() { return Arrays.hashCode(_ary); } + @Override public void write( ByteArrayOutputStream baos ) { baos.write(_ary,0,_ary.length); } } diff --git a/chapter22/src/main/java/com/seaofnodes/simple/type/TypeStruct.java b/chapter22/src/main/java/com/seaofnodes/simple/type/TypeStruct.java index e0426e0c..827e85f0 100644 --- a/chapter22/src/main/java/com/seaofnodes/simple/type/TypeStruct.java +++ b/chapter22/src/main/java/com/seaofnodes/simple/type/TypeStruct.java @@ -205,6 +205,7 @@ public class TypeStruct extends Type { // log_size for a struct is not defined, unless its exactly some power of // 2. *Total size* is well-defined, and is available in the offsets. @Override public int log_size() { throw Utils.TODO(); } + @Override public int size() { return offset(_fields.length); } @Override public int alignment() { int align = 0; for( Field f : _fields ) @@ -251,7 +252,7 @@ public class TypeStruct extends Type { @Override public String str() { return _con==TypeConAry.BOT ? _name : _con.str(); } - public boolean isAry() { return _fields.length==2 && _fields[1]._fname.equals("[]"); } + public boolean isAry() { return _fields.length>=2 && _fields[_fields.length-1]._fname=="[]"; } public int aryBase() { assert isAry(); @@ -274,14 +275,12 @@ public class TypeStruct extends Type { // Compute a layout for a collection of fields assert _fields != null; // No forward refs - // Array layout is different: len,[pad],body... - if( isAry() ) - return _offs = new int[]{ 0, _fields[1]._type.log_size() < 3 ? 4 : 8 }; - // Compute a layout - int[] cnts = new int[4]; // Count of fields at log field size - for( Field f : _fields ) - cnts[f._type.log_size()]++; // Log size is 0(byte), 1(i16/u16), 2(i32/f32), 3(i64/dbl) + int[] cnts = new int[5]; // Count of fields at log field size + int flen = _fields.length; + if( isAry() ) flen--; // Array field is aligned differently + for( int i=0; i<flen; i++ ) + cnts[_fields[i]._type.log_size()]++; // Log size is 0(byte), 1(i16/u16), 2(i32/f32), 3(i64/dbl) int off = 0, idx = 0; // Base common struct fields go here, e.g. Mark/Klass // Compute offsets to the start of each power-of-2 aligned fields. int[] offs = new int[4]; @@ -292,14 +291,38 @@ public class TypeStruct extends Type { // Assign offsets to all fields. // Really a hidden radix sort. _offs = new int[_fields.length+1]; - for( Field f : _fields ) { - int log = f._type.log_size(); + for( int i=0; i<flen; i++ ) { + int log = _fields[i]._type.log_size(); _offs[idx++] = offs[log]; // Field offset offs[log] += 1<<log; // Next field offset at same alignment cnts[log]--; // Count down, should be all zero at end } - _offs[_fields.length] = (off+7)& ~7; // Round out to max alignment + + // Array bits + if( isAry() ) { + // Pad out to the element alignment + int align = (_fields[flen]._fname!="[]" || _con==TypeConAry.BOT ? _fields[flen]._type : _con).alignment(); + off = (off + ((1<<align)-1)) & -(1<<align); + _offs[flen] = off; + if( _con!=TypeConAry.BOT ) + _offs[flen+1] = off+_con.len(); + } else + _offs[flen] = off; // Max struct size, no trailing padding return _offs; } + // Field order with increasing offset + public int[] layout() { + offset(0); + int len = _offs.length; + int[] is = new int[len]; + for( int i=0; i<len; i++ ) is[i] = i; + // Stoopid hand rolled bubble sort + for( int i=0; i<len; i++ ) + for( int j=i+1; j<len; j++ ) + if( _offs[is[j]] < _offs[is[i]] ) + { int tmp = is[i]; is[i] = is[j]; is[j] = tmp; } + return is; + } + } |
