diff options
| author | Cliff Click <cliffc@acm.org> | 2025-04-24 14:38:36 -0700 |
|---|---|---|
| committer | Cliff Click <cliffc@acm.org> | 2025-07-03 09:50:14 -0700 |
| commit | 49534ffa88fedb3a4ebc42094e9edb27d456e6d6 (patch) | |
| tree | d1a0b5f0fd6deb4c38681db88755ccc79f534bb7 /src/main | |
| parent | 892deaa10d8d2bae0e839bb42e4c2ce830eaff39 (diff) | |
| download | Simple-49534ffa88fedb3a4ebc42094e9edb27d456e6d6.tar.gz Simple-49534ffa88fedb3a4ebc42094e9edb27d456e6d6.zip | |
Chapter 21: Instruction Encoding and ELF
Co-authored-by: godalming123 <68993177+godalming123@users.noreply.github.com>
Co-authored-by: Tamás Hadházy <85063808+Hels15@users.noreply.github.com>
Diffstat (limited to 'src/main')
230 files changed, 6485 insertions, 3875 deletions
diff --git a/src/main/java/com/seaofnodes/simple/IterPeeps.java b/src/main/java/com/seaofnodes/simple/IterPeeps.java index 6208209f..66e8a4a8 100644 --- a/src/main/java/com/seaofnodes/simple/IterPeeps.java +++ b/src/main/java/com/seaofnodes/simple/IterPeeps.java @@ -49,6 +49,7 @@ public class IterPeeps { public IterPeeps( long seed ) { _work = new WorkList<>(seed); } + @SuppressWarnings("unchecked") public <N extends Node> N add( N n ) { return (N)_work.push(n); } public void addAll( Ary<Node> ary ) { _work.addAll(ary); } @@ -126,6 +127,7 @@ public class IterPeeps { * Classic WorkList, with a fast add/remove, dup removal, random pull. * The Node's nid is used to check membership in the worklist. */ + @SuppressWarnings("unchecked") public static class WorkList<E extends Node> { private Node[] _es; diff --git a/src/main/java/com/seaofnodes/simple/Parser.java b/src/main/java/com/seaofnodes/simple/Parser.java index 07f82625..219c3c44 100644 --- a/src/main/java/com/seaofnodes/simple/Parser.java +++ b/src/main/java/com/seaofnodes/simple/Parser.java @@ -144,7 +144,25 @@ public class Parser { // Parse whole program, as-if function header "{ int arg -> body }" parseFunctionBody(_code._main,loc(),"arg"); - _code.link(_code._main)._name = "main"; + + // Kill an empty default main. Keep only if it was explicitly defined + // (programmer asked for a "main") or it has stuff (i.e. beginner + // default main). + FunNode main = _code.link(_code._main); + StopNode stop = _code._stop; + if( main.ret().expr() instanceof ConstantNode && main.ret().mem().in(0)==main && stop.nIns() > 1 ) { + // Kill an empty default main; so it does not attempt to put a + // "main" in any final ELF file + stop.delDef(stop._inputs.find(main.ret())); + } else { + // We have a non-empty default main. + // Check for an explicit main + for( Node n : stop._inputs ) + if( n instanceof FunNode fun && fun._name.equals("main") ) + // Found an explicit "main" AND we have a default "main" + throw error("Cannot define both an explicit main and a default main"); + main._name = "main"; + } if( !_lexer.isEOF() ) throw _errorSyntax("unexpected"); @@ -154,7 +172,7 @@ public class Parser { for( StructNode init : INITS.values() ) init.unkeep().kill(); INITS.clear(); - _code._stop.peephole(); + stop.peephole(); if( show ) showGraph(); } @@ -1407,6 +1425,7 @@ public class Parser { // Post-call setup CallEndNode cend = (CallEndNode)new CallEndNode(call).peephole(); + call.peephole(); // Rerun peeps after CallEnd, allows early inlining // Control from CallEnd ctrl(new CProjNode(cend,0,ScopeNode.CTRL).peephole()); // Memory from CallEnd @@ -1669,6 +1688,7 @@ public class Parser { while( Character.isDigit(c=nextChar()) ) ; if( !(c=='e' || c=='.') ) return --_position - old; + if( peek() == '-' ) nextChar(); while( Character.isDigit(c=nextChar()) || c=='e' || c=='.' ) ; return -(--_position - old); } diff --git a/src/main/java/com/seaofnodes/simple/SB.java b/src/main/java/com/seaofnodes/simple/SB.java index a45f0ddf..978fbed9 100644 --- a/src/main/java/com/seaofnodes/simple/SB.java +++ b/src/main/java/com/seaofnodes/simple/SB.java @@ -29,21 +29,28 @@ public final class SB { public SB p( int s ) { _sb.append(s); return this; } public SB p( long s ) { _sb.append(s); return this; } public SB p( boolean s) { _sb.append(s); return this; } - // 4 hex digits - public SB hex4(int s) { - assert (s>>4*4)==0; // Fits in 16 bits - for( int i=0; i<4; i++ ) { - int digit = (s>>((3-i)*4)) & 0xf; - _sb.append((char)((digit <= 9 ? '0' : ('A'-10))+digit)); - } + // 1 byte, 2 hex digits, 8 bits + public SB hex1(int s) { + int digit = (s>>4) & 0xf; + _sb.append((char)((digit <= 9 ? '0' : ('A'-10))+digit)); + digit = s & 0xf; + _sb.append((char)((digit <= 9 ? '0' : ('A'-10))+digit)); return this; } + // 2 bytes, 4 hex digits, 16 bits, Big Endian + public SB hex2(int s) { return hex1(s>> 8).hex1(s); } + // 4 bytes, 8 hex digits, 32 bits, Big Endian + public SB hex4(int s) { return hex2(s>>16).hex2(s); } + // 8 bytes, 16 hex digits, 64 bits, Big Endian + public SB hex8(long s) { return hex4((int)(s>>32)).hex4((int)s); } + // Fixed width field public SB fix( int sz, String s ) { for( int i=0; i<sz; i++ ) _sb.append( i < s.length() ? s.charAt(i) : ' '); return this; } + public char at(int idx ) { return _sb.charAt(idx); } // Not spelled "p" on purpose: too easy to accidentally say "p(1.0)" and // suddenly call the autoboxed version. @@ -83,6 +90,8 @@ public final class SB { // public SB unchar() { return unchar(1); } public SB unchar(int x) { _sb.setLength(_sb.length()-x); return this; } + public SB setLen(int len) { _sb.setLength(len); return this; } + public String subString(int start, int end ) { return _sb.substring(start,end); } public SB clear() { _sb.setLength(0); return this; } public int len() { return _sb.length(); } diff --git a/src/main/java/com/seaofnodes/simple/codegen/BuildLRG.java b/src/main/java/com/seaofnodes/simple/codegen/BuildLRG.java index 88b49863..e9b5eb2e 100644 --- a/src/main/java/com/seaofnodes/simple/codegen/BuildLRG.java +++ b/src/main/java/com/seaofnodes/simple/codegen/BuildLRG.java @@ -1,6 +1,5 @@ package com.seaofnodes.simple.codegen; -import com.seaofnodes.simple.Utils; import com.seaofnodes.simple.node.*; import com.seaofnodes.simple.type.TypeMem; @@ -27,7 +26,7 @@ abstract public class BuildLRG { break; // If none, make one. if( lrg==null ) lrg = alloc.newLRG(n); - if( phi instanceof MachNode mach ) defLRG(alloc,n); + if( phi instanceof MachNode ) defLRG(alloc,n); // Pass 2: everybody uses the same LRG lrg=alloc.union(lrg,phi); for( int i=phi instanceof ParmNode ? 2 : 1; i<n.nIns(); i++ ) @@ -55,8 +54,8 @@ abstract public class BuildLRG { if( n.in(i)!=null ) { LRG lrg2 = alloc.lrg(n.in(i)); if( lrg2 != null ) { // Anti-dep or other, no LRG - RegMask use_mask = mach.regmap(i); - if( !lrg2.machUse(mach,(short)i,use_mask.size1()).and(use_mask) ) + RegMask use_mask = mach.regmap(i); // use_mask is also null for anti-dep + if( use_mask!=null && !lrg2.machUse(mach,(short)i,use_mask.size1()).and(use_mask) ) alloc.fail(lrg2); // Empty register mask, must split } } @@ -64,7 +63,7 @@ abstract public class BuildLRG { } // MultiNodes have projections which set registers - if( n instanceof MultiNode ) + if( n instanceof MultiNode && !(n instanceof CFGNode) ) for( Node proj : n.outs() ) if( proj instanceof MachNode ) defLRG(alloc,proj); diff --git a/src/main/java/com/seaofnodes/simple/codegen/Coalesce.java b/src/main/java/com/seaofnodes/simple/codegen/Coalesce.java new file mode 100644 index 00000000..e2fac779 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/codegen/Coalesce.java @@ -0,0 +1,105 @@ +package com.seaofnodes.simple.codegen; + +import com.seaofnodes.simple.Ary; +import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.node.*; +import java.util.BitSet; +import java.util.IdentityHashMap; + +// Coalesce copies +abstract public class Coalesce { + + public static boolean coalesce( int round, RegAlloc alloc ) { + + // Walk all the splits, looking for coalesce chances + boolean progress = false; + for( CFGNode bb : alloc._code._cfg ) { + outer: + for( int j=0; j<bb.nOuts(); j++ ) { + Node n = bb.out(j); + if( !(n instanceof SplitNode) ) continue; + LRG v1 = alloc.lrg(n); + LRG v2 = alloc.lrg(n.in(1)); + if( v1 != v2 ) { + LRG ov1 = v1, ov2 = v2; + // Get the smaller neighbor count in v1 + if( v1.nadj() > v2.nadj() ) + { v1 = v2; v2 = alloc.lrg(n); } + int v2len = v2._adj==null ? 0 : v2._adj._len; + // See that they do not conflict (coalescing would make a self-conflict) + if( v1._adj!=null && v2._adj!=null && v1._mask.overlap(v2._mask) ) { + // Walk the smaller neighbors, and add to the larger. + // Check for direct conflicts along the way. + for( LRG v3 : v1._adj ) { + if( v3==v2 ) { + // Direct conflict, skip this LRG pair + v2._adj.setLen(v2len); + continue outer; + } + // Add missing LRGs from V1 to V2 + if( v2._adj.find(v3)== -1 ) + v2._adj.push(v3); + } + } + // Most constrained mask + RegMask mask = v1._mask==v2._mask ? v1._mask : v1._mask.copy().and(v2._mask); + // Check for capacity + if( v2.nadj() >= mask.size() ) { + // Fails capacity, will not be trivial colorable + if( v2._adj!=null ) v2._adj.setLen(v2len); + continue; + } + + // Union lrgs. + progress = true; + + // Since `n` is dying, remove from the LRGS BEFORE doing + // the union; this will let the union code keep the + // matching bits from the other side, preserving some + // info for biased coloring + if( ov1. _machDef==n ) ov1. _machDef = null; + if( ov1._splitDef==n ) ov1._splitDef = null; + if( ov2. _machUse==n ) ov2. _machUse = null; + if( ov2._splitUse==n ) ov2._splitUse = null; + // Keep larger adjacency list. + Ary<LRG> larger = v2._adj; + v2._adj = null; + + // Union lrgs. Swaps again, based on small lrg + LRG vWin = v1.union(v2); + vWin._adj = larger; + LRG vLose = v1.leader() ? v2 : v1; + + // LRGs below v2len were always in v2, but maybe not in v1. + // LRGs above v2len are in v1 but were not in v2. + + // Walk v2's neighbors, replacing vLose with vWin. + if( larger != null ) + for( LRG vn : larger ) { + int idx = vn._adj.find(vLose); + if( idx != -1 ) + vn._adj.del(idx); + if( vn._adj.find(vWin) == -1 ) + vn._adj.add(vWin); + } + } else { + + // Since `n` is dying, remove from the LRGS + if( v1. _machDef==n ) v1. _machDef=null; + if( v1. _machUse==n ) v1. _machUse=null; + if( v1._splitDef==n ) v1._splitDef=null; + if( v1._splitUse==n ) v1._splitUse=null; + } + + // Remove junk split + n.removeSplit(); + j--; + } + } + + if( progress ) + alloc.unify(); + + return true; + } +} diff --git a/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java b/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java index a1294890..9ec3bc7b 100644 --- a/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java +++ b/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java @@ -4,11 +4,14 @@ import com.seaofnodes.simple.*; import com.seaofnodes.simple.node.*; import com.seaofnodes.simple.print.*; import com.seaofnodes.simple.type.*; +import java.io.IOException; import java.util.BitSet; import java.util.HashMap; import java.util.IdentityHashMap; +@SuppressWarnings("unchecked") public class CodeGen { + public static final String PORTS = "com.seaofnodes.simple.node.cpus"; // Last created CodeGen as a global; used all over to avoid passing about a // "context". public static CodeGen CODE; @@ -17,11 +20,13 @@ public class CodeGen { Parse, // Parse ASCII text into Sea-of-Nodes IR Opto, // Run ideal optimizations TypeCheck, // Last check for bad programs + LoopTree, // Build a loop tree; break infinite loops InstSelect, // Convert to target hardware nodes Schedule, // Global schedule (code motion) nodes LocalSched, // Local schedule RegAlloc, // Register allocation Encoding, // Encoding + Export, // Export } public Phase _phase; @@ -30,7 +35,6 @@ public class CodeGen { public final String _src; // Compile-time known initial argument type public final TypeInteger _arg; - // --------------------------- public CodeGen( String src ) { this(src, TypeInteger.BOT, 123L ); } public CodeGen( String src, TypeInteger arg, long workListSeed ) { @@ -46,6 +50,27 @@ public class CodeGen { } + // All passes up to Phase, except ELF + public CodeGen driver( Phase phase ) { return driver(phase,null,null); } + public CodeGen driver( Phase phase, String cpu, String callingConv ) { + if( _phase==null ) parse(); + if( _phase.ordinal() < phase.ordinal() ) opto(); + if( _phase.ordinal() < phase.ordinal() ) typeCheck(); + if( _phase.ordinal() < phase.ordinal() ) loopTree(); + if( _phase.ordinal() < phase.ordinal() && cpu != null ) instSelect(cpu,callingConv); + if( _phase.ordinal() < phase.ordinal() ) GCM(); + if( _phase.ordinal() < phase.ordinal() ) localSched(); + if( _phase.ordinal() < phase.ordinal() ) regAlloc(); + if( _phase.ordinal() < phase.ordinal() ) encode(); + return this; + } + + // Run all the phases through final ELF emission + public CodeGen driver( String cpu, String callingConv, String obj ) throws IOException { + return driver(Phase.Encoding,cpu,callingConv).exportELF(obj); + } + + // --------------------------- /** * A counter, for unique node id generation. Starting with value 1, to @@ -62,6 +87,24 @@ public class CodeGen { public int getALIAS() { return _alias++; } + // idepths are cached and valid until *inserting* CFG edges (deleting is + // OK). This happens with inlining, which bumps the version to bulk + // invalidate the idepth caches. + private int _iDepthVersion = 0; + public void invalidateIDepthCaches() { _iDepthVersion++; } + public boolean validIDepth(int idepth) { + if( idepth==0 ) return false; + if( _iDepthVersion==0 ) return true; + return (idepth%100)==_iDepthVersion; + } + public int iDepthAt(int idepth) { + return 100*idepth+_iDepthVersion; + } + public int iDepthFrom(int idepth) { + assert idepth==0 || validIDepth(idepth); + return idepth+100; + } + // Popular visit bitset, declared here, so it gets reused all over public final BitSet _visit = new BitSet(); public BitSet visit() { assert _visit.isEmpty(); return _visit; } @@ -154,7 +197,7 @@ public class CodeGen { // --------------------------- // Last check for bad programs - int _tTypeCheck; + public int _tTypeCheck; public CodeGen typeCheck() { // Demand phase Opto for cleaning up dead control flow at least, // required for the following GCM. @@ -169,17 +212,42 @@ public class CodeGen { return this; } + // --------------------------- + // Build the loop tree; break never-exit loops + public int _tLoopTree; + public CodeGen loopTree() { + assert _phase.ordinal() <= Phase.TypeCheck.ordinal(); + _phase = Phase.LoopTree; + long t0 = System.currentTimeMillis(); + // Build the loop tree, fix never-exit loops + _start.buildLoopTree(_stop); + _tLoopTree = (int)(System.currentTimeMillis() - t0); + return this; + } // --------------------------- // Code generation CPU target public Machine _mach; - // + // Chosen calling convention (usually either Win64 or SystemV) public String _callingConv; + // Callee save registers + public RegMask _callerSave; + + // All returns have the following inputs: + // 0 - ctrl + // 1 - memory + // 2 - varies with returning GPR,FPR + // 3 - RPC + // 4+- Caller save registers + public RegMask[] _retMasks; + // Return Program Counter + public RegMask _rpcMask; // Convert to target hardware nodes public int _tInsSel; - public CodeGen instSelect( String base, String cpu, String callingConv ) { - assert _phase.ordinal() <= Phase.TypeCheck.ordinal(); + public CodeGen instSelect( String cpu, String callingConv ) { return instSelect(cpu,callingConv,PORTS); } + public CodeGen instSelect( String cpu, String callingConv, String base ) { + assert _phase.ordinal() == Phase.LoopTree.ordinal(); _phase = Phase.InstSelect; _callingConv = callingConv; @@ -188,9 +256,30 @@ public class CodeGen { // Look for CPU in fixed named place: // com.seaofnodes.simple.node.cpus."cpu"."cpu.class" String clzFile = base+"."+cpu+"."+cpu; - try { _mach = ((Class<Machine>) Class.forName( clzFile )).getDeclaredConstructor().newInstance(); } + try { _mach = ((Class<Machine>) Class.forName( clzFile )).getDeclaredConstructor(new Class[]{CodeGen.class}).newInstance(this); } catch( Exception e ) { throw new RuntimeException(e); } + // Build global copies of common register masks. + long callerSave = _mach.callerSave(); + long neverSave = _mach. neverSave(); + int maxReg = Math.min(64,_mach.regs().length); + assert maxReg>=64 || (-1L << maxReg & callerSave)==0; // No stack slots in callerSave + _callerSave = new RegMask(callerSave); + + // Build a Return RegMask array. All returns have the following inputs: + // 0 - ctrl + // 1 - memory + // 2 - varies with returning GPR,FPR + // 3 - RPC + // 4+- Caller save registers + _retMasks = new RegMask[(maxReg-_callerSave.size()-Long.bitCount( neverSave ))+4]; + for( int reg=0, i=4; reg<maxReg; reg++ ) + if( !_callerSave.test(reg) && ((1L<<reg)&neverSave)==0 ) + _retMasks[i++] = new RegMask(reg); + _rpcMask = new RegMask(_mach.rpc()); + _retMasks[3] = _rpcMask; + + // Convert to machine ops long t0 = System.currentTimeMillis(); _uid = 1; // All new machine nodes reset numbering @@ -226,7 +315,7 @@ public class CodeGen { for( int i=0; i < x.nIns(); i++ ) x._inputs.set(i, _instSelect(x.in(i),map) ); if( x instanceof MachNode mach ) - mach.postSelect(); // Post selection action + mach.postSelect(this); // Post selection action // Updates forward edges only. n._outputs.clear(); @@ -246,8 +335,8 @@ public class CodeGen { // --------------------------- - // Control Flow Graph in RPO order. - int _tGCM; + // Control Flow Graph in Reverse Post Order. + public int _tGCM; public Ary<CFGNode> _cfg = new Ary<>(CFGNode.class); // Global schedule (code motion) nodes @@ -257,8 +346,6 @@ public class CodeGen { _phase = Phase.Schedule; long t0 = System.currentTimeMillis(); - // Build the loop tree, fix never-exit loops - _start.buildLoopTree(_stop); GlobalCodeMotion.buildCFG(this); _tGCM = (int)(System.currentTimeMillis() - t0); if( show ) @@ -268,7 +355,7 @@ public class CodeGen { // --------------------------- // Local (basic block) scheduler phase, a classic list scheduler - int _tLocal; + public int _tLocal; public CodeGen localSched() { assert _phase == Phase.Schedule; _phase = Phase.LocalSched; @@ -281,7 +368,7 @@ public class CodeGen { // --------------------------- // Register Allocation - int _tRegAlloc; + public int _tRegAlloc; public RegAlloc _regAlloc; public CodeGen regAlloc() { assert _phase == Phase.LocalSched; @@ -292,31 +379,46 @@ public class CodeGen { _tRegAlloc = (int)(System.currentTimeMillis() - t0); return this; } - public String reg(Node n) { + + // Human readable register name + public String reg(Node n) { return reg(n,null); } + public String reg(Node n, FunNode fun) { if( _phase.ordinal() >= Phase.RegAlloc.ordinal() ) { - String s = _regAlloc.reg(n); + String s = _regAlloc.reg(n,fun); if( s!=null ) return s; } return "N"+ n._nid; } + // --------------------------- // Encoding - int _tEncode; + public int _tEncode; + public Encoding _encoding; // Encoding object + public void preEncode() { } // overridden by alternative ports public CodeGen encode() { assert _phase == Phase.RegAlloc; _phase = Phase.Encoding; long t0 = System.currentTimeMillis(); + _encoding = new Encoding(this); + preEncode(); + _encoding.encode(); + _tEncode = (int)(System.currentTimeMillis() - t0); + return this; + } - - //_tEncode = (int)(System.currentTimeMillis() - t0); - //throw Utils.TODO(); + // --------------------------- + // Exporting to external formats + public CodeGen exportELF(String fname) throws IOException { + assert _phase == Phase.Encoding; + _phase = Phase.Export; + if( fname == null ) new LinkMem(this).link(); // In memory patching + else new ElfFile(this).export(fname); // External ELF file return this; } - // Encoded binary, no relocation info - public byte[] binary() { throw Utils.TODO(); } // --------------------------- + public boolean _asmLittle=true; SB asm(SB sb) { return ASMPrinter.print(sb,this); } public String asm() { return asm(new SB()).toString(); } @@ -335,4 +437,44 @@ public class CodeGen { // Debugging helper public Node f(int idx) { return _stop.find(idx); } + + + String printCFG() { + if( _cfg==null ) return "no CFG"; + SB sb = new SB(); + for( CFGNode cfg : _cfg ) + IRPrinter.printLine(cfg,sb); + return sb.toString(); + } + + public static void print_as_hex(Encoding enc) { + for (byte b : enc._bits.toByteArray()) { + System.out.print(String.format("%02X", b)); + } + System.out.println(); + } + + public void print_as_hex() { + for (byte b : _encoding._bits.toByteArray()) { + System.out.print(String.format("%02X", b)); + } + System.out.println(); + } + + //// Debug purposes for now +// public CodeGen printENCODING() { +// ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); +// +// for(Node bb : CodeGen.CODE._cfg) { +// for(Node n: bb.outs()) { +// if(n instanceof MachNode) { +// ((MachNode) n).encoding(E); +// } +// } +// } +// +// print_as_hex(outputStream); +// // Get the raw bytes from the output stream +// return this; +// } } diff --git a/src/main/java/com/seaofnodes/simple/codegen/ElfExpr.java b/src/main/java/com/seaofnodes/simple/codegen/ElfExpr.java new file mode 100644 index 00000000..9d9b8a1c --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/codegen/ElfExpr.java @@ -0,0 +1,87 @@ +package com.seaofnodes.simple.codegen; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +public class ElfExpr { + public static void main(String[] args) { + try (FileOutputStream fos = new FileOutputStream("hello.o")) { + ByteBuffer buffer = ByteBuffer.allocate(512); + buffer.order(ByteOrder.LITTLE_ENDIAN); + + // === ELF HEADER (64 bytes) === + buffer.put(new byte[]{ + 0x7F, 'E', 'L', 'F', // ELF Magic + 2, 1, 1, 0, // 64-bit, little-endian, version + 0, 0, 0, 0, 0, 0, 0, // ABI padding + 2, 0, // Type: Executable (ET_EXEC) + 0x3E, 0x00, // Machine: x86-64 + 1, 0, 0, 0, // ELF version + 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Entry point + 0, 0, 0, 0, 0, 0, 0, 0, // Program header offset + 64, 0, 0, 0, 0, 0, 0, 0, // Section header offset + 0, 0, 0, 0, // Flags + 64, 0, // ELF header size + 0, 0, // Program header entry size + 0, 0, // Number of program header entries + 40, 0, // Section header entry size + 2, 0, // Number of section headers + 1, 0 // Section header string table index + }); + + // === .TEXT SECTION (Executable Code) === + byte[] textSection = { + // sys_write (write(1, message, 13)) + (byte) 0x48, (byte) 0xC7, (byte) 0xC0, 0x01, 0x00, 0x00, 0x00, // mov rax, 1 + (byte) 0x48, (byte) 0xC7, (byte) 0xC7, 0x01, 0x00, 0x00, 0x00, // mov rdi, 1 + (byte) 0x48, (byte) 0xBE, (byte)0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov rsi, message + (byte) 0x48, (byte) 0xC7, (byte) 0xC2, 0x0D, 0x00, 0x00, 0x00, // mov rdx, 13 + (byte) 0x0F, (byte) 0x05, // syscall + + // sys_exit (exit(0)) + (byte) 0x48, (byte) 0xC7, (byte) 0xC0, 0x3C, 0x00, 0x00, 0x00, // mov rax, 60 + (byte) 0x48, (byte) 0x31, (byte) 0xFF, // xor rdi, rdi + (byte) 0x0F, (byte) 0x05 // syscall + }; + + // Append section data + buffer.position(256); + buffer.put(textSection); + + // === STRING MESSAGE (.data Section) === + byte[] message = "Hello, World!\n".getBytes(); + buffer.position(400); + buffer.put(message); + + // === SECTION HEADERS === + buffer.position(64); + + // Null Section (required as first entry) + buffer.put(new byte[40]); + + // .text Section + buffer.putInt(1); // Offset in section string table (".text") + buffer.putInt(1); // Type: SHT_PROGBITS + buffer.putLong(6); // Flags: Executable | Allocatable + buffer.putLong(0); // Virtual address + buffer.putLong(256); // Offset to section data + buffer.putLong(textSection.length); // Section size + buffer.putInt(0); // Link + buffer.putInt(0); // Info + buffer.putLong(4); // Address alignment + buffer.putLong(0); // No extra entries + + // === SECTION HEADER STRING TABLE === + buffer.position(500); + buffer.put(".text".getBytes()); + buffer.put((byte) 0x00); // Null terminator + + fos.write(buffer.array()); + System.out.println("ELF object file 'hello.o' created."); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/src/main/java/com/seaofnodes/simple/codegen/ElfFile.java b/src/main/java/com/seaofnodes/simple/codegen/ElfFile.java new file mode 100644 index 00000000..746ecfed --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/codegen/ElfFile.java @@ -0,0 +1,381 @@ +package com.seaofnodes.simple.codegen; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.*; +import com.seaofnodes.simple.codegen.Encoding.BAOS; +import java.io.*; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.HashMap; +import java.util.Map; + +public class ElfFile { + + final CodeGen _code; + Ary<Section> _sections = new Ary<>(Section.class); + + ElfFile( CodeGen code ) { _code = code; } + + public static int writeCString(DataSection strtab, String name) { + // place name in the string table + int pos = strtab._contents.size(); + byte[] name_bytes = name.getBytes(); + strtab._contents.write(name_bytes, 0, name_bytes.length); + // the strings are null terminated + strtab._contents.write(0); + return pos; + } + + public static void write4( ByteArrayOutputStream bits, int op ) { + bits.write(op ); + bits.write(op>> 8); + bits.write(op>>16); + bits.write(op>>24); + } + public static void write8( ByteArrayOutputStream bits, long i64 ) { + write4(bits, (int) i64 ); + write4(bits, (int)(i64>>32)); + } + + public static final int SYMBOL_SIZE = 4+1+1+2+8+8; + + public static final int SYM_BIND_LOCAL = 0; + public static final int SYM_BIND_GLOBAL = 1; + public static final int SYM_BIND_WEAK = 2; + + public static final int SYM_TYPE_NOTYPE = 0; + public static final int SYM_TYPE_OBJECT = 1; + public static final int SYM_TYPE_FUNC = 2; + public static final int SYM_TYPE_SECTION = 3; + + public static class Symbol { + String _name; + int _name_pos; + + // index in the symbol table + int _index; + + // which section is it in + int _parent; + + // depends on the symbol type + // TODO + int _value, _size; + + // top 4bits are the "bind", bottom 4 are "type" + int _info; + + // bind 1011 + // 00001111 + // 10110000 + + Symbol(String name, int parent, int bind, int type) { + _name = name; + _parent = parent; + _info = (bind << 4) | (type & 0xF); + } + + void writeHeader(ByteBuffer out) { + out.putInt(_name_pos); // name + out.put((byte) _info); // info + // default visibility + out.put((byte) 0); // other + out.putShort((short) _parent); // shndx + out.putLong(_value); // value + out.putLong(_size); // size + } + } + + public static final int SHF_WRITE = 1; + public static final int SHF_ALLOC = 2; + public static final int SHF_EXECINSTR = 4; + public static final int SHF_STRINGS = 32; + public static final int SHF_INFO_LINK = 64; + + public static final int SECTION_HDR_SIZE = 64; + abstract public static class Section { + String _name; + int _index; + + int _type, _flags; + int _link, _info; + + // where's the name in the string table + int _name_pos; + int _file_offset; + + Section(String name, int type) { + _name = name; + _type = type; + } + + void writeHeader(ByteBuffer out) { + out.putInt(_name_pos); // name + out.putInt(_type); // type + out.putLong(_flags); // flags + out.putLong(0); // addr + out.putLong(_file_offset); // offset + out.putLong(size()); // size + out.putInt(_link); // link + out.putInt(_info); // info + out.putLong(16); // addralign + if (_type == 2) { + out.putLong(SYMBOL_SIZE);// entsize + } else if (_type == 4) { + out.putLong(24); // entsize + } else { + out.putLong(0); // entsize + } + } + + abstract void write(ByteBuffer out); + abstract int size(); + } + + public static class SymbolSection extends Section { + Ary<Symbol> _symbols = new Ary<>(Symbol.class); + Ary<Symbol> _loc = new Ary<>(Symbol.class); + + SymbolSection(String name, int type) { + super(name, type); + } + + @Override + void writeHeader(ByteBuffer out) { + // points to string table section + _link = 1; + + // number of non-local symbols + _info = _loc.len() + 1; + + super.writeHeader(out); + } + + void push(Symbol s) { + if ((s._info >> 4) != SYM_BIND_LOCAL) { + _symbols.push(s); + } else { + _loc.push(s); + } + } + + @Override + void write(ByteBuffer out) { + // Index 0 both designates the first entry in the table and serves as the undefined symbol index + for( int i = 0; i < SYMBOL_SIZE/4; i++ ) { + out.putInt(0); + } + // index is already set + for( Symbol s : _loc ) { + s.writeHeader(out); + } + for( Symbol s : _symbols ) { + s.writeHeader(out); + } + } + + @Override + int size() { + return (1 + _symbols.len() + _loc.len()) * SYMBOL_SIZE; + } + } + + public static class DataSection extends Section { + ByteArrayOutputStream _contents; + + DataSection(String name, int type) { + super(name, type); + _contents = new ByteArrayOutputStream(); + } + + DataSection(String name, int type, ByteArrayOutputStream contents) { + super(name, type); + _contents = contents; + } + + @Override + void write(ByteBuffer out) { + out.put(_contents.toByteArray()); + } + + @Override + int size() { + return _contents.size(); + } + } + + private void pushSection(Section s) { + _sections.push(s); + // ELF is effectively "1-based", due to the NULL section + s._index = _sections._len; + } + + /* creates function and stores where it starts*/ + private final HashMap<TypeFunPtr,Symbol> _funcs = new HashMap<>(); + private void encodeFunctions(SymbolSection symbols, DataSection text) { + for( int i=0; i<_code._cfg._len; i++ ) { + if( !(_code._cfg.at(i) instanceof FunNode fun) ) continue; + // skip until the function ends + while( !(_code._cfg.at(i) instanceof ReturnNode ret) ) + i++; + int end = _code._encoding._opStart[ret._nid] + _code._encoding._opLen[ret._nid]; + + Symbol func = new Symbol(fun._name, text._index, SYM_BIND_GLOBAL, SYM_TYPE_FUNC); + func._value = _code._encoding._opStart[fun._nid]; + func._size = end - func._value; + symbols.push(func); + _funcs.put(fun.sig(), func); + } + } + + public void export(String fname) throws IOException { + DataSection strtab = new DataSection(".strtab", 3 /* SHT_SYMTAB */); + // first byte is reserved for an empty string + strtab._contents.write(0); + pushSection(strtab); + + // place all the symbols + SymbolSection symbols = new SymbolSection(".symtab", 2 /* SHT_STRTAB */); + pushSection(symbols); + + // zero flag by default + // we've already constructed this entire section in the encoding phase + DataSection text = new DataSection(".text", 1 /* SHT_PROGBITS */, _code._encoding._bits); + text._flags = SHF_ALLOC | SHF_WRITE | SHF_EXECINSTR; + pushSection(text); + + // Build and write constant pool + BAOS cpool = new BAOS(); + Encoding enc = _code._encoding; + enc.writeConstantPool(cpool,false); + DataSection rdata = new DataSection(".rodata", 1 /* SHT_PROGBITS */, cpool); + rdata._flags = SHF_ALLOC; + pushSection(rdata); + + // populate function symbols + encodeFunctions(symbols, text); + + int idx = 1; + for( Section s : _sections ) { + Symbol sym = new Symbol(s._name, idx++, SYM_BIND_LOCAL, SYM_TYPE_SECTION); + // we can reuse the same name pos from the actual section + sym._name_pos = s._name_pos; + sym._size = s.size(); + symbols.push(sym); + } + + // calculate local index + int num = 1; + for( Symbol s : symbols._loc ) + s._index = num++; + // extra space for .rela.text + 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 */); + for( Node n : enc._externals.keySet()) { + int nid = n._nid; + String extern = enc._externals.get(n); + + Symbol sym = new Symbol(extern, 0, SYM_BIND_GLOBAL, SYM_TYPE_NOTYPE); + sym._index = start_global++; + symbols.push(sym); + + int offset = enc._opStart[nid] + enc._opLen[nid] - 4; + + // u64 offset + write8(text_rela._contents, offset); + // u64 info + write8(text_rela._contents, ((long)sym._index << 32L) | 4L /* PLT32 */); + // i64 addend + write8(text_rela._contents, -4); + } + + // 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); + glob._value = relo._target; + glob._size = 1 << relo._t.log_size(); + glob._index = bigConIdx++; + symbols.push(glob); + write8(text_rela._contents, relo._opStart+relo._off); + write8(text_rela._contents, ((long)glob._index << 32L) | relo._elf ); + write8(text_rela._contents, -4); + } + + text_rela._flags = SHF_INFO_LINK; + text_rela._link = 2; + text_rela._info = text._index; + pushSection(text_rela); + + 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(); + symbols.push(sym); + + // populate string table + for( Section s : _sections ) { s._name_pos = writeCString(strtab, s._name); } + for( Symbol s : symbols._symbols ) { s._name_pos = writeCString(strtab, s._name); } + for( Symbol s : symbols._loc ) { s._name_pos = writeCString(strtab, s._name); } + + int size = 64; // ELF header + // size of all section data + for( Section s : _sections ) { + s._file_offset = size; + size += s.size(); + } + // headers go at the very end + int shdr_offset = size; + // there's a "null" section at the start of the section headers + size += SECTION_HDR_SIZE*(1 + _sections.len()); + + // System.out.printf("Hello %d, %d, %s\n", size, shdr_offset); + ByteBuffer out = ByteBuffer.allocate(size); + out.order(ByteOrder.LITTLE_ENDIAN); + + // ELF header + out.put(new byte[]{ 0x7F, 'E', 'L', 'F', 2, 1, 1, 0 }); + out.put(new byte[8]); + out.putShort((short)1 /* ET_REL */); // type + out.putShort((short)62 /* AMD64 */); // machine + out.putInt(1); // version + out.putLong(0); // entry + out.putLong(0); // phoff + out.putLong(shdr_offset); // shoff + out.putInt(0); // flags + out.putShort((short)64); // ehsize + out.putShort((short)0); // phentsize + out.putShort((short)0); // phnum + out.putShort((short)64); // shentsize + out.putShort((short) (1 + _sections.len())); // shnum + out.putShort((short)1); // shstrndx + + // raw section data + for( Section s : _sections ) { + s.write(out); + } + + // "null" section + for( int i = 0; i < 16; i++ ) { + out.putInt(0); + } + + for( Section s : _sections ) { + s.writeHeader(out); + } + assert out.position() == size; + + File file = new File(fname); + if( file.getParentFile()!=null ) + file.getParentFile().mkdirs(); + BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file)); + bos.write(out.array()); + bos.close(); + } + +} diff --git a/src/main/java/com/seaofnodes/simple/codegen/Encoding.java b/src/main/java/com/seaofnodes/simple/codegen/Encoding.java new file mode 100644 index 00000000..c35ea6a4 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/codegen/Encoding.java @@ -0,0 +1,473 @@ +package com.seaofnodes.simple.codegen; + +import com.seaofnodes.simple.Ary; +import com.seaofnodes.simple.SB; +import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.print.IRPrinter; +import com.seaofnodes.simple.type.*; +import java.io.ByteArrayOutputStream; +import java.util.*; + + +/** + * Instruction encodings + * <p> + * This class holds the encoding bits, plus the relocation information needed + * to move the code to a non-zero offset, or write to an external file (ELF). + * <p> + * There are also a bunch of generic utilities for managing bits and bytes + * common in all encodings. + */ +public class Encoding { + + // Top-level program graph structure + public final CodeGen _code; + + // Instruction bytes. The registers are encoded already. Relocatable data + // is in a fixed format depending on the kind of relocation. + + // - RIP-relative offsets into the same chunk are just encoded with their + // check relative offsets; no relocation info is required. + + // - Fixed address targets in the same encoding will be correct for a zero + // base; moving the entire encoding requires adding the new base address. + + // - RIP-relative to external chunks have a zero offset; the matching + // relocation info will be used to patch the correct value. + + public static class BAOS extends ByteArrayOutputStream { + public byte[] buf() { return buf; } + void set( byte[] buf0, int count0 ) { buf=buf0; count=count0; } + }; + final public BAOS _bits = new BAOS(); + + public int [] _opStart; // Start of opcodes, by _nid + public byte[] _opLen; // Length of opcodes, by _nid + + // Big Constant relocation info. + public static class Relo { + public final Node _op; + 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 _opStart; // Opcode start + Relo( Node op, Type t, byte off, byte elf ) { + _op=op; _t=t; _off=off; _elf=elf; + } + } + public final HashMap<Node,Relo> _bigCons = new HashMap<>(); + + Encoding( CodeGen code ) { _code = code; } + + // Shortcut to the defining register + public short reg(Node n) { + return _code._regAlloc.regnum(n); + } + + public int read1(int idx) { + byte[] buf = _bits.buf(); + return (buf[idx]&0xFF); + } + public int read2(int idx) { + byte[] buf = _bits.buf(); + return (buf[idx]&0xFF) | (buf[idx+1]&0xFF) <<8; + } + public int read4(int idx) { + byte[] buf = _bits.buf(); + return (buf[idx]&0xFF) | (buf[idx+1]&0xFF) <<8 | (buf[idx+2]&0xFF)<<16 | (buf[idx+3]&0xFF)<<24; + } + public long read8(int idx) { + return (read4(idx) & 0xFFFFFFFFL) | read4(idx+4); + } + + // This buffer is invalid/moving until after all encodings are written + public byte[] bits() { return _bits.buf(); } + + // 4 byte little-endian write + public void patch4( int idx, int val ) { + byte[] buf = _bits.buf(); + buf[idx ] = (byte)(val ); + buf[idx+1] = (byte)(val>> 8); + buf[idx+2] = (byte)(val>>16); + buf[idx+3] = (byte)(val>>24); + } + + static void padN(int n, BAOS bits) { + while( (bits.size()+n-1 & -n) > bits.size() ) + bits.write(0); + } + + // Convenience for writing log-N + static void addN( int log, Type t, BAOS bits ) { + long x = t instanceof TypeInteger ti + ? ti.value() + : log==3 + ? Double.doubleToRawLongBits( ((TypeFloat)t).value()) + : Float.floatToRawIntBits((float)((TypeFloat)t).value()); + addN(log,x,bits); + } + static void addN( int log, long x, BAOS bits ) { + for( int i=0; i < 1<<log; i++ ) { + bits.write((int)x); + x >>= 8; + } + } + + public Encoding add1( int op ) { addN(0,op,_bits); return this; } + public Encoding add2( int op ) { addN(1,op,_bits); return this; } + public Encoding add4( int op ) { addN(2,op,_bits); return this; } + public Encoding add8(long op ) { addN(3,op,_bits); return this; } + + + // Nodes need "relocation" patching; things done after code is placed. + // Record src and dst Nodes. + private final HashMap<Node,CFGNode> _internals = new HashMap<>(); + // Source is a Call, destination in the Fun. + public Encoding relo( CallNode call ) { + _internals.put(call,_code.link(call.tfp())); + return this; + } + public Encoding relo( ConstantNode con ) { + TypeFunPtr tfp = (TypeFunPtr)con._con; + _internals.put(con,_code.link(tfp)); + return this; + } + public void jump( CFGNode jmp, CFGNode dst ) { + _internals.put(jmp,dst.uctrlSkipEmpty()); + } + + + final HashMap<Node,String> _externals = new HashMap<>(); + public Encoding external( Node call, String extern ) { + _externals.put(call,extern); + return this; + } + + // Store t as a 32/64 bit constant in the code space; generate RIP-relative + // addressing to load it + public void largeConstant( Node relo, Type t, int off, int elf ) { + assert t.isConstant(); + assert (byte)off == off; + assert (byte)elf == elf; + _bigCons.put(relo,new Relo(relo,t,(byte)off,(byte)elf)); + } + + // -------------------------------------------------- + void encode() { + // Basic block layout: negate branches to keep blocks in-order; insert + // unconditional jumps. Attempt to keep backwards branches taken, + // forwards not-taken (this is the default prediction on most + // hardware). Layout is still Reverse Post Order but with more + // restrictions. + basicBlockLayout(); + + // Write encoding bits in order into a big byte array. + // Record opcode start and length. + writeEncodings(); + + // Short-form RIP-relative support: replace long encodings with short + // encodings and compact the code, changing all the offsets. + compactShortForm(); + + // Patch RIP-relative and local encodings now. + patchLocalRelocations(); + } + + // -------------------------------------------------- + // Basic block layout: negate branches to keep blocks in-order; insert + // unconditional jumps. Attempt to keep backwards branches taken, forwards + // not-taken (this is the default prediction on most hardware). Layout is + // still Reverse Post Order but with more restrictions. + private void basicBlockLayout() { + IdentityHashMap<LoopNode,Ary<CFGNode>> rpos = new IdentityHashMap<>(); + Ary<CFGNode> rpo = new Ary<>(CFGNode.class); + rpos.put(_code._start.loop(),rpo); + BitSet visit = _code.visit(); + //IdentityHashMap<CFGNode,LoopNode> looptail = new IdentityHashMap<>(); + rpo.add(_code._stop); + for( Node n : _code._start._outputs ) + if( n instanceof FunNode fun ) { + int x = rpo._len; + //_rpo_cfg2(fun, visit, rpo, looptail); + _rpo_cfg(fun, visit, rpos ); + assert rpo.at(x) instanceof ReturnNode; + } + rpo.add(_code._start); + + // Reverse in-place + for( int i=0; i< rpo.size()>>1; i++ ) + rpo.swap(i,rpo.size()-1-i); + visit.clear(); + _code._cfg = rpo; // Save the new ordering + } + + + private void _rpo_cfg(CFGNode bb, BitSet visit, IdentityHashMap<LoopNode,Ary<CFGNode>> rpos ) { + if( bb==null || visit.get(bb._nid) ) return; + visit.set(bb._nid); + CFGNode next = bb.uctrl(); + + // Loops run an inner "rpo_cfg", then append the entire loop body in + // place. This keeps loop bodies completely contained, although if + // Some Day Later we have real profile data we ought to arrange the + // branch orderings based on frequency. + if( bb instanceof LoopNode loop ) { + Ary<CFGNode> body = new Ary<>(CFGNode.class); // Private RPO for the loop + rpos.put(loop.loop(),body); // Find it via loop tree + _rpo_cfg(next,visit,rpos); // RPO the loop + body.add(loop); // Include loop last (first in RPO) + Ary<CFGNode> outer = rpos.get(loop.cfg(1).loop()); + outer.addAll(body); // Append to original CFG + return; + } + + // IfNodes visit 2 sides, and may choose to reorder them in the RPO + if( bb instanceof IfNode iff ) { + // Pick out the T/F projections + CProjNode t = iff.cproj(0); + CProjNode f = iff.cproj(1); + // Invert the branch or not + if( shouldInvert(t,f,iff.loopDepth()) ) { + iff.negate(); + t.invert(); + f.invert(); + CProjNode tmp=f; f=t; t=tmp; // Swap t/f + } + + // Whichever side is visited first becomes last in the RPO. With + // no loops, visit the False side last (so True side first) so that + // when the False RPO visit returns, the IF is immediately next. + // When the RPO is reversed, the fall-through path will always be + // following the IF. + _rpo_cfg(t,visit,rpos); // True side first + next = f; // False side last + } + + // If the *next* BB has already been visited, and we are not already a + // jump, we may need an unconditional forwards jump here + Ary<CFGNode> rpo = rpos.get(bb.loop()); + if( next!=null && visit.get(next._nid) && !(bb instanceof IfNode) ) { + boolean needJump = next instanceof LoopNode + // Empty blocks from an IF will invert the IF and backwards + // branch to the loop head. If not empty, or not an IF + // will need a jump. + ? (bb.nOuts()>1 || !(bb.cfg0() instanceof IfNode) ) + // Forwards jump. If all the blocks, in RPO order, to our + // target are empty, we will fall in and not need a jump. + : !backwardsEmptyScan(rpo,next); + if( needJump ) { + CFGNode jmp = _code._mach.jump(); + jmp._ltree = bb._ltree; + jmp.setDefX(0,bb); + next.setDef(next._inputs.find(bb),jmp); + rpo.add(jmp); + } + } + + _rpo_cfg(next,visit,rpos); + rpo.add(bb); + } + + // Should this test be inverted? + private static boolean shouldInvert(CFGNode t, CFGNode f, int bld) { + int tld = t.loopDepth(), fld = f.loopDepth(); + // These next two are symmetric and can happen in any order; if `tld < + // bld` is true, the `fld < bld` must be false, or else both directions + // exit the loop... and the IF test would not be in the loop. + + // true to exit a loop usually (and false falls into Yet Another Loop + // Block), invert if false is taking an empty backwards branch. + if( tld < bld ) return forwardsEmptyScan(f,bld); + // false to exit a loop, normally invert to true (except empty back) + if( fld < bld ) return !forwardsEmptyScan(t,bld); + + // Not exiting the loop; staying at this depth (or going deeper loop). + + // Jump/true to an empty backwards block + if( forwardsEmptyScan(t,bld) ) return false; + if( forwardsEmptyScan(f,bld) ) return true ; + + // Fall/false into a full block, Jump/true to an empty block. + if( f.nOuts()>1 && t.nOuts()==1 ) return false; + if( t.nOuts()>1 && f.nOuts()==1 ) return true ; + // Everything else equal, use pre-order + return t._pre > f._pre; + } + + // Do we continue forwards from 'c' to the Loop back edge, without doing + // anything else? Then this branch can directly jump to the loop head. + private static boolean forwardsEmptyScan( CFGNode c, int bld ) { + if( c.nOuts()!=1 || c.loopDepth()!=bld ) return false; + return c.uctrl() instanceof RegionNode cfg && + (cfg instanceof LoopNode || forwardsEmptyScan(cfg,bld)); + } + + // Is the CFG from "next" to the end empty? This means jumping to "next" + // will naturally fall into the end. + private static boolean backwardsEmptyScan(Ary<CFGNode> rpo, CFGNode next) { + for( int i=rpo._len-1; rpo.at(i)!=next; i-- ) + if( rpo.at(i).nOuts()!=1 ) + return false; + return true; + } + + // -------------------------------------------------- + // Write encoding bits in order into a big byte array. + // Record opcode start and length. + public FunNode _fun; // Currently encoding function + private void writeEncodings() { + _opStart= new int [_code.UID()]; + _opLen = new byte[_code.UID()]; + for( CFGNode bb : _code._cfg ) { + if( !(bb instanceof MachNode mach0) ) + _opStart[bb._nid] = _bits.size(); + else if( bb instanceof FunNode fun ) { + _fun = fun; // Currently encoding function + _opStart[bb._nid] = _bits.size(); + mach0.encoding( this ); + _opLen[bb._nid] = (byte) (_bits.size() - _opStart[bb._nid]); + } + for( Node n : bb._outputs ) { + if( n instanceof MachNode mach && !(n instanceof FunNode) ) { + _opStart[n._nid] = _bits.size(); + mach.encoding( this ); + _opLen[n._nid] = (byte) (_bits.size() - _opStart[n._nid]); + } + } + } + } + + // -------------------------------------------------- + // Short-form RIP-relative support: replace short encodings with long + // encodings and expand the code, changing all the offsets. + private void compactShortForm() { + int len = _code._cfg._len; + int[] oldStarts = new int[len]; + for( int i=0; i<len; i++ ) + oldStarts[i] = _opStart[_code._cfg.at(i)._nid]; + + // TODO: Rewrite this algo to use the small "_jmps" list of just the + // jumps instead of walking all blocks. + // TODO2: Allow short encodings on non-jumps. This algo doesn't + // really care jumps vs other rip-relative things. + + // Check the size of short jumps, adjust the block starts for a whole + // pass over all blocks. If some jumps go long, they might stretch the + // distance for other jumps, so another pass is needed. + int slide = -1; + while( slide != 0) { // While no fails + slide = 0; + for( int i=0; i<len; i++ ) { + CFGNode bb = _code._cfg.at(i); + // Functions pad to align 16 + if( bb instanceof FunNode ) { + int newStart = _opStart[bb._nid]+slide; + slide += (newStart+15 & -16)-newStart; + } + _opStart[bb._nid] += slide; + // Slide down all other (non-CFG) ops in the block + for( Node n : bb._outputs ) + if( n instanceof MachNode && !(n instanceof CFGNode) ) + _opStart[n._nid] += slide; + if( bb instanceof RIPRelSize riprel ) { + CFGNode target = (bb instanceof IfNode iff ? iff.cproj(0) : (CFGNode)bb.out(0)).uctrlSkipEmpty(); + // Delta is from opStart to opStart. X86 at least counts + // the delta from the opEnd, but we don't have the end until + // we decide the size - so the encSize has to deal + int delta = _opStart[target._nid] - _opStart[bb._nid]; + byte opLen = riprel.encSize(delta); + // Recorded size is smaller than the current size? + if( _opLen[bb._nid] < opLen ) { + // Start sliding the code down; record slide amount and new size + slide += opLen - _opLen[bb._nid]; + _opLen[bb._nid] = opLen; + } + } + } + } + + + // Copy/slide the bits to make space for all the longer branches + int grow = _opStart[_code._cfg.at(len-1)._nid] - oldStarts[len-1]; + if( grow > 0 ) { // If no short-form ops, nothing to do here + int end = _bits.size(); + byte[] bits = new byte[end+grow]; + for( int i=len-1; i>=0; i-- ) { + int start = oldStarts[i]; + if( start==0 && i>1 ) continue; + int oldStart = oldStarts[i]; + int newStart = _opStart[_code._cfg.at(i)._nid]; + System.arraycopy(_bits.buf(),oldStart,bits,newStart,end-start); + end = start; + } + _bits.set(bits,bits.length); + } + } + + + // -------------------------------------------------- + // Patch local encodings now + void patchLocalRelocations() { + // Walk the local code-address relocations + for( Node src : _internals.keySet() ) { + int start = _opStart[src._nid]; + Node dst = _internals.get(src); + int target = _opStart[dst._nid]; + ((RIPRelSize)src).patch(this, start, _opLen[src._nid], target - start); + } + } + + // -------------------------------------------------- + // Write the constant pool into the BAOS and optionally patch locally + void writeConstantPool( BAOS bits, boolean patch ) { + padN(16,bits); + + HashMap<Type,Integer> targets = new HashMap<>(); + + // By log size + for( int log = 3; log >= 0; log-- ) { + // Write the 8-byte constants + for( Node op : _bigCons.keySet() ) { + Relo relo = _bigCons.get(op); + if( relo._t.log_size()==log ) { + // Map from relo to constant start and patch + Integer target = targets.get(relo._t); + if( target==null ) { + targets.put(relo._t,target = bits.size()); + // Put constant into code space. + if( relo._t instanceof TypeTuple tt ) // Constant tuples put all entries + for( Type tx : tt._types ) + addN(log,tx,bits); + else + addN(log,relo._t,bits); + } + relo._target = target; + relo._opStart= _opStart[op._nid]; + // Go ahead and locally patch in-memory + if( patch ) + ((RIPRelSize)op).patch(this, relo._opStart, _opLen[op._nid], relo._target - relo._opStart); + } + } + } + } + + + // A series of libc/external calls that Simple can link against in a JIT. + // Since no runtime in the JVM process, using magic numbers for the CPU + // emulators to pick up on. + public static int SENTINAL_CALLOC = -4; + + void patchGlobalRelocations() { + for( Node src : _externals.keySet() ) { + int start = _opStart[src._nid]; + String dst = _externals.get(src); + int target = switch( dst ) { + case "calloc" -> SENTINAL_CALLOC; + default -> throw Utils.TODO(); + }; + ((RIPRelSize)src).patch(this, start, _opLen[src._nid], target - start); + } + } +} diff --git a/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java b/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java index 11c2b061..40f827ec 100644 --- a/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java +++ b/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java @@ -24,7 +24,7 @@ public abstract class GlobalCodeMotion { schedEarly(code); // Break up shared global constants by functions - breakUpGlobalConstants(code); + breakUpGlobalConstants(code._start); code._visit.clear(); schedLate (code); @@ -45,23 +45,28 @@ public abstract class GlobalCodeMotion { } // Break up shared global constants by functions - private static void breakUpGlobalConstants( CodeGen code ) { + private static void breakUpGlobalConstants( Node start ) { // For all global constants - for( int i=0; i< code._start.nOuts(); i++ ) { - Node con = code._start.out(i); + for( int i=0; i< start.nOuts(); i++ ) { + Node con = start.out(i); if( con instanceof MachNode mach && mach.isClone() ) { + breakUpGlobalConstants(con); // While constant has users in different functions while( true ) { // Find a function user, and another function FunNode fun = null; boolean done=true; for( Node use : con.outs() ) { - FunNode fun2 = use.cfg0().fun(); + FunNode fun2 = use instanceof ReturnNode ret ? ret.fun() : use.cfg0().fun(); if( fun==null || fun==fun2 ) fun=fun2; else { done=false; break; } } // Single function user, so this constant is not shared - if( done ) { i--; con.setDef(0,fun); break; } + if( done ) { + if( con.in(0)==start ) i--; + con.setDef(0,fun); + break; + } // Move function users to a private constant Node con2 = mach.copy(); // Private constant clone con2._inputs.set(0,null); // Preserve edge invariants from clone @@ -155,13 +160,15 @@ public abstract class GlobalCodeMotion { continue outer; // Nope, await all uses done // Loads need their memory inputs' uses also done - if( n instanceof LoadNode ld ) + if( n instanceof MemOpNode ld && ld._isLoad ) for( Node memuse : ld.mem()._outputs ) if( late[memuse._nid]==null && // New makes new memory, never crushes load memory !(memuse instanceof NewNode) && // Load-use directly defines memory (memuse._type instanceof TypeMem || + // Load-use directly defines memory + memuse instanceof CallNode || // Load-use indirectly defines memory (memuse._type instanceof TypeTuple tt && tt._types[ld._alias] instanceof TypeMem)) ) continue outer; @@ -175,8 +182,8 @@ public abstract class GlobalCodeMotion { if( def!=null && late[def._nid]==null ) { work.push(def); // if the def has a load use, maybe the load can fire - for( Node ld : def._outputs ) - if( ld instanceof LoadNode && late[ld._nid]==null ) + for( Node out : def._outputs ) + if( out instanceof MemOpNode ld && ld._isLoad && late[ld._nid]==null ) work.push(ld); } } @@ -192,9 +199,36 @@ public abstract class GlobalCodeMotion { lca = use_block(n,use, late)._idom(lca,null); // Loads may need anti-dependencies, raising their LCA - if( n instanceof LoadNode load ) + if( n instanceof MemOpNode load && load._isLoad ) lca = find_anti_dep(lca,load,early,late); + + // Nodes setting a single register and getting killed will stay close + // to the uses, since they will be forced to spill anyway. The kill + // check is very weak, and some may be hoisted only to spill in the RA. + if( n instanceof MachNode mach ) { + RegMask out = mach.outregmap(); + if( out!=null && out.size1() ) { + int reg = mach.outregmap().firstReg(); + // Look for kills + outer: + for( CFGNode lca2=lca; lca2 != early; lca2 = lca2.idom() ) { + if( lca2 instanceof MachNode mach2 ) { + for( int i=1; i<lca2.nIns(); i++ ) { + RegMask mask = mach2.regmap(i); + if( mask!=null && mask.test(reg) ) { + early = lca2 instanceof IfNode ? lca2.idom() : lca2; + break outer; + } + } + RegMask kill = mach2.killmap(); + if( kill != null ) + throw Utils.TODO(); + } + } + } + } + // Walk up from the LCA to the early, looking for best place. This is // the lowest execution frequency, approximated by least loop depth and // deepest control flow. @@ -226,10 +260,12 @@ public abstract class GlobalCodeMotion { // Least loop depth first, then largest idepth private static boolean better( CFGNode lca, CFGNode best ) { return lca.loopDepth() < best.loopDepth() || - (lca.idepth() > best.idepth() || best instanceof IfNode); + lca instanceof NeverNode || + lca.idepth() > best.idepth() || + best instanceof IfNode; } - private static CFGNode find_anti_dep(CFGNode lca, LoadNode load, CFGNode early, CFGNode[] late) { + private static CFGNode find_anti_dep(CFGNode lca, MemOpNode load, CFGNode early, CFGNode[] late) { // We could skip final-field loads here. // Walk LCA->early, flagging Load's block location choices for( CFGNode cfg=lca; early!=null && cfg!=early.idom(); cfg = cfg.idom() ) @@ -237,13 +273,15 @@ public abstract class GlobalCodeMotion { // Walk load->mem uses, looking for Stores causing an anti-dep for( Node mem : load.mem()._outputs ) { switch( mem ) { - case StoreNode st: - assert late[st._nid]!=null; - lca = anti_dep(load,late[st._nid],st.cfg0(),lca,st); - break; - case CallNode st: - assert late[st._nid]!=null; - lca = anti_dep(load,late[st._nid],st.cfg0(),lca,st); + case MemOpNode st: + if( !st._isLoad ) { + assert late[mem._nid] != null; + lca = anti_dep( load, late[mem._nid], mem.cfg0(), lca, st ); + } + break; // Loads do not cause anti-deps on other loads + case CallNode call: + assert late[call._nid]!=null; + lca = anti_dep(load,late[call._nid],call.cfg0(),lca,call); break; case PhiNode phi: // Repeat anti-dep for matching Phi inputs. @@ -253,7 +291,6 @@ public abstract class GlobalCodeMotion { lca = anti_dep(load,phi.region().cfg(i),load.mem().cfg0(),lca,null); break; case NewNode st: break; - case LoadNode ld: break; // Loads do not cause anti-deps on other loads case ReturnNode ret: break; // Load must already be ahead of Return case MemMergeNode ret: break; // Mem uses now on ScopeMin case NeverNode never: break; @@ -264,16 +301,13 @@ public abstract class GlobalCodeMotion { } // - private static CFGNode anti_dep( LoadNode load, CFGNode stblk, CFGNode defblk, CFGNode lca, Node st ) { - // Walk store blocks "reach" from its scheduled location to its earliest - for( ; stblk != defblk.idom(); stblk = stblk.idom() ) { - // Store and Load overlap, need anti-dependence - if( stblk._anti==load._nid ) { - lca = stblk._idom(lca,null); // Raise Loads LCA - if( lca == stblk && st != null && st._inputs.find(load) == -1 ) // And if something moved, - st.addDef(load); // Add anti-dep as well - return lca; // Cap this stores' anti-dep to here - } + private static CFGNode anti_dep( MemOpNode load, CFGNode stblk, CFGNode defblk, CFGNode lca, Node st ) { + // Store and Load overlap, need anti-dependence + if( stblk._anti==load._nid ) { + lca = stblk._idom(lca,null); // Raise Loads LCA + if( lca == stblk && st != null && st._inputs.find(load) == -1 ) // And if something moved, + st.addDef(load); // Add anti-dep as well + return lca; // Cap this stores' anti-dep to here } return lca; } diff --git a/src/main/java/com/seaofnodes/simple/codegen/IFG.java b/src/main/java/com/seaofnodes/simple/codegen/IFG.java index 1f3186ff..fdd4e2f9 100644 --- a/src/main/java/com/seaofnodes/simple/codegen/IFG.java +++ b/src/main/java/com/seaofnodes/simple/codegen/IFG.java @@ -19,7 +19,7 @@ abstract public class IFG { static final IdentityHashMap<LRG,Node> TMP = new IdentityHashMap<>(); // Inteference Graph: Array of Bitsets - private static final Ary<BitSet> IFG = new Ary<>(BitSet.class); + static final Ary<BitSet> IFG = new Ary<>(BitSet.class); static void resetIFG() { for( BitSet bs : IFG ) if( bs!=null ) bs.clear(); @@ -79,6 +79,8 @@ abstract public class IFG { while( !WORK.isEmpty() ) do_block(round,alloc,WORK.pop()); + if( alloc.success() ) + convert2DAdjacency(alloc); return alloc.success(); } @@ -125,18 +127,18 @@ abstract public class IFG { TMP.remove(lrg); // Kill def } + // Phis use and define the same live range, i.e. these LRGs already + // marked conflicted, no need to mark again if( n instanceof PhiNode ) return; - // A copy does not define a new value, and the src and dst can use the - // same register. Remove the input from TMP/liveout set before - // interfering. - //if( n instanceof MachNode m && m.isSplit() ) - // TMP.remove(alloc.lrg(n.in(1))); // Kill spill-use + + // Kill any killed registers; milli-code routines like New can kill + // will not being a CFG. + if( n instanceof MachNode m ) + kills(alloc,m); // Interfere n with all live if( lrg!=null ) { - if( n instanceof MachNode m ) - kills(alloc,m); // Interfere n with all live for( LRG tlrg : TMP.keySet() ) { assert tlrg.leader(); @@ -148,9 +150,10 @@ abstract public class IFG { // last tlrg register at some point, either tlrg or lrg // must fail. If *n* (a subset of lrg) needs the single // last tlrg register then only tlrg must fail. - if( lrg.size1() && !tlrg.clr(lrg._mask.firstReg()) ) - alloc.fail(tlrg); - else addIFG(lrg,tlrg); // Add interference + if( ((MachNode)n).outregmap().size1() ) { + if( !tlrg.clr(lrg._mask.firstReg()) ) // Clear bit, no interference + alloc.fail(tlrg); // Clearing drives mask to empty + } else addIFG(lrg,tlrg); // Add interference } } @@ -171,7 +174,7 @@ abstract public class IFG { // Look for a must-use single register conflicting with some other must-def. if( n instanceof MachNode m ) { RegMask ni_mask = m.regmap(i); - if( ni_mask.size1() ) { // Must-use single register + if( ni_mask!=null && ni_mask.size1() ) { // Must-use single register // Search all current live for( LRG tlrg : TMP.keySet() ) { assert tlrg.leader(); @@ -207,12 +210,17 @@ abstract public class IFG { // Disallow clone-ables from killing registers. Just fail // them and re-clone closer to target... so no kill. // Special case for Intel XOR used to zero. - if( m.isClone() ) + Node n = (Node)m; + CFGNode effUseBlk = n.out(0) instanceof PhiNode phi ? phi.region().cfg(phi._inputs.find(n)) : n.out(0).cfg0(); + if( m.isClone() && // Must be clonable + (n.nOuts()>1 || // Has many users OR + // Only 1 user but effective use is remote block + effUseBlk != n.cfg0() )) + // Then fail the clonable; it should split or move alloc.fail(alloc.lrg((Node)m)); - else if( !tlrg.sub(killMask) ) { - tlrg._killed = true; // Failed by a kill-mask + // Else clonable cannot move + else if( !tlrg.sub(killMask) ) alloc.fail(tlrg); - } } } } @@ -234,14 +242,12 @@ abstract public class IFG { private static void mergeLiveOut( RegAlloc alloc, CFGNode priorbb, int i ) { CFGNode bb = priorbb.cfg(i); if( bb == null ) return; // Start has no prior - if( !bb.blockHead() ) bb = bb.cfg0(); - //if( i==0 && !(bb instanceof StartNode) ) bb = bb.cfg0(); - assert bb.blockHead(); + while( !bb.blockHead() ) bb = bb.cfg0(); // Lazy get live-out set for bb - IdentityHashMap<LRG, Node> lrgs = BBOUTS.computeIfAbsent( bb, k -> new IdentityHashMap<>() ); + IdentityHashMap<LRG, Node> lrgs = BBOUTS.computeIfAbsent( bb, k -> new IdentityHashMap<>() ); - for( LRG lrg : TMP.keySet() ) { + for( LRG lrg : TMP.keySet() ) { Node def = TMP.get(lrg); // Effective def comes from phi input from prior block if( def instanceof PhiNode phi && phi.cfg0()==priorbb ) { @@ -260,6 +266,24 @@ abstract public class IFG { } + static void convert2DAdjacency( RegAlloc alloc ) { + // Convert the 2-D array of bits (a 1-D array of BitSets) into an + // adjacency matrix. + int maxlrg = alloc._LRGS.length; + for( int i=1; i<maxlrg; i++ ) { + BitSet ifg = IFG.atX(i); + if( ifg != null ) { + LRG lrg0 = alloc._LRGS[i]; + for( int lrg = ifg.nextSetBit(0); lrg>=0; lrg=ifg.nextSetBit(lrg+1) ) { + LRG lrg1 = alloc._LRGS[lrg]; + lrg0.addNeighbor(lrg1); + lrg1.addNeighbor(lrg0); + } + } + } + } + + // ------------------------------------------------------------------------ // Color the inference graph. @@ -276,22 +300,10 @@ abstract public class IFG { // If there's no spare color we'll have to spill this at-risk live range. public static boolean color(int round, RegAlloc alloc) { - - // Convert the 2-D array of bits (a 1-D array of BitSets) into an - // adjacency matrix. int maxlrg = alloc._LRGS.length, nlrgs=0; - for( int i=1; i<maxlrg; i++ ) { - if( alloc._LRGS[i] != null ) nlrgs++; - BitSet ifg = IFG.atX(i); - if( ifg != null ) { - LRG lrg0 = alloc._LRGS[i]; - for( int lrg = ifg.nextSetBit(0); lrg>=0; lrg=ifg.nextSetBit(lrg+1) ) { - LRG lrg1 = alloc._LRGS[lrg]; - lrg0.addNeighbor(lrg1); - lrg1.addNeighbor(lrg0); - } - } - } + for( int i=1; i<maxlrg; i++ ) + if( alloc._LRGS[i] != null ) + nlrgs++; // Simplify @@ -407,7 +419,7 @@ abstract public class IFG { int best=sptr; int bestScore = pickRiskyScore(color_stack[best]); for( int i=sptr+1; i<color_stack.length; i++ ) { - if( bestScore == 999999 ) return best; // Already max score + if( bestScore == 1000000 ) return best; // Already max score int iScore = pickRiskyScore(color_stack[i]); if( iScore > bestScore ) { best = i; bestScore = iScore; } @@ -424,13 +436,25 @@ abstract public class IFG { // Picking a live range that is very close to coloring might allow it to // color despite being risky. private static int pickRiskyScore( LRG lrg ) { + // Pick single-def clonables that are not right next to their single-use. + // Failing to color these will clone them closer to their uses. + if( !lrg._multiDef && lrg._machDef.isClone() ) { + Node def = ((Node)lrg._machDef); + Node use = ((Node)lrg._machUse); + CFGNode cfg = def.cfg0(); + if( cfg != use.cfg0() || // Different blocks OR + // Same block, but not close + cfg._outputs.find(def) < cfg._outputs.find(use)+1 ) + return 1000000; + } + // Always pick callee-save registers as being very large area recovered // and very cheap to spill. if( lrg._machDef instanceof CalleeSaveNode ) - return 999998; - if( lrg._splitDef != null && lrg._splitDef.in(1) instanceof CalleeSaveNode && + return 1000000-2-lrg._mask.firstReg(); + if( lrg._splitDef != null && lrg._splitDef. in(1) instanceof CalleeSaveNode && lrg._splitUse != null && lrg._splitUse.out(0) instanceof ReturnNode ) - return 999999; + return 1000000-1; // TODO: cost/benefit model. Perhaps counting loop-depth (freq) of def/use for cost // and "area" for benefit @@ -441,35 +465,39 @@ abstract public class IFG { if( mask.size1() ) return reg; // Check chain of splits up the def-chain. Take first allocated // register, and if it's available in the mask, take it. - Node defSplit = lrg._splitDef, useSplit = lrg._splitUse; - int tidx, cnt=0; + Node def = lrg._splitDef, use = lrg._splitUse; + int tidx=0, cnt=0; - while( (tidx=biasable(defSplit)) != 0 || biasable(useSplit) != 0 ) { + while( def != null || use != null ) { if( cnt++ > 10 ) break; - if( tidx != 0 ) { - short bias = biasColor( alloc, defSplit, mask ); + if( def != null ) { + short bias = biasColor( alloc, def, mask ); if( bias >= 0 ) return bias; // Good bias - if( bias == -2 ) defSplit = null; // Kill this side, no more searching - } else defSplit = null; + if( bias == -2 ) def = null; // Kill this side, no more searching + else if( (tidx=biasable(def)) == 0 ) def = null; + } - if( biasable(useSplit) != 0 ) { - short bias = biasColor( alloc, useSplit, mask ); + if( use != null ) { + short bias = biasColor( alloc, use, mask ); if( bias >= 0 ) return bias; // Good bias - if( bias == -2 ) useSplit = null; // Kill this side, no more searching - } else useSplit = null; + if( bias == -2 ) use = null; // Kill this side, no more searching + else if( biasable(use)==0 ) use = null; + } - if( defSplit != null ) { - short bias = biasColorNeighbors( alloc, defSplit, mask ); + if( def != null ) { + short bias = biasColorNeighbors( alloc, def, mask ); if( bias >= 0 ) return bias; // Advance def side - defSplit = defSplit.in(tidx); + def = def.in(tidx); + if( alloc.lrg(def)==null ) def=null; } - if( useSplit != null ) { - short bias = biasColorNeighbors( alloc, useSplit, mask ); + if( use != null ) { + short bias = biasColorNeighbors( alloc, use, mask ); if( bias >= 0 ) return bias; - useSplit = useSplit.out(0); + use = use.out(0); + if( biasable(use)==0 ) use=null; } } @@ -478,7 +506,7 @@ abstract public class IFG { private static int biasable(Node split) { if( split instanceof SplitNode ) return 1; // Yes biasable, advance is slot 1 - if( split instanceof PhiNode ) return 1; // Yes biasable, advance is slot 1 + if( split instanceof PhiNode phi ) return phi.region() instanceof LoopNode ? 2 : 1; // Yes biasable, advance is slot 1 if( !(split instanceof MachNode mach) ) return 0; // Not biasable return mach.twoAddress(); // Only biasable if 2-addr } @@ -503,7 +531,7 @@ abstract public class IFG { // Can I limit my own choices to valid neighbor choices? for( LRG alrg : slrg._adj ) { - int reg = alrg._reg; + short reg = alrg._reg; if( reg == -1 && alrg._mask.size1() ) reg = alrg._mask.firstReg(); if( reg != -1 ) { diff --git a/src/main/java/com/seaofnodes/simple/codegen/LRG.java b/src/main/java/com/seaofnodes/simple/codegen/LRG.java index 394c4a01..ec254a2e 100644 --- a/src/main/java/com/seaofnodes/simple/codegen/LRG.java +++ b/src/main/java/com/seaofnodes/simple/codegen/LRG.java @@ -2,6 +2,7 @@ package com.seaofnodes.simple.codegen; import com.seaofnodes.simple.Ary; import com.seaofnodes.simple.SB; +import com.seaofnodes.simple.Utils; import com.seaofnodes.simple.node.*; import java.util.IdentityHashMap; @@ -37,16 +38,13 @@ public class LRG { // Count of single-register defs and uses short _1regDefCnt, _1regUseCnt; + // Count of all defs, uses. Mostly interested 1 vs many + boolean _multiDef, _multiUse; // A sample MachNode def in the live range public MachNode _machDef, _machUse; short _uidx; // _machUse input - // Mask set to empty via a kill-mask. This is usually a capacity kill, - // which means we need to split and spill into memory - which means even if - // the def-side has many registers, it MUST spill. - boolean _killed; - // Some splits used in biased coloring MachConcreteNode _splitDef, _splitUse; @@ -59,6 +57,8 @@ public class LRG { // Adjacent Live Range neighbors. Only valid during coloring Ary<LRG> _adj; + public int nadj() { return _adj==null ? 0 : _adj._len; } + void addNeighbor(LRG lrg) { if( _adj==null ) _adj = new Ary<>(LRG.class); _adj.push(lrg); @@ -77,7 +77,7 @@ public class LRG { } // More registers than neighbors - boolean lowDegree() { return (_adj==null ? 0 : _adj._len) < _mask.size(); } + boolean lowDegree() { return nadj() < _mask.size(); } LRG( short lrg ) { _lrg = lrg; _reg = -1; } @@ -121,29 +121,39 @@ public class LRG { if( _machDef==null ) { _machDef = lrg._machDef; } else if( lrg._machDef!=null ) { + if( _machDef != lrg._machDef ) _multiDef=true; if( _1regDefCnt==0 ) _machDef = lrg._machDef; else if( _machDef==lrg._machDef ) _1regDefCnt--; } _1regDefCnt += lrg._1regDefCnt; + _multiDef |= lrg._multiDef; if( _machUse==null ) { _machUse = lrg._machUse; + _uidx = lrg._uidx; } else if( lrg._machUse!=null ) { - if( _1regUseCnt==0 ) + if( _machUse != lrg._machUse ) _multiUse=true; + if( _1regUseCnt==0 ) { _machUse = lrg._machUse; + _uidx = lrg._uidx; + } else if( _machUse==lrg._machUse ) _1regUseCnt--; } _1regUseCnt += lrg._1regUseCnt; + _multiUse |= lrg._multiUse; // Fold deepest Split _splitDef = deepSplit(_splitDef,lrg._splitDef); _splitUse = deepSplit(_splitUse,lrg._splitUse); // Fold together masks - _mask = _mask.and(lrg._mask); + RegMask mask = _mask.and(lrg._mask); + if( mask==null ) + mask = _mask.copy().and(lrg._mask); + _mask = mask; return this; } @@ -153,6 +163,8 @@ public class LRG { // Record any Mach def for spilling heuristics LRG machDef( MachNode def, boolean size1 ) { + if( _machDef!=null && _machDef!=def ) + _multiDef = true; if( _machDef==null || size1 ) _machDef = def; if( size1 ) @@ -164,6 +176,8 @@ public class LRG { // Record any Mach use for spilling heuristics LRG machUse( MachNode use, short uidx, boolean size1 ) { + if( _machUse!=null && _machUse!=use ) + _multiUse = true; if( _machUse==null || size1 ) { _machUse = use; _uidx = uidx; } if( size1 ) diff --git a/src/main/java/com/seaofnodes/simple/codegen/LinkMem.java b/src/main/java/com/seaofnodes/simple/codegen/LinkMem.java new file mode 100644 index 00000000..e1e849e6 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/codegen/LinkMem.java @@ -0,0 +1,23 @@ +package com.seaofnodes.simple.codegen; + +import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.node.Node; + +// In-memory linking +public class LinkMem { + final CodeGen _code; + LinkMem( CodeGen code ) { _code = code; } + + public CodeGen link() { + Encoding enc = _code._encoding; + + // Patch external calls internally + enc.patchGlobalRelocations(); + + // Write any large constants into a constant pool; they + // are accessed by RIP-relative addressing. + enc.writeConstantPool(enc._bits,true); + + return _code; + } +} diff --git a/src/main/java/com/seaofnodes/simple/codegen/ListScheduler.java b/src/main/java/com/seaofnodes/simple/codegen/ListScheduler.java index 1796ef7c..de4fadc4 100644 --- a/src/main/java/com/seaofnodes/simple/codegen/ListScheduler.java +++ b/src/main/java/com/seaofnodes/simple/codegen/ListScheduler.java @@ -48,6 +48,9 @@ public abstract class ListScheduler { computeSingleRDef(bb,use); else computeSingleRDef(bb,n); + + if( n instanceof NewNode || n instanceof CallNode ) + bb.fun()._hasCalls = true; return this; } diff --git a/src/main/java/com/seaofnodes/simple/codegen/Machine.java b/src/main/java/com/seaofnodes/simple/codegen/Machine.java index 13d7abc1..2766414f 100644 --- a/src/main/java/com/seaofnodes/simple/codegen/Machine.java +++ b/src/main/java/com/seaofnodes/simple/codegen/Machine.java @@ -1,22 +1,97 @@ package com.seaofnodes.simple.codegen; import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.TypeFunPtr; abstract public class Machine { // Human readable machine name. Something like "x86-64" or "arm" or "risc5" public abstract String name(); + // Default opcode size for printing; 4 bytes for most 32-bit risc chips + public int defaultOpSize() { return 4; } // Human-readable name for a register number, e.g. "RAX" or "R0" - public abstract String reg( int reg ); + public abstract String[] regs(); // Create a split op; any register to any register, including stack slots public abstract SplitNode split( String kind, byte round, LRG lrg); - // List of caller-save registers - public abstract RegMask callerSave(); - // List of callee-save registers - public abstract RegMask calleeSave(); + // List of caller-save registers, as a 64bit mask + public abstract long callerSave(); + // List of never-save registers, e.g. RSP or a ZERO register if you have one + public abstract long neverSave(); + // Call Argument Mask. Passed in the function signature and argument + // number (2-based; 0 is for control and 1 for memory). Also passed in a 0 + // for the function itself, or for *outgoing* calls, the maximum stack slot + // given to the incoming function arguments (stack slots reserved for + // incoming arguments). + public abstract RegMask callArgMask(TypeFunPtr tfp, int arg, int maxArgSlot); + // Return register mask, based on signature (GPR vs FPR) + public abstract RegMask retMask(TypeFunPtr tfp); + // Return PC register + public abstract int rpc(); // Return a MachNode unconditional branch public abstract CFGNode jump(); - // Break an infinite loop - public abstract IfNode never( CFGNode ctrl ); // Instruction select from ideal nodes public abstract Node instSelect( Node n ); + + /** Stack Slot Numbering + +RA gives all registers a number, starting at 0. Stack slots continue this +numbering going up from the last register number. Common register numbers are +0-15 for GPRs, 16-31 for FPRs, 32 for flags, and stack slots starting at +register#33 going up. These numbers are machine-specific, YMMV, etc; e.g. RPC +is only in stack slot 0 on X86; other cpus start with the rpc in a register +which may spill into any generic spill slot. + +For ease of reading and printing, stack slots start again at slot#0 - but +during RA they are actually biased by CPU.MAX_REG. + +Stack Layout during Reg Alloc: + + Slot Value Description + ---- ------- ----------- +MAXREG+ R+1 argR maxArgSlot(calleR tfp), can be 0 + ... +MAXREG+ 1 arg0 + ---- -- Caller ------ +MAXREG+ 0 RPC +MAXREG+ R+E+N PAD optional alignment + +MAXREG+ + Spills Caller Frame Space; N spills +MAXREG+ R+E+1 Spills + +MAXREG+ R+E argE maxArgSlot(calleE tfp), can be 0 + ... +MAXREG+ R+2 arg0 + ---- -- Callee ------- + ... + +Slots defined by maxArgSlot do not have to contain the actual passed argument; +they can be "shadow slots". If they exist, they are property of the frame / +FunNode where the TFP comes from. + +For a given calleR, there are generally many calleE's and they all share the +low-range stack slots. + +Example:, Win64, main(int arg) { calloc(1,size); } + + Post-Warp: SP+56 shadow-main-arg +Pre-alloc Post-Alloc SP+48 RPC +--- ------- ---------- ------------- +40 spill#3 SP+40 SP+40 spill#3 +39 spill#2 SP+32 "" +38 spill#1 SP+24 "" +37 spill#0 SP+16 "" spill#0 +36 shadow-calloc#size SP+ 8 SP+ 8 shadow-calloc#size +35 shadow-calloc#1 SP+ 0 SP+ 0 shadow-calloc#1 +--- --- FRAME BASE ---------- ---- FRAME BASE ------- +34 shadow-main-arg SP+56 +33 RPC SP+48 + + + */ + + + // Maximum stack slot (or 0) for the args in this TFP. This will include + // shadow slots if defined in the ABI, even if all arguments are passed in + // registers. + public abstract short maxArgSlot(TypeFunPtr tfp); + } diff --git a/src/main/java/com/seaofnodes/simple/codegen/RIPRelSize.java b/src/main/java/com/seaofnodes/simple/codegen/RIPRelSize.java new file mode 100644 index 00000000..777182dc --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/codegen/RIPRelSize.java @@ -0,0 +1,14 @@ +package com.seaofnodes.simple.codegen; + +// This Node rip-relative encodings has sizes that vary by delta +public interface RIPRelSize { + // delta is the distance from the opcode *start* to the target start. Each + // hardware target adjusts as needed, X86 measures from the opcode *end* + // and small jumps are 2 bytes, so they'll need to subtract 2. + byte encSize(int delta); + + // Patch full encoding in. Used for both short and long forms. delta + // again is from the opcode *start*. + void patch( Encoding enc, int opStart, int opLen, int delta ); + +} diff --git a/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java b/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java index badec740..bd4cbf35 100644 --- a/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java +++ b/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java @@ -4,6 +4,7 @@ import com.seaofnodes.simple.Ary; import com.seaofnodes.simple.Utils; import com.seaofnodes.simple.node.*; import java.util.Arrays; +import java.util.BitSet; import java.util.IdentityHashMap; /** @@ -129,11 +130,18 @@ public class RegAlloc { } // Printable register number for node n - String reg( Node n ) { + String reg( Node n ) { return reg(n,null); } + String reg( Node n, FunNode fun ) { LRG lrg = lrg(n); if( lrg==null ) return null; + // No register yet, use LRG if( lrg._reg == -1 ) return "V"+lrg._lrg; - return _code._mach.reg(lrg._reg); + // Chosen machine register unless stack-slot and past RA + String[] regs = _code._mach.regs(); + if( lrg._reg < regs.length || _code._phase.ordinal() <= CodeGen.Phase.RegAlloc.ordinal() || fun==null ) + return RegMask.reg(regs,lrg._reg); + // Stack-slot past RA uses the frame layout logic + return "[rsp+"+fun.computeStackOffset(_code,lrg._reg)+"]"; } // ----------------------- @@ -141,15 +149,31 @@ public class RegAlloc { public void regAlloc() { // Insert callee-save registers + String[] regs = _code._mach.regs(); + long neverSave= _code._mach.neverSave(); for( CFGNode bb : _code._cfg ) - if( bb instanceof FunNode fun ) - insertCalleeSave(fun); + if( bb instanceof FunNode fun ) { + ReturnNode ret = fun.ret(); + int len = Math.min(regs.length,64); + for( int reg=0; reg<len; reg++ ) + if( !_code._callerSave.test(reg) && ((1L<<reg)&neverSave)==0 ) { + ret.addDef(new CalleeSaveNode(fun,reg,regs[reg])); + assert ret.regmap(ret.nIns()-1).firstReg()==reg; + } + } + // Cache reg masks for New and Call + for( CFGNode bb : _code._cfg ) { + if( bb instanceof CallEndNode cend ) cend.cacheRegs(_code); + for( Node n : bb._outputs ) + if( n instanceof NewNode nnn ) nnn.cacheRegs(_code); + } // Top driver: repeated rounds of coloring and splitting. byte round=0; while( !graphColor(round) ) { split(round); - assert round < 7; // Really expect to be done soon + if( round >= 7 ) // Really expect to be done soon + throw Utils.TODO("Allocator taking too long"); round++; } postColor(); // Remove no-op spills @@ -166,24 +190,12 @@ public class RegAlloc { BuildLRG.run(round,this) && // if no hard register conflicts // Build Interference Graph IFG.build(round,this) && // If no self conflicts or uncolorable + // Conservative coalesce copies + Coalesce.coalesce(round,this) && // Color attempt IFG.color(round,this); // If colorable } - // Insert callee-save registers. Walk the callee-save RegMask ignoring any - // Parms, then insert a Parm and an edge from the Ret to the Parm with the - // callee-save register. - private void insertCalleeSave( FunNode fun ) { - RegMask saves = _code._mach.calleeSave(); - ReturnNode ret = fun.ret(); - - for( short reg = saves.firstReg(); reg != -1; reg = saves.nextReg(reg) ) { - ret.addDef(new CalleeSaveNode(fun,reg,_code._mach.reg(reg))); - assert ((MachNode)ret).regmap(ret.nIns()-1).firstReg()==reg; - } - } - - // ----------------------- // Split conflicted live ranges. void split(byte round) { @@ -193,11 +205,15 @@ public class RegAlloc { // independently... which generally requires a full pass over the // program for each failing live range. i.e., might be a lot of // passes. - for( LRG lrg : _failed.keySet() ) + + // Sort, to avoid non-deterministic HashMap ordering + LRG[] splits = _failed.keySet().toArray(new LRG[0]); + Arrays.sort(splits, (x,y) -> x._lrg - y._lrg ); + for( LRG lrg : splits ) split(round,lrg); } - // Split this live range + // Split this live range, top level heuristic boolean split( byte round, LRG lrg ) { assert lrg.leader(); // Already rolled up @@ -206,13 +222,16 @@ public class RegAlloc { // Register mask when empty; split around defs and uses with limited // register masks. - if( lrg._mask.isEmpty() ) { + if( lrg._mask.isEmpty() && (!lrg._multiDef || lrg._1regUseCnt==1) ) { if( lrg._1regDefCnt <= 1 && lrg._1regUseCnt <= 1 && (lrg._1regDefCnt + lrg._1regUseCnt) > 0 ) return splitEmptyMaskSimple(round,lrg); - // Default to splitByLoop - //return splitEmptyMask(round,lrg); + // Repeated single-reg uses from a single def. Special for archs + // with more fixed regs. + if( !lrg._multiDef && lrg._1regDefCnt <= 1 && lrg._1regUseCnt > 2 ) + if( splitEmptyMaskByUse(round,lrg) ) + return true; } // Generic split-by-loop depth. @@ -235,39 +254,80 @@ public class RegAlloc { // alloc // V2/rax - kills prior RAX // st4 [V1],len - No good, must split around - makeSplit("def/empty1",round,lrg).insertAfter((Node)lrg._machDef, false/*true*/); + insertAfterAndReplace( makeSplit("def/empty1",round,lrg), (Node)lrg._machDef, false/*true*/); // Split just before use - if( lrg._1regUseCnt==1 ) + if( lrg._1regUseCnt==1 || (lrg._1regDefCnt==1 && ((Node)lrg._machDef).nOuts()==1) ) insertBefore((Node)lrg._machUse,lrg._uidx,"use/empty1",round,lrg); return true; } - // Split live range with an empty mask. Specifically forces splits at - // single-register defs or uses everywhere. - boolean splitEmptyMask( byte round, LRG lrg ) { - findAllLRG(lrg); - // If no single-use or single-def, assume this is a complete register - // kill and force spilling everywhere. - boolean all = lrg._killed || (lrg._1regDefCnt + lrg._1regUseCnt)==0; - for( Node n : _ns ) { - if( !(n instanceof MachNode mach) ) continue; - // Find def of spilling live range; spilling everywhere, OR - // single-register DEF and not cloneable (since these will clone - // before every use) - if( lrg(n)==lrg && (all || (!mach.isClone() && mach.outregmap().size1() )) ) - makeSplit(n,"def/empty2",round,lrg).insertAfter(n,true); - // Find all uses - for( int i=1; i<n.nIns(); i++ ) { - Node def = n.in(i); - // Skip any new splits inserted just this pass - while( def instanceof SplitNode && lrg(def)==null ) - def = def.in(1); - // Main def (past splits) is of the spilling lrg, and spilling - // single-register USE (or everywhere) - if( lrg(def)==lrg && (all || mach.regmap(i).size1()) ) - insertBefore(n,i,"use/empty2",round,lrg); + // Single-def live range with an empty mask. There are many single-reg + // uses. Theory is there's many repeats of the same reg amongst the uses. + // In of splitting once per use, start by splitting into groups based on + // required input register. + boolean splitEmptyMaskByUse( byte round, LRG lrg ) { + Node def = (Node)lrg._machDef; + + // Look at each use, and break into non-overlapping register classes. + Ary<RegMask> rclass = new Ary<>(RegMask.class); + boolean done=false; + int ncalls=0; + while( !done ) { + done = true; + for( Node use : def._outputs ) + if( use instanceof MachNode mach ) { + if( mach instanceof CallNode ) ncalls++; + for( int i=1; i<use.nIns(); i++ ) + if( use.in(i)==def ) + done = putIntoRegClass( rclass, mach.regmap(i) ); + } + } + + // See how many register classes we split into. Generally not + // productive to split like this across calls, which are going to kill + // all registers anyways. + if( rclass._len <= 1 || ncalls > 1 ) return false; + + // Split by class + Ary<Node> ns = new Ary<>(Node.class); + for( RegMask rmask : rclass ) { + ns.addAll(def._outputs); + Node split = makeSplit(def,"popular",round,lrg); + split.insertAfter( def ); + if( split.nIns()>1 ) split.setDef(1,def); + // all uses by class to split + for( Node use : ns ) { + if( use instanceof MachNode mach && use!=split ) { + // Check all use inputs for n, in case there's several + for( int i = 1; i < use.nIns(); i++ ) + // Find a def input, and check register class + if( use.in( i ) == def ) { + RegMask m = mach.regmap( i ); + if( m!=null && mach.regmap( i ).overlap( rmask ) ) + // Modify use to use the split version specialized to this rclass + use.setDefOrdered( i, split ); + } + } + } + ns.clear(); + } + return true; + } + + + // Put use into a register class, perhaps adding a class or perhaps + // narrowing a class (and causing a repeat) + private static boolean putIntoRegClass( Ary<RegMask> rclass, RegMask rmask ) { + for( int i=0; i<rclass._len; i++ ) { + RegMask omask = rclass.at(i); + if( omask.and(rmask) == omask ) return true; // Within the same register class + if( omask.overlap(rmask) ) { + rclass.set(i,new RegMask(omask.copy().and(rmask))); + return false; // Need go again } } + // Add a new class, no need to go again + rclass.push(rmask); return true; } @@ -281,27 +341,28 @@ public class RegAlloc { // For all conflicts for( Node def : conflicts ) { assert lrg(def)==lrg; // Might be conflict use-side + // Split before each use that extends the live range; i.e. is a + // Phi or two-address + for( int i=0; i<def._outputs._len; i++ ) { + Node use = def.out(i); + if( (use instanceof PhiNode phi && + !(phi.region() instanceof LoopNode loop && phi.in(2)==def && def.cfg0().idepth() > loop.idepth() ) ) || + (use instanceof MachNode mach && mach.twoAddress()!=0 && use.in(mach.twoAddress())==def) ) + insertBefore( use, use._inputs.find(def), "use/self/use",round,lrg ); + } // Split after the Phi which extends the LRG. Split also before // Phi slot 1 (and not all inputs), because Phis extend the live range. // TODO: split before all inputs (except the last; at least 1 split here must be extra) if( def instanceof PhiNode phi && !(def instanceof ParmNode) ) { SplitNode split = makeSplit("def/self",round,lrg); - split.insertAfter(def,false); + insertAfterAndReplace(split,def,false); if( split.nOuts()==0 ) - split.kill(); + split.killOrdered(); insertBefore(phi,1,"use/self/phi",round,lrg); } // Split before two-address ops which extend the live range if( def instanceof MachNode mach && mach.twoAddress()!= 0 ) insertBefore(def,mach.twoAddress(),"use/self/two",round,lrg); - // Split before each use that extends the live range; i.e. is a - // Phi or two-address - for( int i=0; i<def._outputs._len; i++ ) { - Node use = def.out(i); - if( (use instanceof PhiNode phi && !(phi.region() instanceof LoopNode && phi.in(2)==def ) ) || - (use instanceof MachNode mach && mach.twoAddress()!=0 && use.in(mach.twoAddress())==def) ) - insertBefore( use, use._inputs.find(def), "use/self/use",round,lrg ); - } } return true; } @@ -348,10 +409,10 @@ public class RegAlloc { (min==max || n.cfg0().loopDepth() <= min) ) { // Cloneable constants will be cloned at uses, not after def if( !(n instanceof MachNode mach && mach.isClone()) && - // Single user is already a split - !(n.nOuts()==1 && n.out(0) instanceof SplitNode) ) + // Single user is already a split adjacent + !(n.nOuts()==1 && n.out(0) instanceof SplitNode split && sameBlockNoClobber(split) ) ) // Split after def in min loop nest - makeSplit("def/loop",round,lrg).insertAfter(n,false); + insertAfterAndReplace( makeSplit("def/loop",round,lrg), n,true); } // PhiNodes check all CFG inputs @@ -362,20 +423,19 @@ public class RegAlloc { // splitting in inner loop or at loop border (min==max || phi.region().cfg(i).loopDepth() <= min) && // and not around the backedge of a loop (bad place to force a split, hard to remove) - !(phi.region() instanceof LoopNode && i==2) ) + !(phi.region() instanceof LoopNode && i==2 && (phi.in(i) instanceof PhiNode pp && pp.region()==phi.region())) ) // Split before phi-use in prior block insertBefore(phi,i, "use/loop/phi",round,lrg); } else { // Others check uses for( int i=1; i<n.nIns(); i++ ) { - if( lrgSame(n.in(i),lrg) && // This is a LRG use - // splitting in inner loop or at loop border - (min==max || n.cfg0().loopDepth() <= min) && - // Not a single-use split same block already - !(n.in(i) instanceof SplitNode && n.in(i).nOuts()==1 && n.cfg0()==n.in(i).cfg0()) ) + // This is a LRG use + // splitting in inner loop or at loop border + if( lrgSame( n.in( i ), lrg ) && + (min == max || (n.in(i) instanceof MachNode mach && mach.isClone()) || n.cfg0().loopDepth() <= min) ) // Split before in this block - insertBefore( n, i, "use/loop/use", round,lrg ); + insertBefore( n, i, "use/loop/use", round,lrg, false ); } } } @@ -396,6 +456,22 @@ public class RegAlloc { int min = (int)ld; int max = (int)(ld>>32); int d = cfg.loopDepth(); + // if n will lower the min loop and is in the tail end of the loop + // header, splitting "around" the loop will not help. Treat n as being + // in the loop. + if( d < min ) { + if( cfg.uctrl() instanceof LoopNode loop && loop.entry()==cfg ) { + for( int i=cfg.nOuts()-2; i>=0; i-- ) { + Node out = cfg.out(i); + if( n==out ) + { d = loop.loopDepth(); break; } // Treat n as being "in the loop" + if( !((out instanceof MachNode mach && mach.isClone()) || out instanceof SplitNode ) ) + break; // Treat b as "normal", out of loop + } + } + } + + // lower min, raise max, and re-fold min = Math.min(min,d); max = Math.max(max,d); return ((long)max<<32) | min; @@ -421,12 +497,12 @@ public class RegAlloc { for( Node n : _ns ) assert !n.isDead(); } - void insertBefore(Node n, int i, String kind, byte round, LRG lrg) { + void insertBefore(Node n, int i, String kind, byte round, LRG lrg, boolean skip) { Node def = n.in(i); // Effective block for use CFGNode cfg = n instanceof PhiNode phi ? phi.region().cfg(i) : n.cfg0(); // Def is a split ? - if( def instanceof SplitNode ) { + if( skip && def instanceof SplitNode ) { boolean singleReg = n instanceof MachNode mach && mach.regmap(i).size1(); // Same block, multiple registers, split is only used by n, // assume this is good enough and do not split again. @@ -435,9 +511,30 @@ public class RegAlloc { } makeSplit(def,kind,round,lrg).insertBefore(n, i); // Skip split-of-split same block - if( def instanceof SplitNode && cfg==def.cfg0() ) + if( skip && def instanceof SplitNode && cfg==def.cfg0() ) n.in(i).setDefOrdered(1,def.in(1)); } + void insertBefore(Node n, int i, String kind, byte round, LRG lrg) { + insertBefore(n,i,kind,round,lrg,true); + } + + // Replace uses of `def` with `split`, and insert `split` immediately after + // `def` in the basic block. + public void insertAfterAndReplace( Node split, Node def, boolean must ) { + split.insertAfter(def); + if( split.nIns()>1 ) split.setDef(1,def); + for( int j=def.nOuts()-1; j>=0; j-- ) { + Node use = def.out(j); + if( use==split ) continue; // Skip self + // Can we avoid a split of a split? 'this' split is used by + // another split in the same block. + if( !must && use instanceof SplitNode split2 && sameBlockNoClobber(split2) ) + continue; + int idx = use._inputs.find(def); + use.setDefOrdered(idx,split); + if( j < def.nOuts() ) j++; + } + } private Node makeSplit( Node def, String kind, byte round, LRG lrg ) { Node split = def instanceof MachNode mach && mach.isClone() @@ -456,11 +553,27 @@ public class RegAlloc { // ----------------------- // POST PASS: Remove empty spills that biased-coloring made private void postColor() { + int maxReg = -1; for( CFGNode bb : _code._cfg ) { // For all ops + if( bb instanceof FunNode fun ) + maxReg = -1; // Reset for new function + // Compute frame size, based on arguments and largest reg seen + if( bb instanceof ReturnNode ret ) + ret.fun().computeFrameAdjust(_code,maxReg); + // Raise frame size by max stack args passed, even if ignored + if( bb instanceof CallEndNode cend ) + maxReg = Math.max(maxReg,cend._xslot); + for( int j=0; j<bb.nOuts(); j++ ) { Node n = bb.out(j); - if( !(n instanceof SplitNode lo) ) continue; - int defreg = lrg(n )._reg; + if( lrg(n)!=null ) + maxReg = Math.max(maxReg,lrg(n)._reg+1); + // Raise frame size by max stack args passed to New + if( n instanceof NewNode nnn ) + maxReg = Math.max(maxReg,nnn._xslot); + + if( !(n instanceof SplitNode ) ) continue; + int defreg = lrg(n )._reg; int usereg = lrg(n.in(1))._reg; // Attempt to bypass split if( defreg != usereg && splitBypass(bb,j,n,defreg) ) @@ -499,4 +612,24 @@ public class RegAlloc { lo.setDefOrdered(1,hi.in(1)); return true; } + + + public boolean sameBlockNoClobber( SplitNode split ) { + Node def = split.in(1); + CFGNode cfg = def.cfg0(); + if( cfg != split.cfg0() ) return false; // Not same block + // Get multinode head + Node def0 = def instanceof ProjNode ? def.in(0) : def; + int defreg = lrg(def)._reg; + if( defreg == -1 ) defreg = lrg(def)._mask.firstReg(); + if( defreg == -1 ) return false; // no allowed registers -> clobbered + for( int idx = cfg._outputs.find(split) -1; idx >= 0; idx-- ) { + Node n = cfg.out(idx); + if( n==def0 ) return true; // No clobbers + if( lrg(n) == lrg(def) ) return false; // Self conflict + if( lrg(n)!=null && lrg(n)._reg == defreg ) + return false; // Clobbered + } + throw Utils.TODO(); + } } diff --git a/src/main/java/com/seaofnodes/simple/codegen/RegMask.java b/src/main/java/com/seaofnodes/simple/codegen/RegMask.java index f28805b5..ec0c4e30 100644 --- a/src/main/java/com/seaofnodes/simple/codegen/RegMask.java +++ b/src/main/java/com/seaofnodes/simple/codegen/RegMask.java @@ -4,11 +4,11 @@ import com.seaofnodes.simple.Utils; import com.seaofnodes.simple.SB; /** RegMask - * A "register mask" - 1 bit set for each allowed register. In addition + * A "register mask" - 1 bit set for each allowed register. In addition, * "stack slot" registers may be allowed, effectively making the set infinite. * <p> * For smaller and simpler machines it suffices to make such masks an i64 or - * i128 (64 or 128 bit integers), and this presentation is by far the better + * i128 (64- or 128-bit integers), and this presentation is by far the better * way to go... if all register allocations can fit in this bit limitation. * The allocator will need bits for stack-based parameters and for splits * which cannot get a register. For a 32-register machine like the X86, add 1 @@ -28,10 +28,11 @@ public class RegMask { public RegMask(int bit) { if( bit < 64 ) _bits0 = 1L<<bit; - else _bits1 = 1L<<(bit=64); + else _bits1 = 1L<<(bit-64); } public RegMask(long bits ) { _bits0 = bits; } public RegMask(long bits0, long bits1 ) { _bits0 = bits0; _bits1 = bits1; } + public RegMask(RegMask r) { _bits0 = r._bits0; _bits1 = r._bits1; } private RegMask() { _bits0 = _bits1 = 0; } // AND, with copy-on-write if changing @@ -65,6 +66,7 @@ public class RegMask { public short firstReg() { + if( isEmpty() ) return -1; return (short)(_bits0 != 0 ? Long.numberOfTrailingZeros(_bits0) : Long.numberOfTrailingZeros(_bits1)+64); @@ -84,7 +86,7 @@ public class RegMask { boolean isEmpty() { return _bits0==0 && _bits1==0; } - boolean test( int reg ) { + public boolean test( int reg ) { return ((reg<64 ? (_bits0 >> reg) : (_bits1 >> (reg-64))) & 1) != 0; } @@ -108,35 +110,17 @@ public class RegMask { Machine mach = CodeGen.CODE._mach; if( _bits0==0 && _bits1==0 ) return sb.p("[]"); sb.p("["); + String[] regs = mach.regs(); for( int i=0; i<64; i++ ) if( ((_bits0 >> i)&1) != 0 ) - sb.p(mach.reg(i)).p(","); + sb.p(reg(regs,i)).p(","); for( int i=0; i<64; i++ ) if( ((_bits1 >> i)&1) != 0 ) - sb.p(mach.reg(i+64)).p(","); + sb.p(reg(regs,i+64)).p(","); return sb.unchar().p("]"); } -} -// Mutable regmask(writable) -class RegMaskRW extends RegMask { - public RegMaskRW(long x, long y) { super(x,y); } - // clears bit at position r. Returns true if the mask is still not empty. - public boolean clr(int r) { - if( r < 64 ) _bits0 &= ~(1L<<(r )); - else _bits1 &= ~(1L<<(r-64)); - return _bits0!=0 || _bits1!=0; - } - @Override RegMaskRW and( RegMask mask ) { - if( mask==null ) return this; - _bits0 &= mask._bits0; - _bits1 &= mask._bits1; - return this; - } - @Override RegMaskRW sub( RegMask mask ) { - if( mask==null ) return this; - _bits0 &= ~mask._bits0; - _bits1 &= ~mask._bits1; - return this; + public static String reg(String[] regs, int i) { + return i<regs.length ? regs[i] : "[stk#"+i+"]"; } } diff --git a/src/main/java/com/seaofnodes/simple/codegen/RegMaskRW.java b/src/main/java/com/seaofnodes/simple/codegen/RegMaskRW.java new file mode 100644 index 00000000..1da9a3b1 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/codegen/RegMaskRW.java @@ -0,0 +1,36 @@ +package com.seaofnodes.simple.codegen; + +// Mutable regmask(writable) +public class RegMaskRW extends RegMask { + public RegMaskRW(long x, long y) { super(x,y); } + // clears bit at position r. Returns true if the mask is still not empty. + public boolean clr(int r) { + if( r < 64 ) _bits0 &= ~(1L<<(r )); + else _bits1 &= ~(1L<<(r-64)); + return _bits0!=0 || _bits1!=0; + } + // sets bit at position r. + public void set(int r) { + if( r < 64 ) _bits0 |= (1L<<(r )); + else _bits1 |= (1L<<(r-64)); + } + @Override RegMaskRW and( RegMask mask ) { + if( mask==null ) return this; + _bits0 &= mask._bits0; + _bits1 &= mask._bits1; + return this; + } + @Override RegMaskRW sub( RegMask mask ) { + if( mask==null ) return this; + _bits0 &= ~mask._bits0; + _bits1 &= ~mask._bits1; + return this; + } + public RegMaskRW or( RegMask mask ) { + if( mask!=null ) { + _bits0 |= mask._bits0; + _bits1 |= mask._bits1; + } + return this; + } +} diff --git a/src/main/java/com/seaofnodes/simple/node/AddFNode.java b/src/main/java/com/seaofnodes/simple/node/AddFNode.java index 7d4e2910..792da62e 100644 --- a/src/main/java/com/seaofnodes/simple/node/AddFNode.java +++ b/src/main/java/com/seaofnodes/simple/node/AddFNode.java @@ -13,7 +13,7 @@ public class AddFNode extends Node { @Override public String glabel() { return "+"; } @Override - StringBuilder _print1(StringBuilder sb, BitSet visited) { + public StringBuilder _print1(StringBuilder sb, BitSet visited) { in(1)._print0(sb.append("("), visited); in(2)._print0(sb.append("+"), visited); return sb.append(")"); diff --git a/src/main/java/com/seaofnodes/simple/node/AddNode.java b/src/main/java/com/seaofnodes/simple/node/AddNode.java index bc65e79a..dd128e76 100644 --- a/src/main/java/com/seaofnodes/simple/node/AddNode.java +++ b/src/main/java/com/seaofnodes/simple/node/AddNode.java @@ -15,7 +15,7 @@ public class AddNode extends Node { @Override public String glabel() { return "+"; } @Override - StringBuilder _print1(StringBuilder sb, BitSet visited) { + public StringBuilder _print1(StringBuilder sb, BitSet visited) { in(1)._print0(sb.append("("), visited); in(2)._print0(sb.append("+"), visited); return sb.append(")"); @@ -96,7 +96,7 @@ public class AddNode extends Node { // If lhs.in(2) is not a constant, we add ourselves as a dependency // because if it later became a constant then we could make this // transformation. - if( lhs.in(2).addDep(this)._type.isConstant() && rhs._type.isConstant() ) + if( addDep(lhs.in(2))._type.isConstant() && rhs._type.isConstant() ) return new AddNode(lhs.in(1),new AddNode(lhs.in(2),rhs).peephole()); diff --git a/src/main/java/com/seaofnodes/simple/node/BoolNode.java b/src/main/java/com/seaofnodes/simple/node/BoolNode.java index e6b87ce8..fd6448a2 100644 --- a/src/main/java/com/seaofnodes/simple/node/BoolNode.java +++ b/src/main/java/com/seaofnodes/simple/node/BoolNode.java @@ -21,7 +21,7 @@ abstract public class BoolNode extends Node { public String glabel() { return op(); } @Override - StringBuilder _print1(StringBuilder sb, BitSet visited) { + public StringBuilder _print1(StringBuilder sb, BitSet visited) { in(1)._print0(sb.append("("), visited); in(2)._print0(sb.append(op()), visited); return sb.append(")"); @@ -92,6 +92,18 @@ abstract public class BoolNode extends Node { Node copyF() { return new EQF(null,null); } } + public static class NE extends BoolNode { + public NE(Node lhs, Node rhs) { super(lhs,rhs); } + public String op() { return "!="; } + TypeInteger doOp(TypeInteger i1, TypeInteger i2) { + if( i1==i2 && i1.isConstant() ) return TRUE; + if( i1._max < i2._min || i1._min > i2._max ) return FALSE; + return BOOL; + } + Node copy(Node lhs, Node rhs) { return new NE(lhs,rhs); } + Node copyF() { throw Utils.TODO(); } + } + public static class LT extends BoolNode { public LT(Node lhs, Node rhs) { super(lhs,rhs); } public String op() { return "<" ; } diff --git a/src/main/java/com/seaofnodes/simple/node/CFGNode.java b/src/main/java/com/seaofnodes/simple/node/CFGNode.java index 37d08fb0..7877f93a 100644 --- a/src/main/java/com/seaofnodes/simple/node/CFGNode.java +++ b/src/main/java/com/seaofnodes/simple/node/CFGNode.java @@ -3,6 +3,7 @@ package com.seaofnodes.simple.node; import com.seaofnodes.simple.Ary; import com.seaofnodes.simple.Parser; import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.codegen.CodeGen; import com.seaofnodes.simple.type.*; import java.util.BitSet; import java.util.HashMap; @@ -55,6 +56,13 @@ public abstract class CFGNode extends Node { return c; } + // Used by the encoding / final BB layout + public CFGNode uctrlSkipEmpty() { + CFGNode x = this, y; + while( x.nOuts() == 1 && (y=x.uctrl())!=null ) // Skip empty blocks + x = y; + return x; + } // ------------------------------------------------------------------------ /** @@ -65,7 +73,9 @@ public abstract class CFGNode extends Node { * See {@link <a href="https://en.wikipedia.org/wiki/Dominator_(graph_theory)">...</a>} */ public int _idepth; - public int idepth() { return _idepth==0 ? (_idepth=idom().idepth()+1) : _idepth; } + public int idepth() { + return CodeGen.CODE.validIDepth(_idepth) ? _idepth : (_idepth=CodeGen.CODE.iDepthFrom(idom().idepth())); + } // Return the immediate dominator of this Node and compute dom tree depth. public CFGNode idom(Node dep) { return cfg(0); } @@ -77,8 +87,8 @@ public abstract class CFGNode extends Node { CFGNode lhs = this; while( lhs != rhs ) { var comp = lhs.idepth() - rhs.idepth(); - if( comp >= 0 ) lhs = ((CFGNode)lhs.addDep(dep)).idom(); - if( comp <= 0 ) rhs = ((CFGNode)rhs.addDep(dep)).idom(); + if( comp >= 0 ) lhs = (dep==null ? lhs : dep.addDep(lhs)).idom(); + if( comp <= 0 ) rhs = (dep==null ? rhs : dep.addDep(rhs)).idom(); } return lhs; } @@ -99,9 +109,9 @@ public abstract class CFGNode extends Node { public LoopNode loop() { return _ltree._head; } public int loopDepth() { return _ltree==null ? 0 : _ltree.depth(); } - LoopTree _ltree; - int _pre; // Pre-order numbers for loop tree finding - private static class LoopTree { + public LoopTree _ltree; + public int _pre; // Pre-order numbers for loop tree finding + static class LoopTree { LoopTree _par; final LoopNode _head; int _depth; @@ -177,4 +187,9 @@ public abstract class CFGNode extends Node { (this instanceof CallNode && usecfg instanceof FunNode) || (this instanceof ReturnNode && usecfg instanceof CallEndNode); } + + + public String label( CFGNode target ) { + return (target instanceof LoopNode ? "LOOP" : "L")+target._nid; + } } diff --git a/src/main/java/com/seaofnodes/simple/node/CProjNode.java b/src/main/java/com/seaofnodes/simple/node/CProjNode.java index adc4e66f..94770f16 100644 --- a/src/main/java/com/seaofnodes/simple/node/CProjNode.java +++ b/src/main/java/com/seaofnodes/simple/node/CProjNode.java @@ -10,10 +10,10 @@ import java.util.BitSet; public class CProjNode extends CFGNode { // Which slice of the incoming multipart value - public final int _idx; + public int _idx; // Debugging label - public final String _label; + public String _label; public CProjNode(Node ctrl, int idx, String label) { super(ctrl); @@ -24,7 +24,7 @@ public class CProjNode extends CFGNode { @Override public String label() { return _label; } - @Override StringBuilder _print1(StringBuilder sb, BitSet visited) { return sb.append(_label); } + @Override public StringBuilder _print1(StringBuilder sb, BitSet visited) { return sb.append(_label); } @Override public boolean blockHead() { return true; } @@ -46,15 +46,21 @@ public class CProjNode extends CFGNode { } // Flip a negating if-test, to remove the not - if( ctrl() instanceof IfNode iff && iff.pred().addDep(this) instanceof NotNode not ) + if( ctrl() instanceof IfNode iff && addDep(iff.pred()) instanceof NotNode not ) return new CProjNode(new IfNode(iff.ctrl(),not.in(1)).peephole(),1-_idx,_idx==0 ? "False" : "True"); // Copy of some other input return ((MultiNode)ctrl()).pcopy(_idx); } + // Only called during basic-block layout, inverts a T/F CProj + public void invert() { + _label = _idx == 0 ? "False" : "True"; + _idx = 1-_idx; + } + @Override - boolean eq( Node n ) { return _idx == ((CProjNode)n)._idx; } + public boolean eq( Node n ) { return _idx == ((CProjNode)n)._idx; } @Override int hash() { return _idx; } diff --git a/src/main/java/com/seaofnodes/simple/node/CallEndNode.java b/src/main/java/com/seaofnodes/simple/node/CallEndNode.java index 03a4462d..f99a0b89 100644 --- a/src/main/java/com/seaofnodes/simple/node/CallEndNode.java +++ b/src/main/java/com/seaofnodes/simple/node/CallEndNode.java @@ -1,7 +1,7 @@ package com.seaofnodes.simple.node; import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.type.*; import java.util.BitSet; @@ -23,7 +23,7 @@ public class CallEndNode extends CFGNode implements MultiNode { public CallNode call() { return (CallNode)in(0); } @Override - StringBuilder _print1(StringBuilder sb, BitSet visited) { + public StringBuilder _print1(StringBuilder sb, BitSet visited) { sb.append("cend( "); sb.append( in(0) instanceof CallNode ? "Call, " : "----, "); for( int i=1; i<nIns()-1; i++ ) @@ -38,7 +38,7 @@ public class CallEndNode extends CFGNode implements MultiNode { return TypeTuple.RET.dual(); Type ret = Type.BOTTOM; TypeMem mem = TypeMem.BOT; - if( call.fptr().addDep(this)._type instanceof TypeFunPtr tfp ) { + if( addDep(call.fptr())._type instanceof TypeFunPtr tfp ) { ret = tfp.ret(); // Here, if I can figure out I've found *all* callers, then I can meet // across the linked returns and join with the function return type. @@ -70,8 +70,9 @@ public class CallEndNode extends CFGNode implements MultiNode { assert fun.in(1) instanceof StartNode && fun.in(2)==call; // Disallow self-recursive inlining (loop unrolling by another name) CFGNode idom = call; - while( !(idom instanceof FunNode fun2) ) + while( !(idom instanceof FunNode) ) idom = idom.idom(); + // Inline? if( idom != fun ) { // Trivial inline: rewrite _folding = true; @@ -81,13 +82,16 @@ public class CallEndNode extends CFGNode implements MultiNode { fun.setDef(2,call.ctrl()); // Bypass the Call; fun.ret().setDef(3,null); // Return is folding also CodeGen.CODE.addAll(fun._outputs); + // Inlining immediately blows all cache idepth fields past the inline point. + // Bump the global version number invalidating them en-masse. + CodeGen.CODE.invalidateIDepthCaches(); return this; } } else { - fun.addDep(this); + addDep(fun); } } else { // Function ptr has multiple users (so maybe multiple call sites) - fptr.addDep(this); + addDep(fptr); } } @@ -97,4 +101,34 @@ public class CallEndNode extends CFGNode implements MultiNode { @Override public Node pcopy(int idx) { return _folding ? in(1).in(idx) : null; } + + // ------------ + // MachNode specifics, shared across all CPUs + public int _xslot; + private RegMask _retMask; + private RegMask _kills; + public void cacheRegs(CodeGen code) { + // Return mask depends on TFP (either GPR or FPR) + _retMask = code._mach.retMask(call().tfp()); + // Kill mask is all caller-saves, and any mirror stack slots for args + // in registers. + RegMaskRW kills = code._callerSave.copy(); + // Start of stack slots + int maxReg = code._mach.regs().length; + // Incoming function arg slots, all low numbered in the RA + int fslot = fun()._maxArgSlot; + // Killed slots for this calls outgoing args + int xslot = code._mach.maxArgSlot(call().tfp()); + _xslot = (maxReg+fslot)+xslot; + for( int i=0; i<xslot; i++ ) + kills.set((maxReg+fslot)+i); + _kills = kills; + } + public String op() { return "cend"; } + public RegMask regmap(int i) { return null; } + public RegMask outregmap() { return null; } + public RegMask outregmap(int idx) { return idx==2 ? _retMask : null; } + public RegMask killmap() { return _kills; } + public void encoding( Encoding enc ) { } + public void asm(CodeGen code, SB sb) { } } diff --git a/src/main/java/com/seaofnodes/simple/node/CallNode.java b/src/main/java/com/seaofnodes/simple/node/CallNode.java index c9bbcc52..78a0b153 100644 --- a/src/main/java/com/seaofnodes/simple/node/CallNode.java +++ b/src/main/java/com/seaofnodes/simple/node/CallNode.java @@ -21,7 +21,7 @@ public class CallNode extends CFGNode { @Override public String label() { return "Call"; } - @Override StringBuilder _print1(StringBuilder sb, BitSet visited) { + @Override public StringBuilder _print1(StringBuilder sb, BitSet visited) { String fname = name(); if( fname == null ) fptr()._print0(sb,visited); else sb.append(fname); @@ -50,6 +50,10 @@ public class CallNode extends CFGNode { // Error if not a TFP public TypeFunPtr tfp() { return (TypeFunPtr)fptr()._type; } + // Call is to an externally supplied code + public boolean external() { return false; } + + // Find the Call End from the Call public CallEndNode cend() { // Always in use slot 0 @@ -87,6 +91,7 @@ public class CallNode extends CFGNode { // Link: call calls target function. Linking makes the target FunNode // point to this Call, and all his Parms point to the call arguments; // also the CallEnd points to the Return. + Node progress = null; if( fptr()._type instanceof TypeFunPtr tfp && tfp.nargs() == nargs() ) { // If fidxs is negative, then infinite unknown functions long fidxs = tfp.fidxs(); @@ -98,12 +103,12 @@ public class CallNode extends CFGNode { TypeFunPtr tfp0 = tfp.makeFrom(fidx); FunNode fun = CodeGen.CODE.link(tfp0); if( fun!=null && !fun._folding && !linked(fun) ) - link(fun); + progress = link(fun); } } } - return null; + return progress; } // True if Fun is linked to this Call @@ -116,7 +121,7 @@ public class CallNode extends CFGNode { // Link so this calls fun - private void link( FunNode fun ) { + private Node link( FunNode fun ) { assert !linked(fun); fun.addDef(this); for( Node use : fun._outputs ) @@ -124,7 +129,7 @@ public class CallNode extends CFGNode { parm.addDef(parm._idx==0 ? new ConstantNode(cend()._rpc).peephole() : arg(parm._idx)); // Call end points to function return CodeGen.CODE.add(cend()).addDef(fun.ret()); - assert linked(fun); + return this; } // Unlink all linked functions @@ -134,7 +139,7 @@ public class CallNode extends CFGNode { assert linked(fun); int idx = fun._inputs.find(this); for( Node use : fun._outputs ) - if( use instanceof ParmNode parm ) + if( use instanceof ParmNode ) use.delDef(idx); fun.delDef(idx); cend().delDef(cend()._inputs.find(fun.ret())); diff --git a/src/main/java/com/seaofnodes/simple/node/CalleeSaveNode.java b/src/main/java/com/seaofnodes/simple/node/CalleeSaveNode.java index d4a937bd..0afc5827 100644 --- a/src/main/java/com/seaofnodes/simple/node/CalleeSaveNode.java +++ b/src/main/java/com/seaofnodes/simple/node/CalleeSaveNode.java @@ -1,10 +1,8 @@ package com.seaofnodes.simple.node; -import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.codegen.Encoding; import com.seaofnodes.simple.codegen.RegMask; import com.seaofnodes.simple.type.Type; -import java.util.BitSet; -import java.io.ByteArrayOutputStream; public class CalleeSaveNode extends ProjNode implements MachNode { final RegMask _mask; @@ -18,7 +16,7 @@ public class CalleeSaveNode extends ProjNode implements MachNode { } @Override public RegMask regmap(int i) { return null; } @Override public RegMask outregmap() { return _mask; } - @Override public int encoding(ByteArrayOutputStream bytes) { return 0; } + @Override public void encoding( Encoding enc ) { } @Override public Type compute() { return Type.BOTTOM; } @Override public Node idealize() { return null; } diff --git a/src/main/java/com/seaofnodes/simple/node/CastNode.java b/src/main/java/com/seaofnodes/simple/node/CastNode.java index 5a92802c..0947bcf0 100644 --- a/src/main/java/com/seaofnodes/simple/node/CastNode.java +++ b/src/main/java/com/seaofnodes/simple/node/CastNode.java @@ -31,7 +31,7 @@ public class CastNode extends Node { @Override public boolean isConst() { return true; } @Override - StringBuilder _print1(StringBuilder sb, BitSet visited) { + public StringBuilder _print1(StringBuilder sb, BitSet visited) { return in(1)._print0(sb.append(label()), visited); } @@ -46,7 +46,7 @@ public class CastNode extends Node { } @Override - boolean eq(Node n) { + public boolean eq(Node n) { CastNode cast = (CastNode)n; // Contract return _t==cast._t; } diff --git a/src/main/java/com/seaofnodes/simple/node/ConstantNode.java b/src/main/java/com/seaofnodes/simple/node/ConstantNode.java index f7ebc006..79f301c1 100644 --- a/src/main/java/com/seaofnodes/simple/node/ConstantNode.java +++ b/src/main/java/com/seaofnodes/simple/node/ConstantNode.java @@ -42,13 +42,13 @@ public class ConstantNode extends Node { public String uniqueName() { return "Con_" + _nid; } @Override - StringBuilder _print1(StringBuilder sb, BitSet visited) { + public StringBuilder _print1(StringBuilder sb, BitSet visited) { if( _con instanceof TypeFunPtr tfp && tfp.isConstant() ) { FunNode fun = CodeGen.CODE.link(tfp); - if( fun._name != null ) + if( fun!=null && fun._name != null ) return sb.append("{ ").append(fun._name).append("}"); } - return sb.append(_con.print(new SB())); + return sb.append(_con==null ? "---" : _con.print(new SB())); } @Override public boolean isConst() { return true; } @@ -60,7 +60,7 @@ public class ConstantNode extends Node { public Node idealize() { return null; } @Override - boolean eq(Node n) { + public boolean eq(Node n) { ConstantNode con = (ConstantNode)n; // Contract return _con==con._con; } diff --git a/src/main/java/com/seaofnodes/simple/node/CtrlNode.java b/src/main/java/com/seaofnodes/simple/node/CtrlNode.java index 61d9c310..eda7d684 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 java.util.BitSet; public class CtrlNode extends CFGNode { public CtrlNode() { super(CodeGen.CODE._start); } @Override public String label() { return "Ctrl"; } - @Override StringBuilder _print1(StringBuilder sb, BitSet visited) { return sb.append("Cctrl"); } + @Override public StringBuilder _print1(StringBuilder sb, BitSet visited) { return sb.append("Cctrl"); } @Override public boolean isConst() { return true; } @Override public Type compute() { return Type.CONTROL; } @Override public Node idealize() { return null; } diff --git a/src/main/java/com/seaofnodes/simple/node/DivFNode.java b/src/main/java/com/seaofnodes/simple/node/DivFNode.java index a6e459c9..633f3224 100644 --- a/src/main/java/com/seaofnodes/simple/node/DivFNode.java +++ b/src/main/java/com/seaofnodes/simple/node/DivFNode.java @@ -13,7 +13,7 @@ public class DivFNode extends Node { @Override public String glabel() { return "/"; } @Override - StringBuilder _print1(StringBuilder sb, BitSet visited) { + public StringBuilder _print1(StringBuilder sb, BitSet visited) { in(1)._print0(sb.append("("), visited); in(2)._print0(sb.append("/"), visited); return sb.append(")"); diff --git a/src/main/java/com/seaofnodes/simple/node/DivNode.java b/src/main/java/com/seaofnodes/simple/node/DivNode.java index 0d5ed297..41518311 100644 --- a/src/main/java/com/seaofnodes/simple/node/DivNode.java +++ b/src/main/java/com/seaofnodes/simple/node/DivNode.java @@ -12,7 +12,7 @@ public class DivNode extends Node { @Override public String glabel() { return "//"; } @Override - StringBuilder _print1(StringBuilder sb, BitSet visited) { + public StringBuilder _print1(StringBuilder sb, BitSet visited) { in(1)._print0(sb.append("("), visited); in(2)._print0(sb.append("/"), visited); return sb.append(")"); diff --git a/src/main/java/com/seaofnodes/simple/node/FRefNode.java b/src/main/java/com/seaofnodes/simple/node/FRefNode.java index f0c4045d..6093b8a6 100644 --- a/src/main/java/com/seaofnodes/simple/node/FRefNode.java +++ b/src/main/java/com/seaofnodes/simple/node/FRefNode.java @@ -19,7 +19,7 @@ public class FRefNode extends ConstantNode { @Override public String uniqueName() { return "FRef_" + _nid; } - @Override StringBuilder _print1(StringBuilder sb, BitSet visited) { + @Override public StringBuilder _print1(StringBuilder sb, BitSet visited) { return sb.append("FRef_").append(_n); } @@ -30,6 +30,6 @@ public class FRefNode extends ConstantNode { public Parser.ParseException err() { return Parser.error("Undefined name '"+_n._name+"'",_n._loc); } - @Override boolean eq(Node n) { return this==n; } + @Override public boolean eq(Node n) { return this==n; } @Override int hash() { return _n._idx; } } diff --git a/src/main/java/com/seaofnodes/simple/node/FunNode.java b/src/main/java/com/seaofnodes/simple/node/FunNode.java index 38aa83a0..b41a7e54 100644 --- a/src/main/java/com/seaofnodes/simple/node/FunNode.java +++ b/src/main/java/com/seaofnodes/simple/node/FunNode.java @@ -1,10 +1,13 @@ package com.seaofnodes.simple.node; + import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.Encoding; +import com.seaofnodes.simple.codegen.RegMask; import com.seaofnodes.simple.type.Type; import com.seaofnodes.simple.type.TypeFunPtr; import com.seaofnodes.simple.type.TypeTuple; - import java.util.BitSet; import static com.seaofnodes.simple.codegen.CodeGen.CODE; @@ -109,8 +112,11 @@ public class FunNode extends RegionNode { return null; } - // Bypass Region idom, always assume depth == 1, one more than Start - @Override public int idepth() { return (_idepth=1); } + // Bypass Region idom, always assume depth == 1, one more than Start, + // unless folding then just a ID on input#1 + @Override public int idepth() { + return _folding ? super.idepth() : CodeGen.CODE.iDepthAt(1); + } // Bypass Region idom, always assume idom is Start @Override public CFGNode idom(Node dep) { return cfg(1); } @@ -161,4 +167,53 @@ public class FunNode extends RegionNode { if( in ) body.set(n._nid); return in; } + + + // ------------ + // MachNode specifics, shared across all CPUs + + // Function may call other functions? X86 requires 16b aligned SP on + // outbound calls, but not leaf. Set during an unrelated RA scan. + public boolean _hasCalls; + + // Maximum incoming argument slot, based on function type + public int _maxArgSlot; + + // Frame adjust, including padding, set during early Encoding + public int _frameAdjust; + + public String op() { return _frameAdjust==0 ? "entry" : "subi"; } + public void postSelect(CodeGen code) { + code.link(this); + // Max slot seen past space reserved for incoming arguments + _maxArgSlot = code._mach.maxArgSlot(sig()); + } + public RegMask regmap(int i) { return null; } + public RegMask outregmap() { return null; } + public void encoding( Encoding enc ) { throw Utils.TODO(); } + public void asm(CodeGen code, SB sb) { + if( _frameAdjust!=0 ) + sb.p("rsp -= #").p(_frameAdjust); + } + + public void computeFrameAdjust(CodeGen code, int maxReg) { + // Max slot seen, or 0. + int maxSlot = Math.max(maxReg - code._mach.regs().length,_maxArgSlot); + // Frame adjust before rounding, can be negative for no spills + int size = maxSlot - _maxArgSlot; + _frameAdjust = size*8; + } + + // Given a high register number and during encoding compute the stack + // pointer offset. Pre-RA we lack the fun._frameAdjust. We also need a + // FunNode. + public int computeStackOffset(CodeGen code, int reg) { + String[] regs = code._mach.regs(); + int stackSlot = reg - regs.length; + int slotRotate = stackSlot < _maxArgSlot + ? stackSlot + (_frameAdjust>>3) + : stackSlot - _maxArgSlot; + return slotRotate*8; + } + } diff --git a/src/main/java/com/seaofnodes/simple/node/IfNode.java b/src/main/java/com/seaofnodes/simple/node/IfNode.java index e04522b0..5d90449e 100644 --- a/src/main/java/com/seaofnodes/simple/node/IfNode.java +++ b/src/main/java/com/seaofnodes/simple/node/IfNode.java @@ -1,10 +1,9 @@ package com.seaofnodes.simple.node; +import com.seaofnodes.simple.Utils; import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.IterPeeps; import com.seaofnodes.simple.type.*; import java.util.BitSet; -import java.util.HashSet; public class IfNode extends CFGNode implements MultiNode { @@ -20,7 +19,9 @@ public class IfNode extends CFGNode implements MultiNode { @Override public StringBuilder _print1(StringBuilder sb, BitSet visited) { sb.append("if( "); - return in(1)._print0(sb, visited).append(" )"); + if( in(1)==null ) sb.append("never"); + else in(1)._print0(sb, visited); + return sb.append(" )"); } public Node ctrl() { return in(0); } @@ -57,11 +58,40 @@ public class IfNode extends CFGNode implements MultiNode { // test on either the true or false branch, that side wins. if( !pred()._type.isHighOrConst() ) for( CFGNode dom = idom(), prior=this; dom!=null; prior = dom, dom = dom.idom() ) - if( dom.addDep(this) instanceof IfNode iff && iff.pred().addDep(this)==pred() && prior instanceof CProjNode prj ) { + if( addDep(dom) instanceof IfNode iff && addDep(iff.pred())==pred() && prior instanceof CProjNode prj ) { setDef(1,con( prj._idx==0 ? 1 : 0 )); return this; } return null; } + // MachNode variants need to support this and negate the conditional test. + // The following CProjs will be inverted by the caller. + public void negate() { throw Utils.TODO(); } + + // Negate the sense of a test + public static String negate( String bop ) { + return switch( bop ) { + case "<" -> ">="; + case "<=" -> ">" ; + case "==" -> "!="; + case "!=" -> "=="; + case ">" -> "<="; + case ">=" -> "<" ; + default -> throw Utils.TODO(); + }; + } + + // Swap compare operands + public static String swap( String bop ) { + return switch( bop ) { + case "<" -> ">" ; + case "<=" -> ">="; + case "==" -> "!="; + case "!=" -> "=="; + case ">" -> "<" ; + case ">=" -> "<="; + default -> throw Utils.TODO(); + }; + } } diff --git a/src/main/java/com/seaofnodes/simple/node/LoadNode.java b/src/main/java/com/seaofnodes/simple/node/LoadNode.java index b70a938d..6147de81 100644 --- a/src/main/java/com/seaofnodes/simple/node/LoadNode.java +++ b/src/main/java/com/seaofnodes/simple/node/LoadNode.java @@ -20,7 +20,7 @@ public class LoadNode extends MemOpNode { * @param off The offset inside the struct base */ public LoadNode(Parser.Lexer loc, String name, int alias, Type glb, Node mem, Node ptr, Node off) { - super(loc, name, alias, glb, mem, ptr, off); + super(loc, name, alias, true, glb, mem, ptr, off); } // GraphVis DOT code (must be valid Java identifiers) and debugger labels @@ -29,7 +29,7 @@ public class LoadNode extends MemOpNode { @Override public String glabel() { return "." +_name; } @Override - StringBuilder _print1(StringBuilder sb, BitSet visited) { return sb.append(".").append(_name); } + public StringBuilder _print1(StringBuilder sb, BitSet visited) { return sb.append(".").append(_name); } @Override public Type compute() { @@ -77,7 +77,7 @@ public class LoadNode extends MemOpNode { while( true ) { switch( mem ) { case StoreNode st: - if( ptr == st.ptr().addDep(this) && off() == st.off() ) + if( ptr == addDep(st.ptr()) && off() == st.off() ) return extend(castRO(st.val())); // Proved equal // Can we prove unequal? Offsets do not overlap? if( !off()._type.join(st.off()._type).isHigh() && // Offsets overlap @@ -88,7 +88,7 @@ public class LoadNode extends MemOpNode { break; case PhiNode phi: // Assume related - phi.addDep(this); + addDep(phi); break outer; case ConstantNode top: break outer; // Assume shortly dead case ProjNode mproj: // Memory projection @@ -182,7 +182,7 @@ public class LoadNode extends MemOpNode { if( px==null ) return false; if( px._type instanceof TypeMem mem && mem._t.isHighOrConst() ) return true; if( px instanceof StoreNode st1 && ptr()==st1.ptr() && off()==st1.off() ) return true; - px.addDep(this); + addDep(px); return false; } diff --git a/src/main/java/com/seaofnodes/simple/node/LogicalNode.java b/src/main/java/com/seaofnodes/simple/node/LogicalNode.java index 8c3452bf..e8552789 100644 --- a/src/main/java/com/seaofnodes/simple/node/LogicalNode.java +++ b/src/main/java/com/seaofnodes/simple/node/LogicalNode.java @@ -13,7 +13,7 @@ public abstract class LogicalNode extends Node { abstract String op(); @Override - StringBuilder _print1(StringBuilder sb, BitSet visited) { + public StringBuilder _print1(StringBuilder sb, BitSet visited) { in(1)._print0(sb.append("("), visited); in(2)._print0(sb.append(op()), visited); return sb.append(")"); diff --git a/src/main/java/com/seaofnodes/simple/node/LoopNode.java b/src/main/java/com/seaofnodes/simple/node/LoopNode.java index d2b0e36c..2fa070bc 100644 --- a/src/main/java/com/seaofnodes/simple/node/LoopNode.java +++ b/src/main/java/com/seaofnodes/simple/node/LoopNode.java @@ -2,9 +2,10 @@ package com.seaofnodes.simple.node; import com.seaofnodes.simple.Parser; import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.codegen.CodeGen; import com.seaofnodes.simple.type.Type; -import com.seaofnodes.simple.type.TypeMem; import com.seaofnodes.simple.type.TypeInteger; +import com.seaofnodes.simple.type.TypeMem; import java.util.BitSet; import java.util.HashSet; @@ -25,7 +26,9 @@ public class LoopNode extends RegionNode { } // Bypass Region idom, same as the default idom() using use in(1) instead of in(0) - @Override public int idepth() { return _idepth==0 ? (_idepth=idom().idepth()+1) : _idepth; } + public int idepth() { + return CodeGen.CODE.validIDepth(_idepth) ? _idepth : (_idepth=CodeGen.CODE.iDepthFrom(idom().idepth())); + } // Bypass Region idom, same as the default idom() using use in(1) instead of in(0) @Override public CFGNode idom(Node dep) { return entry(); } @@ -39,42 +42,48 @@ public class LoopNode extends RegionNode { while( x != this ) { if( x instanceof CProjNode exit && exit.in(0) instanceof IfNode iff ) { CFGNode other = iff.cproj(1-exit._idx); - if( other!=null && other.loopDepth() < loopDepth() ) + if( other!=null && other._ltree != _ltree && nested(_ltree,other._ltree) ) return stop; // Found an exit, not an infinite loop } x = x.idom(); } // Found a no-exit loop. Insert an exit - NeverNode iff = new NeverNode(back()); - for( Node use : _outputs ) - if( use instanceof PhiNode ) - iff.addDef(use); + NeverNode iff = new NeverNode(back()); // Ideal never-branch CProjNode t = new CProjNode(iff,0,"True" ).init(); CProjNode f = new CProjNode(iff,1,"False").init(); - setDef(2,f); + setDef(2,t); // True continues loop, False (never) exits loop + ReturnNode ret = fun.ret(); + iff._ltree = t._ltree = _ltree; + ret._ltree = f._ltree = stop._ltree; // Now fold control into the exit. Might have 1 valid exit, or an // XCtrl or a bunch of prior NeverNode exits. Node top = new ConstantNode(Type.TOP).peephole(); - ReturnNode ret = fun.ret(); + Node memout = new MemMergeNode(false); + memout.addDef(f); // placeholder for control + for( Node u : _outputs ) + if( u instanceof PhiNode phi && phi._type.isa(TypeMem.BOT) ) + memout.addDef(phi); + Node ctrl = ret.ctrl(), mem = ret.mem(), expr = ret.expr(); - if( ctrl._type != Type.XCONTROL ) { + if( ctrl!=null && ctrl._type != Type.XCONTROL ) { // Perfect aligned exit? if( !(ctrl instanceof RegionNode r && mem instanceof PhiNode pmem && pmem.region()==r && expr instanceof PhiNode prez && prez.region()==r ) ) { // Nope, insert an aligned exit layer - ctrl = new RegionNode(_loc,null,ctrl).init(); - mem = new PhiNode((RegionNode)ctrl,mem ).init(); - expr = new PhiNode((RegionNode)ctrl,expr).init(); + RegionNode r = new RegionNode(_loc,null,ctrl).init(); + ctrl = r; r._ltree = stop._ltree; + mem = new PhiNode(r,mem ).init(); + expr = new PhiNode(r,expr).init(); } // Append new Never exit - ctrl.addDef(t ); - mem .addDef(top); + ctrl.addDef(f ); + mem .addDef(memout); expr.addDef(top); } else { - ctrl = t; - mem = top; + ctrl = f; + mem = memout; expr = top; } ret.setDef(0,ctrl); @@ -83,4 +92,12 @@ public class LoopNode extends RegionNode { return stop; } + + private static boolean nested(LoopTree inner, LoopTree outer) { + for( ; inner!=null; inner = inner._par ) + if( inner == outer ) + return true; + return false; + } + } diff --git a/src/main/java/com/seaofnodes/simple/node/MachNode.java b/src/main/java/com/seaofnodes/simple/node/MachNode.java index ecdbc651..f63b3651 100644 --- a/src/main/java/com/seaofnodes/simple/node/MachNode.java +++ b/src/main/java/com/seaofnodes/simple/node/MachNode.java @@ -1,10 +1,7 @@ package com.seaofnodes.simple.node; import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.Machine; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; -import java.io.ByteArrayOutputStream; +import com.seaofnodes.simple.codegen.*; public interface MachNode { @@ -12,7 +9,7 @@ public interface MachNode { default Machine machine() { return CodeGen.CODE._mach; } // Run a post-instruction-selection action - default void postSelect() { } + default void postSelect( CodeGen code ) { } // Register mask allowed on input i. 0 for no register. RegMask regmap( int i ); @@ -47,8 +44,8 @@ public interface MachNode { // Make a clone of a cheap instruction default Node copy() { return null; } - // Encoding is appended into the byte array; size is returned - int encoding(ByteArrayOutputStream bytes); + // Encoding is appended, relocations gathered. + void encoding( Encoding enc ); // Human-readable form appended to the SB. Things like the encoding, // indentation, leading address or block labels not printed here. diff --git a/src/main/java/com/seaofnodes/simple/node/MemMergeNode.java b/src/main/java/com/seaofnodes/simple/node/MemMergeNode.java index 1495a703..32c25a8b 100644 --- a/src/main/java/com/seaofnodes/simple/node/MemMergeNode.java +++ b/src/main/java/com/seaofnodes/simple/node/MemMergeNode.java @@ -30,7 +30,7 @@ public class MemMergeNode extends Node { @Override public boolean isMem() { return true; } @Override - StringBuilder _print1(StringBuilder sb, BitSet visited) { + public StringBuilder _print1(StringBuilder sb, BitSet visited) { sb.append(_inProgress ? "Merge[" : "MEM[ "); for( int j=2; j<nIns(); j++ ) { sb.append(j); @@ -65,11 +65,18 @@ public class MemMergeNode extends Node { if( inProgress() ) return null; // If not merging any memory (all memory is just the default) - if( nIns()==2 ) + if( allDefault() ) return in(1); // Become default memory return null; } + private boolean allDefault() { + for( int i=2; i<nIns(); i++ ) + if( in(1) != in(i) ) + return false; + return true; + } + public Node in( Var v ) { return in(v._idx); } @@ -149,7 +156,7 @@ public class MemMergeNode extends Node { } } - @Override boolean eq( Node n ) { + @Override public boolean eq( Node n ) { return this==n || !_inProgress; } } diff --git a/src/main/java/com/seaofnodes/simple/node/MemOpNode.java b/src/main/java/com/seaofnodes/simple/node/MemOpNode.java index cba4dc1b..058c86fa 100644 --- a/src/main/java/com/seaofnodes/simple/node/MemOpNode.java +++ b/src/main/java/com/seaofnodes/simple/node/MemOpNode.java @@ -9,26 +9,49 @@ import java.util.BitSet; /** * Convenience common base for Load and Store. + * + * 0 - Control + * 1 - Memory + * 2 - Base of object, OOP + * 3 - Offset, integer types + * 4 - Value, for Stores only */ public abstract class MemOpNode extends Node { - public final String _name; + // The equivalence alias class public final int _alias; + + // True if load-like, false if store-like. + // + // Stores produce memory (maybe as part of a tuple with other things), + // loads do not. + // + // Loads might pick up anti-dependences on prior Stores, and never cause an + // anti-dependence themselves. + // + // Stores must maximally sink to the least dominator use. Loads can be + // opportunistically hoisted. + public final boolean _isLoad; + // Declared type; not final because it might be a forward-reference // which will be lazily improved when the reference is declared. public Type _declaredType; + + // A debug name, no semantic meaning + public final String _name; // Source location for late reported errors public final Parser.Lexer _loc; - public MemOpNode(Parser.Lexer loc, String name, int alias, Type glb, Node mem, Node ptr, Node off) { + public MemOpNode(Parser.Lexer loc, String name, int alias, boolean isLoad, Type glb, Node mem, Node ptr, Node off) { super(null, mem, ptr, off); _name = name; _alias = alias; _declaredType = glb; _loc = loc; + _isLoad = isLoad; } - public MemOpNode(Parser.Lexer loc, String name, int alias, Type glb, Node mem, Node ptr, Node off, Node value) { - this(loc,name, alias, glb, mem, ptr, off); + public MemOpNode(Parser.Lexer loc, String name, int alias, boolean isLoad, Type glb, Node mem, Node ptr, Node off, Node value) { + this(loc,name, alias, isLoad, glb, mem, ptr, off); addDef(value); } public MemOpNode( Node mach, MemOpNode mop ) { @@ -36,7 +59,20 @@ public abstract class MemOpNode extends Node { _name = mop==null ? null : mop._name; _alias = mop==null ? 0 : mop._alias; _loc = mop==null ? null : mop._loc; + _isLoad= mop==null ? true : mop._isLoad; _declaredType = mop==null ? Type.BOTTOM : mop._declaredType; + if( mop==null ) + throw Utils.TODO("Load or not"); + } + + // Used by M2 when translating graph to Simple + public MemOpNode( boolean isLoad ) { + super((Node)null); + _name = null; + _alias = 0; + _loc = null; + _isLoad = isLoad; + _declaredType = Type.BOTTOM; } // @@ -47,12 +83,12 @@ public abstract class MemOpNode extends Node { public Node ptr() { return in(2); } public Node off() { return in(3); } - @Override StringBuilder _print1( StringBuilder sb, BitSet visited ) { return _printMach(sb,visited); } + @Override public StringBuilder _print1( StringBuilder sb, BitSet visited ) { return _printMach(sb,visited); } public StringBuilder _printMach( StringBuilder sb, BitSet visited ) { throw Utils.TODO(); } @Override - boolean eq(Node n) { + public boolean eq(Node n) { MemOpNode mem = (MemOpNode)n; // Invariant return _alias==mem._alias; // When comparing types error to use "equals"; always use "==" } diff --git a/src/main/java/com/seaofnodes/simple/node/MinusFNode.java b/src/main/java/com/seaofnodes/simple/node/MinusFNode.java index 2b749ba3..99fc3144 100644 --- a/src/main/java/com/seaofnodes/simple/node/MinusFNode.java +++ b/src/main/java/com/seaofnodes/simple/node/MinusFNode.java @@ -13,7 +13,7 @@ public class MinusFNode extends Node { @Override public String glabel() { return "-"; } @Override - StringBuilder _print1(StringBuilder sb, BitSet visited) { + public StringBuilder _print1(StringBuilder sb, BitSet visited) { in(1)._print0(sb.append("(-"), visited); return sb.append(")"); } diff --git a/src/main/java/com/seaofnodes/simple/node/MinusNode.java b/src/main/java/com/seaofnodes/simple/node/MinusNode.java index dd2cbd26..e06e0c69 100644 --- a/src/main/java/com/seaofnodes/simple/node/MinusNode.java +++ b/src/main/java/com/seaofnodes/simple/node/MinusNode.java @@ -14,7 +14,7 @@ public class MinusNode extends Node { @Override public String glabel() { return "-"; } @Override - StringBuilder _print1(StringBuilder sb, BitSet visited) { + public StringBuilder _print1(StringBuilder sb, BitSet visited) { in(1)._print0(sb.append("(-"), visited); return sb.append(")"); } diff --git a/src/main/java/com/seaofnodes/simple/node/MulFNode.java b/src/main/java/com/seaofnodes/simple/node/MulFNode.java index 2d44a9fb..6fbf4bbb 100644 --- a/src/main/java/com/seaofnodes/simple/node/MulFNode.java +++ b/src/main/java/com/seaofnodes/simple/node/MulFNode.java @@ -13,7 +13,7 @@ public class MulFNode extends Node { @Override public String glabel() { return "*"; } @Override - StringBuilder _print1(StringBuilder sb, BitSet visited) { + public StringBuilder _print1(StringBuilder sb, BitSet visited) { in(1)._print0(sb.append("("), visited); in(2)._print0(sb.append("*"), visited); return sb.append(")"); diff --git a/src/main/java/com/seaofnodes/simple/node/MulNode.java b/src/main/java/com/seaofnodes/simple/node/MulNode.java index 80f8c319..384dbace 100644 --- a/src/main/java/com/seaofnodes/simple/node/MulNode.java +++ b/src/main/java/com/seaofnodes/simple/node/MulNode.java @@ -13,7 +13,7 @@ public class MulNode extends Node { @Override public String glabel() { return "*"; } @Override - StringBuilder _print1(StringBuilder sb, BitSet visited) { + public StringBuilder _print1(StringBuilder sb, BitSet visited) { in(1)._print0(sb.append("("), visited); in(2)._print0(sb.append("*"), visited); return sb.append(")"); diff --git a/src/main/java/com/seaofnodes/simple/node/NeverNode.java b/src/main/java/com/seaofnodes/simple/node/NeverNode.java index 26198500..516f218b 100644 --- a/src/main/java/com/seaofnodes/simple/node/NeverNode.java +++ b/src/main/java/com/seaofnodes/simple/node/NeverNode.java @@ -9,7 +9,8 @@ import java.util.BitSet; // "Never true" for infinite loop exits public class NeverNode extends IfNode { - public NeverNode(Node ctrl) { super(ctrl,Parser.ZERO); } + public NeverNode(NeverNode ctrl) { super(ctrl); } + public NeverNode(Node ctrl) { super(ctrl,null); } @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 80545d66..5a8c1b6b 100644 --- a/src/main/java/com/seaofnodes/simple/node/NewNode.java +++ b/src/main/java/com/seaofnodes/simple/node/NewNode.java @@ -1,7 +1,9 @@ package com.seaofnodes.simple.node; -import com.seaofnodes.simple.type.*; +import com.seaofnodes.simple.SB; import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.type.*; import java.util.BitSet; /** @@ -39,7 +41,7 @@ public class NewNode extends Node implements MultiNode { } @Override - StringBuilder _print1(StringBuilder sb, BitSet visited) { + public StringBuilder _print1(StringBuilder sb, BitSet visited) { sb.append("new "); return sb.append(_ptr._obj.str()); } @@ -76,8 +78,42 @@ public class NewNode extends Node implements MultiNode { public Node idealize() { return null; } @Override - boolean eq(Node n) { return this == n; } + public boolean eq(Node n) { return this == n; } @Override int hash() { return _ptr.hashCode(); } + + // ------------ + // MachNode specifics, shared across all CPUs + public int _arg2Reg, _xslot; + private RegMask _arg3Mask; + private RegMask _retMask; + private RegMask _kills; + public void cacheRegs(CodeGen code) { + _arg2Reg = code._mach.callArgMask(TypeFunPtr.CALLOC,2,0).firstReg(); + _arg3Mask = code._mach.callArgMask(TypeFunPtr.CALLOC,3,0); + // Return mask depends on TFP (either GPR or FPR) + _retMask = code._mach.retMask(TypeFunPtr.CALLOC); + // Kill mask is all caller-saves, and any mirror stack slots for args + // in registers. + RegMaskRW kills = code._callerSave.copy(); + // Start of stack slots + int maxReg = code._mach.regs().length; + // Incoming function arg slots, all low numbered in the RA + int fslot = cfg0().fun()._maxArgSlot; + // Killed slots for this calls outgoing args + int xslot = code._mach.maxArgSlot(TypeFunPtr.CALLOC); + _xslot = (maxReg+fslot)+xslot; + for( int i=0; i<xslot; i++ ) + kills.set((maxReg+fslot)+i); + _kills = kills; + } + public String op() { return "alloc"; } + public RegMask regmap(int i) { return i==1 ? _arg3Mask : null; } + public RegMask outregmap() { return null; } + public RegMask outregmap(int idx) { return idx==1 ? _retMask : null; } + public RegMask killmap() { return _kills; } + public void asm(CodeGen code, SB sb) { + sb.p("#calloc, ").p(code.reg(size())); + } } diff --git a/src/main/java/com/seaofnodes/simple/node/Node.java b/src/main/java/com/seaofnodes/simple/node/Node.java index b3d6c94e..c0c2e606 100644 --- a/src/main/java/com/seaofnodes/simple/node/Node.java +++ b/src/main/java/com/seaofnodes/simple/node/Node.java @@ -16,14 +16,14 @@ import static com.seaofnodes.simple.codegen.CodeGen.CODE; * The Node class provides common functionality used by all subtypes. * Subtypes of Node specialize by overriding methods. */ -public abstract class Node { +public abstract class Node implements Cloneable { /** * Each node has a unique dense Node ID within a compilation context * The ID is useful for debugging, for using as an offset in a bitvector, * as well as for computing equality of nodes (to be implemented later). */ - public final int _nid; + public int _nid; /** * Inputs to the node. These are use-def references to Nodes. @@ -32,7 +32,7 @@ public abstract class Node { * Ordering is required because e.g. "a/b" is different from "b/a". * The first input (offset 0) is often a {@link CFGNode} node. */ - public final Ary<Node> _inputs; + public Ary<Node> _inputs; /** * Outputs reference Nodes that are not null and have this Node as an @@ -43,7 +43,7 @@ public abstract class Node { * walked in either direction. These outputs are typically used for * efficient optimizations but otherwise have no semantics meaning. */ - public final Ary<Node> _outputs; + public Ary<Node> _outputs; /** @@ -120,7 +120,7 @@ public abstract class Node { : _print1(sb, visited); } // Every Node implements this; a partial-line recursive print - abstract StringBuilder _print1(StringBuilder sb, BitSet visited); + abstract public StringBuilder _print1(StringBuilder sb, BitSet visited); public String p(int depth) { return IRPrinter.prettyPrint(this,depth); } @@ -226,6 +226,7 @@ public abstract class Node { } // Breaks the edge invariants, used temporarily + @SuppressWarnings("unchecked") protected <N extends Node> N addUse(Node n) { _outputs.add(n); return (N)this; } // Remove node 'use' from 'def's (i.e. our) output list, by compressing the list in-place. @@ -267,6 +268,15 @@ public abstract class Node { assert isDead(); // Really dead now } + // Preserve CFG use-ordering when killing + public void killOrdered() { + CFGNode cfg = cfg0(); + cfg._outputs.remove(cfg._outputs.find(this)); + _inputs.set(0,null); + kill(); + } + + // Mostly used for asserts and printing. public boolean isDead() { return isUnused() && nIns()==0 && _type==null; } @@ -274,6 +284,7 @@ public abstract class Node { // Add bogus null use to keep node alive public <N extends Node> N keep() { return addUse(null); } // Remove bogus null. + @SuppressWarnings("unchecked") public <N extends Node> N unkeep() { delUse(null); return (N)this; @@ -300,9 +311,8 @@ public abstract class Node { kill(); } - // Replace uses of `def` with `this`, and insert `this` immediately after - // `def` in the basic block. - public void insertAfter( Node def, boolean must ) { + // insert `this` immediately after `def` in the same basic block. + public void insertAfter( Node def ) { CFGNode cfg = def.cfg0(); int i = cfg._outputs.find(def)+1; if( cfg instanceof CallEndNode ) { @@ -315,19 +325,6 @@ public abstract class Node { while( cfg.out(i) instanceof PhiNode || cfg.out(i) instanceof CalleeSaveNode ) i++; cfg._outputs.insert(this,i); _inputs.set(0,cfg); - for( int j=def.nOuts()-1; j>=0; j-- ) { - // Can we avoid a split of a split? 'this' split is used by - // another split in the same block. - if( !must && def.out(j) instanceof SplitNode split && def.out(j).cfg0()==cfg && - !split._kind.contains("self") ) - continue; - Node use = def._outputs.del(j); - use.unlock(); - int idx = use._inputs.find(def); - use._inputs.set(idx,this); - addUse(use); - } - if( nIns()>1 ) setDef(1,def); } // Insert this in front of use.in(uidx) with this, and insert this @@ -337,6 +334,8 @@ public abstract class Node { int i; if( use instanceof PhiNode phi ) { cfg = phi.region().cfg(uidx); + if( cfg instanceof CProjNode && cfg.in(0) instanceof NeverNode nvr ) + cfg = nvr.cfg0(); i = cfg.nOuts()-1; } else { i = cfg._outputs.find(use); @@ -348,7 +347,7 @@ public abstract class Node { use.setDefOrdered(uidx,this); } - public void setDefOrdered(int idx, Node def) { + public void setDefOrdered( int idx, Node def) { // If old is dying, remove from CFG ordered Node old = in(idx); if( old!=null && old.nOuts()==1 ) { @@ -487,6 +486,7 @@ public abstract class Node { return old; } + @SuppressWarnings("unchecked") public <N extends Node> N init() { _type = compute(); return (N)this; } /** @@ -549,17 +549,16 @@ public abstract class Node { * or output of this node, that is, it is at least one step away. The node * being added must benefit from this node being peepholed. */ - Node addDep( Node dep ) { + <N extends Node> N addDep( N dep ) { // Running peepholes during the big assert cannot have side effects // like adding dependencies. - if( CODE._midAssert ) return this; - if( dep == null ) return this; - if( _deps==null ) _deps = new Ary<>(Node.class); - if( _deps .find(dep) != -1 ) return this; // Already on list - if( _inputs .find(dep) != -1 ) return this; // No need for deps on immediate neighbors - if( _outputs.find(dep) != -1 ) return this; - _deps.add(dep); - return this; + if( CODE._midAssert ) return dep; + if( dep._deps==null ) dep._deps = new Ary<>(Node.class); + if( dep._deps .find(this) != -1 ) return dep; // Already on list + if( dep._inputs .find(this) != -1 ) return dep; // No need for deps on immediate neighbors + if( dep._outputs.find(this) != -1 ) return dep; + dep._deps.add(this); + return dep; } // Move the dependents onto a worklist, and clear for future dependents. @@ -585,7 +584,7 @@ public abstract class Node { } // Subclasses add extra checks (such as ConstantNodes have same constant), // and can assume "this!=n" and has the same Java class. - boolean eq( Node n ) { return true; } + public boolean eq( Node n ) { return true; } // Cached hash. If zero, then not computed AND this Node is NOT in the GVN @@ -667,7 +666,7 @@ public abstract class Node { boolean allCons(Node dep) { for( int i=1; i<nIns(); i++ ) if( !(in(i)._type.isConstant()) ) { - in(i).addDep(dep); // If in(i) becomes a constant later, will trigger some peephole + dep.addDep(in(i)); // If in(i) becomes a constant later, will trigger some peephole return false; } return true; @@ -679,6 +678,18 @@ public abstract class Node { // Does not need to be implemented in isCFG() nodes. Node copy(Node lhs, Node rhs) { throw Utils.TODO("Binary ops need to implement copy"); } + public Node copy() { + Node n; + try { n = (Node)clone(); } + catch( Exception e ) { throw new RuntimeException(e); } + n._nid = CODE.getUID(); // allocate unique dense ID + n._inputs = new Ary<>(Node.class); + n._outputs = new Ary<>(Node.class); + n._deps = null; + n._hash = 0; + return n; + } + // Report any post-optimize errors public Parser.ParseException err() { return null; } diff --git a/src/main/java/com/seaofnodes/simple/node/NotNode.java b/src/main/java/com/seaofnodes/simple/node/NotNode.java index b454de66..0314841d 100644 --- a/src/main/java/com/seaofnodes/simple/node/NotNode.java +++ b/src/main/java/com/seaofnodes/simple/node/NotNode.java @@ -12,7 +12,7 @@ public class NotNode extends Node { @Override public String glabel() { return "!"; } @Override - StringBuilder _print1(StringBuilder sb, BitSet visited) { + public StringBuilder _print1(StringBuilder sb, BitSet visited) { in(1)._print0(sb.append("(!"), visited); return sb.append(")"); } diff --git a/src/main/java/com/seaofnodes/simple/node/ParmNode.java b/src/main/java/com/seaofnodes/simple/node/ParmNode.java index 8d7c4205..9e11a129 100644 --- a/src/main/java/com/seaofnodes/simple/node/ParmNode.java +++ b/src/main/java/com/seaofnodes/simple/node/ParmNode.java @@ -22,7 +22,7 @@ public class ParmNode extends PhiNode { public FunNode fun() { return (FunNode)in(0); } @Override - StringBuilder _print1(StringBuilder sb, BitSet visited) { + public StringBuilder _print1(StringBuilder sb, BitSet visited) { if( "main".equals(fun()._name) && _label.equals("arg") ) return sb.append("arg"); sb.append("Parm_").append(_label).append("("); diff --git a/src/main/java/com/seaofnodes/simple/node/PhiNode.java b/src/main/java/com/seaofnodes/simple/node/PhiNode.java index 16f38d45..0579ecd0 100644 --- a/src/main/java/com/seaofnodes/simple/node/PhiNode.java +++ b/src/main/java/com/seaofnodes/simple/node/PhiNode.java @@ -13,7 +13,7 @@ public class PhiNode extends Node { final Type _declaredType; public PhiNode(String label, Type declaredType, Node... inputs) { super(inputs); _label = label; assert declaredType!=null; _declaredType = declaredType; } - public PhiNode(PhiNode phi, String label, Type declaredType) { super(phi); _label = label; _declaredType = declaredType; } + public PhiNode(PhiNode phi, String label, Type declaredType) { super(phi); _label = label; _type = _declaredType = declaredType; } public PhiNode(PhiNode phi) { super(phi); _label = phi._label; _declaredType = phi._declaredType; } public PhiNode(RegionNode r, Node sample) { @@ -29,7 +29,7 @@ public class PhiNode extends Node { @Override public String glabel() { return "φ_"+_label; } @Override - StringBuilder _print1(StringBuilder sb, BitSet visited) { + public StringBuilder _print1(StringBuilder sb, BitSet visited) { if( !(region() instanceof RegionNode r) || r.inProgress() ) sb.append("Z"); sb.append("Phi("); @@ -58,7 +58,7 @@ public class PhiNode extends Node { for (int i = 1; i < nIns(); i++) // If the region's control input is live, add this as a dependency // to the control because we can be peeped should it become dead. - if( r.in(i).addDep(this)._type != Type.XCONTROL ) + if( addDep(r.in(i))._type != Type.XCONTROL ) t = t.meet(in(i)._type); return t; } @@ -80,37 +80,15 @@ public class PhiNode extends Node { if( r.in(i)._type == Type.XCONTROL ) return null; - // Pull "down" a common data op. One less op in the world. One more - // Phi, but Phis do not make code. - // Phi(op(A,B),op(Q,R),op(X,Y)) becomes - // op(Phi(A,Q,X), Phi(B,R,Y)). - Node op = in(1); - if( !isMem() && op.nIns()==3 && op.in(0)==null && same_op() ) { - assert !(op instanceof CFGNode); - Node[] lhss = new Node[nIns()]; - Node[] rhss = new Node[nIns()]; - lhss[0] = rhss[0] = in(0); // Set Region - for( int i=1; i<nIns(); i++ ) { - lhss[i] = in(i).in(1); - rhss[i] = in(i).in(2); - } - Node phi_lhs = new PhiNode(_label, _declaredType,lhss).peephole(); - Node phi_rhs = new PhiNode(_label, _declaredType,rhss).peephole(); - Node down = op.copy(phi_lhs,phi_rhs); - // Test not running backwards, which can happen for e.g. And's - if( down.compute().isa(compute()) ) - return down; - in(1).in(1).addDep(this); - in(1).in(2).addDep(this); - in(2).in(1).addDep(this); - in(2).in(2).addDep(this); - down.kill(); - } + // Generic "pull down op" + Node progress; + if( same_op() && (progress = drop_same_op()) != null ) + return progress; // If merging Phi(N, cast(N)) - we are losing the cast JOIN effects, so just remove. if( nIns()==3 ) { - if( in(1) instanceof CastNode cast && cast.in(1).addDep(this)==in(2) ) return in(2); - if( in(2) instanceof CastNode cast && cast.in(1).addDep(this)==in(1) ) return in(1); + if( in(1) instanceof CastNode cast && addDep(cast.in(1))==in(2) ) return in(2); + if( in(2) instanceof CastNode cast && addDep(cast.in(1))==in(1) ) return in(1); } // If merging a null-checked null and the checked value, just use the value. // if( val ) ..; phi(Region,False=0/null,True=val); @@ -123,7 +101,7 @@ public class PhiNode extends Node { Node val = in(3-nullx); if( val instanceof CastNode cast ) val = cast.in(1); - if( r.idom(this).addDep(this) instanceof IfNode iff && iff.pred().addDep(this)==val ) { + if( addDep(r.idom(this)) instanceof IfNode iff && addDep(iff.pred())==val ) { // Must walk the idom on the null side to make sure we hit False. CFGNode idom = (CFGNode)r.in(nullx); while( idom != null && idom.nIns() > 0 && idom.in(0) != iff ) idom = idom.idom(); @@ -136,13 +114,52 @@ public class PhiNode extends Node { return null; } + // Same op on all Phi paths; all ops have only the Phi as a use. + // None have a control input. private boolean same_op() { - for( int i=2; i<nIns(); i++ ) - if( in(1).getClass() != in(i).getClass() ) + for( int i=1; i<nIns(); i++ ) { + Node op = in(i); + if( in(1).getClass() != op.getClass() || op.in(0)!=null || in(1).nIns() != op.nIns() ) + return false; // Wrong class or CFG bound or mismatched inputs + if( op.nOuts() > 1 ) { // Too many users, but addDep in case lose users + for( Node out : op._outputs ) + if( out!=null && out!=this ) + addDep(out); return false; + } + } return true; } + private Node drop_same_op() { + assert !(in(1) instanceof CFGNode); + Node op = in(1); + Node cp = op.copy(); + cp._type = null; // Fresh type + cp.addDef(null); // No control + + for( int j=1; j<op.nIns(); j++ ) { + boolean needsPhi = false; + Node x = op.in(j); // Jth input from sample #1 + for( int i=2; i<nIns(); i++ ) + if( in(i).in(j) != x ) + { needsPhi=true; break; } + if( needsPhi ) { + x = new PhiNode(_label,op.in(j)._type.glb()); + x.addDef(region()); + for( int i=1; i<nIns(); i++ ) + x.addDef(in(i).in(j)); + x = x.peephole(); + } + cp.addDef(x); + } + // Test not running backwards, which can happen for e.g. And's + if( cp.compute().isa(compute()) ) + return cp; + cp.kill(); + return null; + } + /** * If only single unique input, return it */ @@ -153,7 +170,7 @@ public class PhiNode extends Node { for( int i=1; i<nIns(); i++ ) { // If the region's control input is live, add this as a dependency // to the control because we can be peeped should it become dead. - if( region().in(i).addDep(this)._type != Type.XCONTROL && in(i) != this ) + if( addDep(region().in(i))._type != Type.XCONTROL && in(i) != this ) if( live == null || live == in(i) ) live = in(i); else return null; } @@ -165,7 +182,7 @@ public class PhiNode extends Node { if( !(region() instanceof RegionNode r) ) return false; // When the region completes (is no longer in progress) the Phi can // become a "all constants" Phi, and the "dep" might make progress. - addDep(dep); + dep.addDep(this); if( r.inProgress() ) return false; return super.allCons(dep); } @@ -176,7 +193,7 @@ public class PhiNode extends Node { } // Never equal if inProgress - @Override boolean eq( Node n ) { + @Override public boolean eq( Node n ) { return !inProgress(); } diff --git a/src/main/java/com/seaofnodes/simple/node/ProjNode.java b/src/main/java/com/seaofnodes/simple/node/ProjNode.java index 3d9db4ed..a0394e5b 100644 --- a/src/main/java/com/seaofnodes/simple/node/ProjNode.java +++ b/src/main/java/com/seaofnodes/simple/node/ProjNode.java @@ -24,7 +24,7 @@ public class ProjNode extends Node { @Override public String label() { return _label; } @Override - StringBuilder _print1(StringBuilder sb, BitSet visited) { + public StringBuilder _print1(StringBuilder sb, BitSet visited) { if( _label != null ) return sb.append(_label); if( in(0) instanceof CallEndNode cend && cend.call()!=null ) return cend.call()._print0(sb,visited); @@ -47,7 +47,7 @@ public class ProjNode extends Node { @Override public Node idealize() { return ((MultiNode)in(0)).pcopy(_idx); } @Override - boolean eq( Node n ) { return _idx == ((ProjNode)n)._idx; } + public boolean eq( Node n ) { return _idx == ((ProjNode)n)._idx; } @Override int hash() { return _idx; } diff --git a/src/main/java/com/seaofnodes/simple/node/ReadOnlyNode.java b/src/main/java/com/seaofnodes/simple/node/ReadOnlyNode.java index b755af3f..e9c25f47 100644 --- a/src/main/java/com/seaofnodes/simple/node/ReadOnlyNode.java +++ b/src/main/java/com/seaofnodes/simple/node/ReadOnlyNode.java @@ -14,7 +14,7 @@ public class ReadOnlyNode extends Node { @Override public String label() { return "ReadOnly"; } @Override - StringBuilder _print1(StringBuilder sb, BitSet visited) { + public StringBuilder _print1(StringBuilder sb, BitSet visited) { return in(1)._print0(sb.append("(const)"),visited); } @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 fb6539ae..8f65d0e5 100644 --- a/src/main/java/com/seaofnodes/simple/node/RegionNode.java +++ b/src/main/java/com/seaofnodes/simple/node/RegionNode.java @@ -1,6 +1,7 @@ package com.seaofnodes.simple.node; import com.seaofnodes.simple.*; +import com.seaofnodes.simple.Parser; import com.seaofnodes.simple.codegen.CodeGen; import com.seaofnodes.simple.type.Type; import java.util.*; @@ -17,7 +18,7 @@ public class RegionNode extends CFGNode { public String label() { return "Region"; } @Override - StringBuilder _print1(StringBuilder sb, BitSet visited) { + public StringBuilder _print1(StringBuilder sb, BitSet visited) { return sb.append(label()); } @@ -47,7 +48,7 @@ public class RegionNode extends CFGNode { if( !hasPhi() && // No Phi users, just a control user in(1) instanceof CProjNode p1 && in(2) instanceof CProjNode p2 && - p1.in(0).addDep(this)==p2.in(0).addDep(this) && + addDep(p1.in(0))==addDep(p2.in(0)) && p1.in(0) instanceof IfNode iff ) { // Replace with the iff.ctrl directly if( nIns()==3 ) return iff.ctrl(); @@ -56,6 +57,29 @@ public class RegionNode extends CFGNode { return delDef(2); } + // Flatten stack regions (no loops involved) + if( getClass() == RegionNode.class ) { + for( int i=1; i<nIns(); i++ ) + if( cfg(i) instanceof RegionNode region && region.getClass() == RegionNode.class && region.nIns()>2 && !hasMidUser(region) ) { + assert !region.inProgress(); + // Fold Phis + for( Node use : _outputs ) { + if( use instanceof PhiNode phi ) { + PhiNode phi2 = phi.in(i) instanceof PhiNode phi2x && phi2x.region()==region ? phi2x : null; + for( int j=1; j<region.nIns(); j++ ) + phi.addDef(phi2==null ? phi.in(i) : phi2.in(j)); + CodeGen.CODE.add(phi); + } + } + + // Fold Region + for( int j=1; j<region.nIns(); j++ ) + addDef(region.in(j)); + setDef(i,Parser.XCTRL); + return this; + } + } + return null; } @@ -98,14 +122,32 @@ public class RegionNode extends CFGNode { return false; } + // Is there a value in-between 2 Regions? Difficult to correctly fuse + // without extensive testing, so just ignore. + boolean hasMidUser(RegionNode r) { + for( Node use : r._outputs ) { + if( use==this ) continue; + if( use instanceof PhiNode ) { + for( Node data : use._outputs ) { + if( !(data instanceof PhiNode phi2) || phi2.region()!=this ) + { addDep(use); addDep(data); return true; } + } + } else + { addDep(use); return true; } // Control user + } + return false; + } + + // Immediate dominator of Region is a little more complicated. @Override public int idepth() { - if( _idepth!=0 ) return _idepth; + if( CodeGen.CODE.validIDepth(_idepth) ) + return _idepth; int d=0; for( Node n : _inputs ) if( n!=null ) - d = Math.max(d,((CFGNode)n).idepth()+1); - return _idepth=d; + d = Math.max(d,CodeGen.CODE.iDepthFrom(((CFGNode)n).idepth())); + return _idepth = d; } @Override public CFGNode idom(Node dep) { @@ -121,6 +163,6 @@ public class RegionNode extends CFGNode { public boolean inProgress() { return nIns()>1 && in(nIns()-1) == null; } // Never equal if inProgress - @Override boolean eq( Node n ) { return !inProgress(); } + @Override public boolean eq( Node n ) { return !inProgress(); } } diff --git a/src/main/java/com/seaofnodes/simple/node/ReturnNode.java b/src/main/java/com/seaofnodes/simple/node/ReturnNode.java index 809dd1e1..317f4ce8 100644 --- a/src/main/java/com/seaofnodes/simple/node/ReturnNode.java +++ b/src/main/java/com/seaofnodes/simple/node/ReturnNode.java @@ -3,6 +3,9 @@ package com.seaofnodes.simple.node; import com.seaofnodes.simple.Parser; import com.seaofnodes.simple.SB; import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.Encoding; import com.seaofnodes.simple.type.*; import java.util.BitSet; @@ -29,13 +32,13 @@ public class ReturnNode extends CFGNode { public Node mem () { return in(1); } public Node expr() { return in(2); } public Node rpc () { return in(3); } - public FunNode fun() { return _fun; } + @Override public FunNode fun() { return _fun; } @Override public String label() { return "Return"; } @Override - StringBuilder _print1(StringBuilder sb, BitSet visited) { + public StringBuilder _print1(StringBuilder sb, BitSet visited) { sb.append("return "); if( expr()==null ) sb.append("----"); else expr()._print0(sb, visited); @@ -127,4 +130,39 @@ public class ReturnNode extends CFGNode { if( tp || tn ) sb.p("reference and "); return Parser.error(sb.unchar(5).toString(),loc); } + + + // ------------ + // MachNode specifics, shared across all CPUs + public String op() { + return _fun._frameAdjust > 0 ? "addi" : "ret"; + } + // Correct Nodes outside the normal edges + public void postSelect(CodeGen code) { + FunNode fun = (FunNode)rpc().in(0); + _fun = fun; + fun.setRet(this); + } + public RegMask regmap(int i) { + return i==2 + ? CodeGen.CODE._mach.retMask(_fun.sig()) + : CodeGen.CODE._retMasks[i]; + } + public RegMask outregmap() { return null; } + public void encoding( Encoding enc ) { throw Utils.TODO(); } + public void asm(CodeGen code, SB sb) { + int frameAdjust = fun()._frameAdjust; + if( frameAdjust>0 ) + sb.p("rsp += #").p(frameAdjust).p("\nret"); + // Post code-gen, just print the "ret" + if( code._phase.ordinal() <= CodeGen.Phase.RegAlloc.ordinal() ) + // Prints return reg (either RAX or XMM0), RPC and then the + // callee-save registers. + for( int i=2; i<nIns(); i++ ) + sb.p(code.reg(in(i),fun())).p(" "); + // If we did not get the expected rpc, print which one we got + else if( code._regAlloc.regnum(rpc()) != code._mach.rpc() ) + sb.p("[").p(code.reg(rpc())).p("]"); + } + } diff --git a/src/main/java/com/seaofnodes/simple/node/RoundF32Node.java b/src/main/java/com/seaofnodes/simple/node/RoundF32Node.java index 44d3a587..7936086d 100644 --- a/src/main/java/com/seaofnodes/simple/node/RoundF32Node.java +++ b/src/main/java/com/seaofnodes/simple/node/RoundF32Node.java @@ -13,7 +13,7 @@ public class RoundF32Node extends Node { @Override public String glabel() { return "(f32)"; } @Override - StringBuilder _print1(StringBuilder sb, BitSet visited) { + public StringBuilder _print1(StringBuilder sb, BitSet visited) { return in(1)._print0(sb.append("((f32)"), visited).append(")"); } diff --git a/src/main/java/com/seaofnodes/simple/node/ScopeNode.java b/src/main/java/com/seaofnodes/simple/node/ScopeNode.java index f193e55a..38a5d968 100644 --- a/src/main/java/com/seaofnodes/simple/node/ScopeNode.java +++ b/src/main/java/com/seaofnodes/simple/node/ScopeNode.java @@ -44,7 +44,7 @@ public class ScopeNode extends MemMergeNode { @Override public String label() { return "Scope"; } @Override - StringBuilder _print1(StringBuilder sb, BitSet visited) { + public StringBuilder _print1(StringBuilder sb, BitSet visited) { sb.append("Scope[ "); int j=1; for( int i=0; i<nIns(); i++ ) { diff --git a/src/main/java/com/seaofnodes/simple/node/ShlNode.java b/src/main/java/com/seaofnodes/simple/node/ShlNode.java index 21dbd503..82e01841 100644 --- a/src/main/java/com/seaofnodes/simple/node/ShlNode.java +++ b/src/main/java/com/seaofnodes/simple/node/ShlNode.java @@ -38,7 +38,7 @@ public class ShlNode extends LogicalNode { if( (shl.value()&63)==0 ) return lhs; // (x + c) << i => (x << i) + (c << i) - if( lhs instanceof AddNode add && add.addDep(this).in(2)._type instanceof TypeInteger c && c.isConstant() ) { + if( lhs instanceof AddNode add && addDep(add).in(2)._type instanceof TypeInteger c && c.isConstant() ) { long sum = c.value() << shl.value(); if( Integer.MIN_VALUE <= sum && sum <= Integer.MAX_VALUE ) return new AddNode(new ShlNode(_loc,add.in(1),rhs).peephole(), Parser.con(sum) ); diff --git a/src/main/java/com/seaofnodes/simple/node/SplitNode.java b/src/main/java/com/seaofnodes/simple/node/SplitNode.java index 22cb9bb9..3a1ed011 100644 --- a/src/main/java/com/seaofnodes/simple/node/SplitNode.java +++ b/src/main/java/com/seaofnodes/simple/node/SplitNode.java @@ -11,7 +11,10 @@ public abstract class SplitNode extends MachConcreteNode { public SplitNode(String kind, byte round, Node[] nodes) { super(nodes); _kind = kind; _round = round; } @Override public String op() { return "mov"; } @Override public StringBuilder _print1(StringBuilder sb, BitSet visited) { - return in(1)._print0(sb.append("mov("),visited).append(")"); + sb.append("mov("); + if( in(1) == null ) sb.append("---"); + else in(1)._print0(sb,visited); + return sb.append(")"); } @Override public Type compute() { return in(0)._type; } @Override public Node idealize() { return null; } diff --git a/src/main/java/com/seaofnodes/simple/node/StartNode.java b/src/main/java/com/seaofnodes/simple/node/StartNode.java index 22a1750d..f40203b4 100644 --- a/src/main/java/com/seaofnodes/simple/node/StartNode.java +++ b/src/main/java/com/seaofnodes/simple/node/StartNode.java @@ -25,7 +25,7 @@ public class StartNode extends LoopNode implements MultiNode { @Override public String label() { return "Start"; } @Override - StringBuilder _print1(StringBuilder sb, BitSet visited) { + public StringBuilder _print1(StringBuilder sb, BitSet visited) { return sb.append(label()); } @@ -51,7 +51,7 @@ public class StartNode extends LoopNode implements MultiNode { @Override public Node idealize() { return null; } // No immediate dominator, and idepth==0 - @Override public int idepth() { return 0; } + @Override public int idepth() { return CodeGen.CODE.iDepthAt(0); } @Override public CFGNode idom(Node dep) { return null; } } diff --git a/src/main/java/com/seaofnodes/simple/node/StopNode.java b/src/main/java/com/seaofnodes/simple/node/StopNode.java index b4cb002b..9be08ad9 100644 --- a/src/main/java/com/seaofnodes/simple/node/StopNode.java +++ b/src/main/java/com/seaofnodes/simple/node/StopNode.java @@ -21,12 +21,13 @@ public class StopNode extends CFGNode { } @Override - StringBuilder _print1(StringBuilder sb, BitSet visited) { + public StringBuilder _print1(StringBuilder sb, BitSet visited) { // For the sake of many old tests, and single value prints as "return val" if( ret()!=null ) return ret()._print0(sb,visited); sb.append("Stop[ "); for( Node ret : _inputs ) - ret._print0(sb, visited).append(" "); + if( ret!=null ) + ret._print0(sb, visited).append(" "); return sb.append("]"); } diff --git a/src/main/java/com/seaofnodes/simple/node/StoreNode.java b/src/main/java/com/seaofnodes/simple/node/StoreNode.java index 93294dba..a6992f04 100644 --- a/src/main/java/com/seaofnodes/simple/node/StoreNode.java +++ b/src/main/java/com/seaofnodes/simple/node/StoreNode.java @@ -20,7 +20,7 @@ public class StoreNode extends MemOpNode { * @param value Value to be stored */ public StoreNode(Parser.Lexer loc, String name, int alias, Type glb, Node mem, Node ptr, Node off, Node value, boolean init) { - super(loc, name, alias, glb, mem, ptr, off, value); + super(loc, name, alias, false, glb, mem, ptr, off, value); _init = init; } @@ -33,7 +33,7 @@ public class StoreNode extends MemOpNode { public Node val() { return in(4); } @Override - StringBuilder _print1(StringBuilder sb, BitSet visited) { + public StringBuilder _print1(StringBuilder sb, BitSet visited) { return sb.append(".").append(_name).append("=").append( val()).append(";"); } @@ -84,6 +84,11 @@ public class StoreNode extends MemOpNode { } } + // Store of zero after alloc + if( mem() instanceof ProjNode prj && prj.in(0) instanceof NewNode && + prj.in(0)==ptr().in(0) && // Same NewNode memory & pointer + (val()._type==TypeInteger.ZERO || val()._type==Type.NIL ) ) + return mem(); return null; } @@ -95,7 +100,7 @@ public class StoreNode extends MemOpNode { // when the other uses go away we can retry. for( Node use : mem._outputs ) if( use != this ) - use.addDep(this); + addDep(use); return false; } @@ -106,7 +111,7 @@ public class StoreNode extends MemOpNode { TypeMemPtr tmp = (TypeMemPtr)ptr()._type; if( tmp._obj.field(_name)._final && !_init ) return Parser.error("Cannot modify final field '"+_name+"'",_loc); - Type t = val()._type; + //Type t = val()._type; //return _init || t.isa(_declaredType) ? null : Parser.error("Cannot store "+t+" into field "+_declaredType+" "+_name,_loc); return null; } diff --git a/src/main/java/com/seaofnodes/simple/node/StructNode.java b/src/main/java/com/seaofnodes/simple/node/StructNode.java index 076b342a..4efc0de9 100644 --- a/src/main/java/com/seaofnodes/simple/node/StructNode.java +++ b/src/main/java/com/seaofnodes/simple/node/StructNode.java @@ -14,7 +14,7 @@ public class StructNode extends Node { @Override public String label() { return _ts==null ? "STRUCT?" : _ts.str(); } @Override - StringBuilder _print1(StringBuilder sb, BitSet visited) { + public StringBuilder _print1(StringBuilder sb, BitSet visited) { if( _ts==null ) return sb.append("STRUCT?"); sb.append(_ts._name).append(" {"); for( int i=0; i<nIns(); i++ ) { @@ -39,7 +39,7 @@ public class StructNode extends Node { public Node idealize() { return null; } @Override - boolean eq(Node n) { return _ts == ((StructNode)n)._ts; } + public boolean eq(Node n) { return _ts == ((StructNode)n)._ts; } @Override int hash() { return _ts==null ? 0 : _ts.hashCode(); } diff --git a/src/main/java/com/seaofnodes/simple/node/SubFNode.java b/src/main/java/com/seaofnodes/simple/node/SubFNode.java index b591718d..5c7e969f 100644 --- a/src/main/java/com/seaofnodes/simple/node/SubFNode.java +++ b/src/main/java/com/seaofnodes/simple/node/SubFNode.java @@ -13,7 +13,7 @@ public class SubFNode extends Node { @Override public String glabel() { return "-"; } @Override - StringBuilder _print1(StringBuilder sb, BitSet visited) { + public StringBuilder _print1(StringBuilder sb, BitSet visited) { in(1)._print0(sb.append("("), visited); in(2)._print0(sb.append("-"), visited); return sb.append(")"); diff --git a/src/main/java/com/seaofnodes/simple/node/SubNode.java b/src/main/java/com/seaofnodes/simple/node/SubNode.java index 339c65ca..4290ff9b 100644 --- a/src/main/java/com/seaofnodes/simple/node/SubNode.java +++ b/src/main/java/com/seaofnodes/simple/node/SubNode.java @@ -13,7 +13,7 @@ public class SubNode extends Node { @Override public String glabel() { return "-"; } @Override - StringBuilder _print1(StringBuilder sb, BitSet visited) { + public StringBuilder _print1(StringBuilder sb, BitSet visited) { in(1)._print0(sb.append("("), visited); in(2)._print0(sb.append("-"), visited); return sb.append(")"); diff --git a/src/main/java/com/seaofnodes/simple/node/ToFloatNode.java b/src/main/java/com/seaofnodes/simple/node/ToFloatNode.java index 1f4d67e0..c098ad99 100644 --- a/src/main/java/com/seaofnodes/simple/node/ToFloatNode.java +++ b/src/main/java/com/seaofnodes/simple/node/ToFloatNode.java @@ -14,7 +14,7 @@ public class ToFloatNode extends Node { @Override public String glabel() { return "(flt)"; } @Override - StringBuilder _print1(StringBuilder sb, BitSet visited) { + public StringBuilder _print1(StringBuilder sb, BitSet visited) { return in(1)._print0(sb.append("(flt)"), visited); } diff --git a/src/main/java/com/seaofnodes/simple/node/XCtrlNode.java b/src/main/java/com/seaofnodes/simple/node/XCtrlNode.java index da021246..8c620a04 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 java.util.BitSet; public class XCtrlNode extends CFGNode { public XCtrlNode() { super(new Node[]{CodeGen.CODE._start}); } @Override public String label() { return "Xctrl"; } - @Override StringBuilder _print1(StringBuilder sb, BitSet visited) { return sb.append("Xctrl"); } + @Override public StringBuilder _print1(StringBuilder sb, BitSet visited) { return sb.append("Xctrl"); } @Override public boolean isConst() { return true; } @Override public Type compute() { return Type.XCONTROL; } @Override public Node idealize() { return null; } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddARM.java index c4014571..f37738bf 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddARM.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddARM.java @@ -1,34 +1,20 @@ package com.seaofnodes.simple.node.cpus.arm; import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.TypeInteger; -import java.io.ByteArrayOutputStream; public class AddARM extends MachConcreteNode implements MachNode { + AddARM( Node add) { super(add); } + @Override public String op() { return "add"; } + @Override public RegMask regmap(int i) { return arm.RMASK; } + @Override public RegMask outregmap() { return arm.WMASK; } - AddARM( Node add) {super(add); } - - // Register mask allowed on input i. - @Override public RegMask regmap(int i) { - assert i== 1 || i == 2 || i == 3; - return arm.RMASK; - } - // Register mask allowed as a result. 0 for no register. - @Override public RegMask outregmap() { return arm.RMASK; } - - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); - } - + // ADD (shifted register) + @Override public void encoding( Encoding enc ) { arm.r_reg(enc,this,arm.OP_ADD); } // General form: "rd = rs1 + rs2" @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" + ").p(code.reg(in(2))); } - @Override public String op() { - return "add";} } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddFARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddFARM.java index 098ab8c1..209f24f4 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddFARM.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddFARM.java @@ -1,31 +1,22 @@ package com.seaofnodes.simple.node.cpus.arm; - -import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.SB; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.TypeInteger; -import java.io.ByteArrayOutputStream; public class AddFARM extends MachConcreteNode implements MachNode { AddFARM(Node addf) { super(addf); } - - // Register mask allowed on input i. - @Override public RegMask regmap(int i) { assert i==1 || i==2; return arm.DMASK; } - // Register mask allowed as a result. 0 for no register. + @Override public String op() { return "addf"; } + @Override public RegMask regmap(int i) { return arm.DMASK; } @Override public RegMask outregmap() { return arm.DMASK; } - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); - } + //FADD (scalar) + @Override public void encoding( Encoding enc ) { arm.f_scalar(enc,this,arm.OPF_OP_ADD);} // Default on double precision for now(64 bits) - // General form: "vadd.32 rd = src1 + src2 + // General form: "addf rd = src1 + src2 @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" + ").p(code.reg(in(2))); } - @Override public String op() { return "addf"; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddIARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddIARM.java index c8f5f8dd..a1cd8306 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddIARM.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddIARM.java @@ -1,51 +1,27 @@ package com.seaofnodes.simple.node.cpus.arm; import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.TypeInteger; -import java.io.ByteArrayOutputStream; public class AddIARM extends MachConcreteNode implements MachNode { - final TypeInteger _ti; - - AddIARM(Node add, TypeInteger ti) { + final int _imm; + AddIARM(Node add, int imm) { super(add); _inputs.pop(); - _ti = ti; + _imm = imm; } - - // Register mask allowed on input i. - @Override - public RegMask regmap(int i) { - // assert i== i; - return arm.RMASK; + @Override public String op() { + return _imm == 1 ? "inc" : (_imm == -1 ? "dec" : "addi"); } - - // Register mask allowed as a result. 0 for no register. - @Override - public RegMask outregmap() { - return arm.RMASK; - } - - // Encoding is appended into the byte array; size is returned - @Override - public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); + @Override public RegMask regmap(int i) { return arm.RMASK; } + @Override public RegMask outregmap() { return arm.WMASK; } + //ADD (immediate) + @Override public void encoding( Encoding enc ) { + arm.imm_inst(enc,this, in(1), arm.OPI_ADD,_imm); } - // General form: "addi rd = rs1 + imm" - @Override - public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" + #"); - _ti.print(sb); + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" + #").p(_imm); } - - @Override - public String op() { - return _ti.value() == 1 ? "inc" : (_ti.value() == -1 ? "dec" : "addi"); - - } - -}
\ No newline at end of file +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/AndARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AndARM.java index 7655df40..ad76ca4c 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/AndARM.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AndARM.java @@ -1,36 +1,19 @@ package com.seaofnodes.simple.node.cpus.arm; import com.seaofnodes.simple.SB; -import com.seaofnodes.simple.Utils; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.MachConcreteNode; import com.seaofnodes.simple.node.MachNode; import com.seaofnodes.simple.node.Node; -import java.io.ByteArrayOutputStream; - public class AndARM extends MachConcreteNode implements MachNode { - AndARM(Node and) { - super(and); - } - - // Register mask allowed on input i. - // This is the normal calling convention - @Override public RegMask regmap(int i) { - assert i==1 || i==2; - return arm.RMASK; - } - - // Register mask allowed as a result. 0 for no register. - @Override public RegMask outregmap() { return arm.RMASK; } - - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); - } - - // General form + AndARM(Node and) { super(and); } + @Override public String op() { return "and"; } + @Override public String glabel() { return "&"; } + @Override public RegMask regmap(int i) { return arm.RMASK; } + @Override public RegMask outregmap() { return arm.WMASK; } + // AND (shifted register) + @Override public void encoding( Encoding enc ) { arm.r_reg(enc,this,arm.OP_AND); } // General form: #rd = rs1 & rs2 @Override public void asm(CodeGen code, SB sb){ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" & ").p(code.reg(in(2))); diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/AndIARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AndIARM.java index ee05654b..1f12ef13 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/AndIARM.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AndIARM.java @@ -1,56 +1,24 @@ package com.seaofnodes.simple.node.cpus.arm; - import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.Type; -import com.seaofnodes.simple.type.TypeInteger; -import java.io.ByteArrayOutputStream; -import java.util.BitSet; -import java.lang.StringBuilder; - -public class AndIARM extends MachConcreteNode implements MachNode { - final TypeInteger _ti; - AndIARM(AndNode and, TypeInteger ti) { +public class AndIARM extends MachConcreteNode implements MachNode { + final int _imm; + AndIARM(Node and, int imm) { super(and); _inputs.pop(); - _ti = ti; - } - - // Register mask allowed on input i. - // This is the normal calling convention - @Override - public RegMask regmap(int i) { - assert i == 1 || i == 2; - return arm.RMASK; + _imm = imm; } - - // Register mask allowed as a result. 0 for no register. - @Override - public RegMask outregmap() { - return arm.RMASK; + @Override public String op() { return "andi"; } + @Override public RegMask regmap(int i) { return arm.RMASK; } + @Override public RegMask outregmap() { return arm.WMASK; } + @Override public void encoding( Encoding enc ) { + arm.imm_inst_n(enc,this, in(1), arm.OPI_AND,_imm); } - - // Encoding is appended into the byte array; size is returned - @Override - public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); - } - - // General form // General form: "andi rd = rs1 & imm" - @Override - public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" & #"); - _ti.print(sb); + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" & #").p(arm.decodeImm12(_imm)); } - - @Override - public String op() { - return "andi"; - } - }
\ No newline at end of file diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/AsrARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AsrARM.java index a79de49b..342b7519 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/AsrARM.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AsrARM.java @@ -1,40 +1,18 @@ package com.seaofnodes.simple.node.cpus.arm; import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.Type; -import com.seaofnodes.simple.type.TypeInteger; -import java.io.ByteArrayOutputStream; -import java.util.BitSet; -import java.lang.StringBuilder; // Arithmetic Shift Right (register) public class AsrARM extends MachConcreteNode implements MachNode { AsrARM(Node asr) {super(asr);} - // Register mask allowed on input i. - // This is the normal calling convention - @Override public RegMask regmap(int i) { - // assert i==1; - return arm.RMASK; } - // Register mask allowed as a result. 0 for no register. - @Override public RegMask outregmap() { return arm.RMASK; } - - // Output is same register as input#1 - @Override public int twoAddress() { return 1; } - - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); - } - - // General form - // General form: "asr rd, rs1, rs2" + @Override public String op() { return "sar"; } + @Override public RegMask regmap(int i) { return arm.RMASK; } + @Override public RegMask outregmap() { return arm.DMASK; } + @Override public void encoding( Encoding enc ) { arm.shift_reg(enc,this,arm.OP_ASR); } @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" >> ").p(code.reg(in(2))); } - @Override public String op() { return "asr"; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/AsrIARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AsrIARM.java index 099c3699..a1775dde 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/AsrIARM.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AsrIARM.java @@ -1,41 +1,31 @@ package com.seaofnodes.simple.node.cpus.arm; - -import com.seaofnodes.simple.*; +import com.seaofnodes.simple.SB; import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.Encoding; import com.seaofnodes.simple.codegen.RegMask; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.Type; -import com.seaofnodes.simple.type.TypeInteger; -import java.io.ByteArrayOutputStream; -import java.util.BitSet; -import java.lang.StringBuilder; +import com.seaofnodes.simple.node.MachConcreteNode; +import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.node.Node; -// Arithmetic Shift Right (immediate) public class AsrIARM extends MachConcreteNode implements MachNode { - final TypeInteger _ti; - AsrIARM(Node asri, TypeInteger ti) {super(asri); _inputs.pop(); _ti = ti;} - - // Register mask allowed on input i. - // This is the normal calling convention - @Override public RegMask regmap(int i) { - // assert i==1; - return arm.RMASK; } - - // Register mask allowed as a result. 0 for no register. - @Override public RegMask outregmap() { return arm.RMASK; } - - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); + final int _imm; + AsrIARM(Node asr, int imm) { + super(asr); + _inputs.pop(); + _imm = imm; } - - // General form - // General form: "asri rd, rs1, imm" + @Override public String op() { return "asri"; } + @Override public RegMask regmap(int i) { return arm.RMASK; } + @Override public RegMask outregmap() { return arm.WMASK; } + @Override public void encoding( Encoding enc ) { + short rd = enc.reg(this); + short rn = enc.reg(in(1)); + assert _imm > 0; + enc.add4(arm.imm_shift(arm.OPI_ASR,_imm, 0b111111, rn, rd)); + } + // General form: "asri rd = rs1 >> imm" @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" >> #"); - _ti.print(sb); + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" >> #").p(_imm); } - - @Override public String op() { return "asri"; } -} +}
\ No newline at end of file diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/BranchARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/BranchARM.java index 1a5108cf..b36a395b 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/BranchARM.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/BranchARM.java @@ -1,25 +1,22 @@ package com.seaofnodes.simple.node.cpus.arm; import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; -import java.io.ByteArrayOutputStream; - - // Jump on flags, uses flags -public class BranchARM extends IfNode implements MachNode{ - final String _bop; +public class BranchARM extends IfNode implements MachNode, RIPRelSize { + String _bop; BranchARM(IfNode iff, String bop ) { super(iff); _bop = bop; } - + @Override public String op() { return "j"+_bop; } @Override public String label() { return op(); } - @Override public void postSelect() { + @Override public void postSelect(CodeGen code) { Node set = in(1); + if( set==null ) return; // Never-node cutout Node cmp = set.in(1); // Bypass an expected Set and just reference the cmp directly if( set instanceof SetARM) @@ -30,19 +27,50 @@ public class BranchARM extends IfNode implements MachNode{ @Override public RegMask regmap(int i) { assert i==1; return arm.FLAGS_MASK; } @Override public RegMask outregmap() { return null; } + @Override public void negate() { _bop = negate(_bop); } + + @Override public void encoding( Encoding enc ) { + // Assuming that condition flags are already set. These flags are set + // by comparison (or sub). No need for regs because it uses flags + if( in(1)!=null ) { + // B.cond + enc.add4( arm.b_cond(arm.OP_BRANCH, 0, arm.make_condition(_bop)) ); + } else { + if( _bop=="!=" ) return; // Inverted, no code + enc.add4(arm.b(arm.OP_UJMP, 0)); + } + enc.jump(this,cproj(0)); + } - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { + // Delta is from opcode start + @Override public byte encSize(int delta) { + if( in(1)==null && _bop=="!=" ) return 0; // Inverted never-node, no code + if( -(1<<19) <= delta && delta < (1<<19) ) return 4; + // 2 word encoding needs a tmp register, must teach RA throw Utils.TODO(); } - @Override public void asm(CodeGen code, SB sb) { - String src = code.reg(in(1)); - if( src!="flags" ) sb.p(src); + // Delta is from opcode start + @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { + assert !( in(1)==null && _bop=="!=" ); // Inverted never-node, no code no patch + if( opLen==4 ) { + enc.patch4(opStart,arm.b_cond(arm.OP_BRANCH, delta, arm.make_condition(_bop))); + } else { + throw Utils.TODO(); + } } - @Override public String op() { return "j"+_bop; } - @Override public String comment() { - return "L"+cproj(1)._nid+", L"+cproj(0)._nid; + @Override public void asm(CodeGen code, SB sb) { + if( in(1)!=null ) { // Never-node + String src = code.reg(in(1)); + if( src!="flags" ) sb.p(src).p(" "); + } else if( _bop=="!=" ) { + sb.p("never"); + return; + } + CFGNode prj = cproj(0).uctrlSkipEmpty(); + if( !prj.blockHead() ) prj = prj.cfg0(); + sb.p(label(prj)); } + @Override public String comment() { return "L"+cproj(1)._nid; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallARM.java index 4359c045..7f6fe93c 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallARM.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallARM.java @@ -1,13 +1,11 @@ package com.seaofnodes.simple.node.cpus.arm; import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; import com.seaofnodes.simple.type.TypeFunPtr; -import java.io.ByteArrayOutputStream; -public class CallARM extends CallNode implements MachNode { +public class CallARM extends CallNode implements MachNode, RIPRelSize { final TypeFunPtr _tfp; final String _name; @@ -19,27 +17,31 @@ public class CallARM extends CallNode implements MachNode { _name = CodeGen.CODE.link(tfp)._name; } + @Override public String op() { return "call"; } @Override public String label() { return op(); } - @Override public RegMask regmap(int i) { - return arm.callInMask(_tfp,i); // Normal argument - } - @Override public RegMask outregmap() { return null; } - @Override public String name() { return _name; } @Override public TypeFunPtr tfp() { return _tfp; } + @Override public RegMask regmap(int i) { return arm.callInMask(_tfp,i,fun()._maxArgSlot); } + @Override public RegMask outregmap() { return null; } - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); + @Override public void encoding( Encoding enc ) { + enc.relo(this); + // BL + enc.add4(arm.b(arm.OP_CALL,0)); // Target patched at link time + } + + // Delta is from opcode start, but X86 measures from the end of the 5-byte encoding + @Override public byte encSize(int delta) { return 4; } + + // Delta is from opcode start + @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { + enc.patch4(opStart,arm.b(arm.OP_CALL,delta)); } @Override public void asm(CodeGen code, SB sb) { sb.p(_name); for( int i=0; i<nargs(); i++ ) - sb.p(code.reg(arg(i))).p(" "); + sb.p(code.reg(arg(i+2))).p(" "); sb.unchar(2); } - - @Override public String op() { return "call"; } - } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallEndARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallEndARM.java index 6177c5ea..b3c2f6c2 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallEndARM.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallEndARM.java @@ -1,35 +1,8 @@ package com.seaofnodes.simple.node.cpus.arm; -import com.seaofnodes.simple.SB; -import com.seaofnodes.simple.Utils; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; import com.seaofnodes.simple.node.CallEndNode; import com.seaofnodes.simple.node.MachNode; -import com.seaofnodes.simple.type.TypeFloat; -import com.seaofnodes.simple.type.TypeFunPtr; -import java.io.ByteArrayOutputStream; public class CallEndARM extends CallEndNode implements MachNode { - final TypeFunPtr _tfp; - CallEndARM( CallEndNode cend ) { - super(cend); - _tfp = (TypeFunPtr)(cend.call().fptr()._type); - } - - @Override public String label() { return op(); } - @Override public RegMask regmap(int i) { return null; } - @Override public RegMask outregmap() { return null; } - @Override public RegMask outregmap(int idx) { return idx == 2 ? arm.retMask(_tfp,2) : null; } - @Override public RegMask killmap() { return arm.armCallerSave(); } - - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); - } - - @Override public void asm(CodeGen code, SB sb) { } - - @Override public String op() { return "cend"; } - + CallEndARM( CallEndNode cend ) { super(cend); } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallRRARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallRRARM.java index db85a9a9..87dde38c 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallRRARM.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallRRARM.java @@ -1,26 +1,25 @@ package com.seaofnodes.simple.node.cpus.arm; import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; -import java.io.ByteArrayOutputStream; public class CallRRARM extends CallNode implements MachNode { - CallRRARM(CallNode call) {super(call);} + CallRRARM(CallNode call) { super(call); } @Override public String op() { return "callr"; } @Override public String label() { return op(); } @Override public RegMask regmap(int i) { - // Todo: float or int? return i==_inputs._len ? arm.RMASK // Function call target - : arm.callInMask(tfp(),i); // Normal argument + : arm.callInMask(tfp(),i,fun()._maxArgSlot); // Normal argument } @Override public RegMask outregmap() { return null; } - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); + @Override public void encoding( Encoding enc ) { + // Needs a register, typically a jump-and-link-register opcode + // blr + short self = enc.reg(this); + enc.add4(arm.blr(arm.OP_CALLRARM, self)); } @Override public void asm(CodeGen code, SB sb) { @@ -29,4 +28,5 @@ public class CallRRARM extends CallNode implements MachNode { sb.p(code.reg(arg(i))).p(" "); sb.unchar(2); } + } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/CastARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/CastARM.java new file mode 100644 index 00000000..a5a4e112 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/CastARM.java @@ -0,0 +1,18 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.SB; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.Encoding; +import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.node.CastNode; +import com.seaofnodes.simple.node.MachNode; + +public class CastARM extends CastNode implements MachNode { + public CastARM( CastNode cast ) { super(cast); } + @Override public String op() { return label(); } + @Override public RegMask regmap(int i) { assert i==1; return RegMask.FULL; } + @Override public RegMask outregmap() { return RegMask.FULL; } + @Override public int twoAddress( ) { return 1; } + @Override public void encoding( Encoding enc ) { } + @Override public void asm(CodeGen code, SB sb) { _t.print(sb.p(code.reg(in(1))).p(" isa ")); } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpARM.java index f4f6242d..dbc86f62 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpARM.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpARM.java @@ -1,25 +1,17 @@ package com.seaofnodes.simple.node.cpus.arm; -import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.SB; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.Type; -import com.seaofnodes.simple.type.TypeFunPtr; -import java.io.ByteArrayOutputStream; public class CmpARM extends MachConcreteNode implements MachNode { CmpARM( Node cmp ) { super(cmp); } @Override public String op() { return "cmp"; } - - - @Override public RegMask regmap(int i) { assert i==1 || i==2; return arm.RMASK; } + @Override public RegMask regmap(int i) { return arm.RMASK; } @Override public RegMask outregmap() { return arm.FLAGS_MASK; } - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); - } + // SUBS (shifted register) + @Override public void encoding( Encoding enc ) { arm.r_reg_subs(enc,this,arm.OP_CMP); } // General form: "cmp rs1, rs2" @Override public void asm(CodeGen code, SB sb) { diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpFARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpFARM.java index 9c287fcb..e82a2ee3 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpFARM.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpFARM.java @@ -1,31 +1,22 @@ package com.seaofnodes.simple.node.cpus.arm; import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.Type; -import com.seaofnodes.simple.type.TypeFunPtr; -import java.io.ByteArrayOutputStream; // compare instruction on float regs input(D-registers) public class CmpFARM extends MachConcreteNode implements MachNode{ CmpFARM(Node cmp) {super(cmp);} + @Override public String op() { return "cmpf"; } + @Override public RegMask regmap(int i) { return arm.DMASK; } + @Override public RegMask outregmap() { return arm.FLAGS_MASK; } - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); - } - + // FCMP + @Override public void encoding( Encoding enc ) { arm.f_cmp(enc,this); } // General form: "cmp d0, d1" @Override public void asm(CodeGen code, SB sb) { String dst = code.reg(this); if( dst!="flags" ) sb.p(dst).p(" = "); sb.p(code.reg(in(1))).p(", ").p(code.reg(in(2))); } - - @Override public RegMask regmap(int i) { assert i==1 || i==2; return arm.DMASK; } - @Override public RegMask outregmap() { return arm.FLAGS_MASK; } - - @Override public String op() { return "cmpf"; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpIARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpIARM.java index a0b10ba4..8a4f242a 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpIARM.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpIARM.java @@ -1,46 +1,34 @@ package com.seaofnodes.simple.node.cpus.arm; import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.Type; -import com.seaofnodes.simple.type.TypeInteger; -import java.io.ByteArrayOutputStream; // Compare with immediate. -// Conditional compare (immediate)? e.g CCMP(immediate) +// CMP (immediate) - SUBS public class CmpIARM extends MachConcreteNode implements MachNode { final int _imm; final String _bop; - CmpIARM(BoolNode bool, TypeInteger ti) { + CmpIARM(BoolNode bool, int imm) { super(bool); _inputs.pop(); _bop = bool.op(); - _imm = (int)ti.value(); - assert _imm == ti.value(); + _imm = imm; } - // Copy constructor does not set reverse edges in, because the basic block - // is changing from the original node - and it must be properly placed in a - // new block. CmpIARM( CmpIARM cmp ) { - super(cmp); + super((Node)null); + addDef(null); + addDef(cmp.in(1)); _bop = cmp._bop; _imm = cmp._imm; - // While in(0) will be handled by the caller, all other edges must be - // handled in the constructor. - cmp.in(1)._outputs.push(this); } - - @Override public RegMask regmap(int i) { assert i==1; return arm.RMASK; } + @Override public String op() { return _imm==0 ? "test" : "cmp"; } + @Override public RegMask regmap(int i) { return arm.RMASK; } @Override public RegMask outregmap() { return arm.FLAGS_MASK; } @Override public boolean isClone() { return true; } @Override public Node copy() { return new CmpIARM(this); } - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); - } + @Override public void encoding( Encoding enc ) { arm.imm_inst_subs(enc,in(1),in(1), arm.OPI_CMP,_imm); } // General form: "cmp rs1, 1" @Override public void asm(CodeGen code, SB sb) { @@ -49,6 +37,4 @@ public class CmpIARM extends MachConcreteNode implements MachNode { sb.p(code.reg(in(1))); if( _imm != 0 ) sb.p(", #").p(_imm); } - @Override public String op() { return _imm==0 ? "test" : "cmp"; } - } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpMemARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpMemARM.java deleted file mode 100644 index 47f68ccc..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpMemARM.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.seaofnodes.simple.node.cpus.arm; - -public class CmpMemARM { -} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/DivARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/DivARM.java index 7c67e2dd..40bd8387 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/DivARM.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/DivARM.java @@ -1,33 +1,19 @@ package com.seaofnodes.simple.node.cpus.arm; import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.TypeInteger; -import java.io.ByteArrayOutputStream; public class DivARM extends MachConcreteNode implements MachNode { DivARM( Node div ) { super(div); } - - // Register mask allowed on input i. - @Override public RegMask regmap(int i) { - assert i==1 || i==2; - return arm.RMASK; } - // Register mask allowed as a result. 0 for no register. - @Override public RegMask outregmap() { return arm.RMASK; } - // Output is same register as input#1 - @Override public int twoAddress() { return 1; } - - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); - } - + @Override public String op() { return "div"; } + @Override public RegMask regmap(int i) { return arm.RMASK; } + @Override public RegMask outregmap() { return arm.WMASK; } + // SDIV + @Override public void encoding( Encoding enc ) { arm.madd(enc,this,arm.OP_DIV,3); } // General form: "div dst /= src" @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" / ").p(code.reg(in(2))); } - @Override public String op() { return "div"; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/DivFARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/DivFARM.java index 2137c01c..72b76444 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/DivFARM.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/DivFARM.java @@ -1,30 +1,19 @@ package com.seaofnodes.simple.node.cpus.arm; import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.TypeInteger; -import java.io.ByteArrayOutputStream; public class DivFARM extends MachConcreteNode implements MachNode { DivFARM( Node divf) { super(divf); } - - // Register mask allowed on input i. - @Override public RegMask regmap(int i) { assert i==1 || i==2; return arm.DMASK; } - // Register mask allowed as a result. 0 for no register. + @Override public String op() { return "divf"; } + @Override public RegMask regmap(int i) { return arm.DMASK; } @Override public RegMask outregmap() { return arm.DMASK; } - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); - } - - + // FDIV (scalar) + @Override public void encoding( Encoding enc ) { arm.f_scalar(enc,this,arm.OPF_DIV); } // General form: "VDIF = dst /= src" @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" / ").p(code.reg(in(2))); } - - @Override public String op() { return "divf"; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/FloatARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/FloatARM.java index 7fafc31c..6de65b03 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/FloatARM.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/FloatARM.java @@ -2,28 +2,36 @@ package com.seaofnodes.simple.node.cpus.arm; import com.seaofnodes.simple.SB; import com.seaofnodes.simple.Utils; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.ConstantNode; import com.seaofnodes.simple.node.MachNode; -import java.io.ByteArrayOutputStream; +import com.seaofnodes.simple.type.TypeFloat; //FMOV (scalar, immediate) //Floating-point move immediate. -public class FloatARM extends ConstantNode implements MachNode { +public class FloatARM extends ConstantNode implements MachNode, RIPRelSize { FloatARM(ConstantNode con) { super(con); } - @Override public String op() { return "fld"; } - // Register mask allowed on input i. 0 for no register. + @Override public String op() { return "ld8"; } @Override public RegMask regmap(int i) { return null; } - // General int registers @Override public RegMask outregmap() { return arm.DMASK; } - @Override public boolean isClone() { return true; } @Override public FloatARM copy() { return new FloatARM(this); } - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); + @Override public void encoding( Encoding enc ) { + enc.largeConstant(this,_con,0,-1/*TODO: ARM-style ELF patching*/); + short dst = (short)(enc.reg(this) - arm.D_OFFSET); + double d = ((TypeFloat)_con).value(); + long x = Double.doubleToRawLongBits(d); + enc.add4(arm.load_pc(arm.OPF_ARM, 0, dst)); + } + + // Delta is from opcode start. + @Override public byte encSize(int delta) { return 4; } + + // Delta is from opcode start + @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { + short dst = (short)(enc.reg(this) - arm.D_OFFSET); + enc.patch4(opStart, arm.load_pc(arm.OPF_ARM, delta, dst)); } // Human-readable form appended to the SB. Things like the encoding, @@ -31,6 +39,7 @@ public class FloatARM extends ConstantNode implements MachNode { // Just something like "ld4\tR17=[R18+12] // Load array base". // General form: "op\tdst=src+src" @Override public void asm(CodeGen code, SB sb) { - _con.print(sb.p(code.reg(this)).p(" #")); + _con.print(sb.p(code.reg(this)).p(" = #")); } + } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/FunARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/FunARM.java index 0134911f..32777f03 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/FunARM.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/FunARM.java @@ -1,21 +1,22 @@ package com.seaofnodes.simple.node.cpus.arm; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.SB; import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.FunNode; import com.seaofnodes.simple.node.MachNode; -import java.io.ByteArrayOutputStream; -public class FunARM extends FunNode implements MachNode{ - FunARM(FunNode fun) {super(fun);} - // Register mask allowed on input i. 0 for no register. - @Override public RegMask regmap(int i) { return null; } - // Register mask allowed as a result. 0 for no register. - @Override public RegMask outregmap() { return null; } - - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); +public class FunARM extends FunNode implements MachNode { + FunARM(FunNode fun) { super(fun); } + @Override public void computeFrameAdjust(CodeGen code, int maxReg) { + super.computeFrameAdjust(code,maxReg); + if( _hasCalls ) // If non-leaf, pad to 16b + _frameAdjust = ((_frameAdjust+8) & -16); + } + @Override public void encoding( Encoding enc ) { + int sz = _frameAdjust; + if( sz == 0 ) return; // Skip if no frame adjust + if( sz >= 1L<<12 ) throw Utils.TODO(); + enc.add4(arm.imm_inst(arm.OPI_ADD, -sz&0xFFF, arm.RSP, arm.RSP)); } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/I2F8ARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/I2F8ARM.java index b106ab8a..5ecc98b6 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/I2F8ARM.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/I2F8ARM.java @@ -1,29 +1,25 @@ package com.seaofnodes.simple.node.cpus.arm; import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.TypeInteger; -import java.io.ByteArrayOutputStream; -// VCVT (between floating-point and integer) public class I2F8ARM extends MachConcreteNode implements MachNode { - I2F8ARM(Node i2f8) {super(i2f8);} - @Override public String op() { return "i2f8"; } - - // Register mask allowed on input i. - @Override public RegMask regmap(int i) { assert i==1; return arm.RMASK; } - // Register mask allowed as a result. 0 for no register. + I2F8ARM(Node i2f8) { super(i2f8); } + @Override public String op() { return "cvt"; } + @Override public RegMask regmap(int i) { return arm.RMASK; } @Override public RegMask outregmap() { return arm.DMASK; } - - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); + @Override public void encoding( Encoding enc ) { + // SCVTF + short self = (short)(enc.reg(this )-arm.D_OFFSET); + short reg1 = enc.reg(in(1)); + int body = arm.float_cast(arm.OP_FLOAT_C, 1, reg1, self); + enc.add4(body); } // General form: "i2f8 (flt)int_value" @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(this)).p(" = ").p("(flt)").p(code.reg(in(1))); } + } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/IntARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/IntARM.java index 1f81300e..0a07b07a 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/IntARM.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/IntARM.java @@ -1,30 +1,59 @@ package com.seaofnodes.simple.node.cpus.arm; import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.ConstantNode; import com.seaofnodes.simple.node.MachNode; import com.seaofnodes.simple.type.Type; import com.seaofnodes.simple.type.TypeInteger; -import java.io.ByteArrayOutputStream; // Integer constants public class IntARM extends ConstantNode implements MachNode { IntARM( ConstantNode con ) { super(con); } - @Override public String op() { return "ldi"; } - // Register mask allowed on input i. 0 for no register. + @Override public String op() { return "ldi"; } @Override public RegMask regmap(int i) { return null; } - // General int registers @Override public RegMask outregmap() { return arm.WMASK; } @Override public boolean isClone() { return true; } @Override public IntARM copy() { return new IntARM(this); } - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); + @Override public void encoding( Encoding enc ) { + short self = enc.reg(this); + long x = _con==Type.NIL ? 0 : ((TypeInteger)_con).value(); + int nb0 = 0; + int nb1 = 0; + // Count number of 0000 and FFFF blocks + for (int i=0; i<64; i+=16) { + int block = (int)(x >> i) & 0xFFFF; + if (block == 0) nb0++; + if (block == 0xFFFF) nb1++; + } + int pattern; + int op; + if(nb0 >= nb1) { + // More 0 blocks then F blocks, use movz + pattern = 0; + op = arm.OP_MOVZ; + } else { + // More F blocks then 0 blocks, use movn + pattern = 0xFFFF; + op = arm.OP_MOVN; + } + int invert = pattern; + for (int i=0; i<4; i++) { + int block = (int)x & 0xFFFF; + x >>= 16; + if (block != pattern) { + enc.add4(arm.mov(op, i, block ^ invert, self)); + op = arm.OP_MOVK; + invert = 0; + } + } + if (op != arm.OP_MOVK) { + // All blocks are the same, special case + enc.add4(arm.mov(op, 0, 0, self)); + } } // Human-readable form appended to the SB. Things like the encoding, diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/LoadARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/LoadARM.java index 0d59cc47..44c13321 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/LoadARM.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/LoadARM.java @@ -1,12 +1,10 @@ package com.seaofnodes.simple.node.cpus.arm; - import com.seaofnodes.simple.SB; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.LoadNode; import com.seaofnodes.simple.node.Node; - +import com.seaofnodes.simple.type.TypeFloat; // Load memory addressing on ARM // Support imm, reg(direct), or reg+off(indirect) addressing @@ -14,24 +12,28 @@ import com.seaofnodes.simple.node.Node; // idx = null // off = off - offset added to base -public class LoadARM extends MemOpARM{ - LoadARM(LoadNode ld,Node base, Node idx, int off) { +public class LoadARM extends MemOpARM { + LoadARM(LoadNode ld, Node base, Node idx, int off) { super(ld, base, idx, off, 0); } - - @Override public RegMask regmap(int i) { - return arm.RMASK; - } - - // Wide mask loads both ints and floats; encoding varies. + @Override public String op() { return "ld"+_sz; } @Override public RegMask outregmap() { return arm.MEM_MASK; } + private static final int[] OP_LOADS = new int[]{ arm.OP_LOAD_IMM_8, arm.OP_LOAD_IMM_16, arm.OP_LOAD_IMM_32, arm.OP_LOAD_IMM_64, }; + private int imm_op() { + return _declaredType == TypeFloat.F32 ? arm.OPF_LOAD_IMM_32 + : _declaredType == TypeFloat.F64 ? arm.OPF_LOAD_IMM_64 + : OP_LOADS[_declaredType.log_size()]; + } - @Override public String op() { return "ld"+_sz; } + private static final int[] OP_LOAD_RS = new int[]{ arm.OP_LOAD_R_8 , arm.OP_LOAD_R_16 , arm.OP_LOAD_R_32 , arm.OP_LOAD_R_64, }; + // ldr(immediate - unsigned offset) | ldr(register) + @Override public void encoding( Encoding enc ) { + ldst_encode(enc, imm_op(), OP_LOAD_RS[_declaredType.log_size()], this, size()); + } @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(this)).p(","); asm_address(code,sb); } - } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/LslARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/LslARM.java index 6108c889..055d6dcc 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/LslARM.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/LslARM.java @@ -1,41 +1,19 @@ package com.seaofnodes.simple.node.cpus.arm; import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.Type; -import com.seaofnodes.simple.type.TypeInteger; -import java.io.ByteArrayOutputStream; -import java.util.BitSet; -import java.lang.StringBuilder; - // Logical Shift Left (register) public class LslARM extends MachConcreteNode implements MachNode { LslARM(Node asr) {super(asr);} - // Register mask allowed on input i. - // This is the normal calling convention - @Override public RegMask regmap(int i) { - // assert i==1; - return arm.RMASK; } - // Register mask allowed as a result. 0 for no register. - @Override public RegMask outregmap() { return arm.RMASK; } - - // Output is same register as input#1 - @Override public int twoAddress() { return 1; } + @Override public String op() { return "shr"; } + @Override public RegMask regmap(int i) { return arm.RMASK; } + @Override public RegMask outregmap() { return arm.WMASK; } - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); - } - - // General form - // General form: "lsl rd, rs1, rs2" + @Override public void encoding( Encoding enc ) { arm.shift_reg(enc,this,arm.OP_LSL); } @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" << ").p(code.reg(in(2))); } - @Override public String op() { return "lsl"; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/LslIARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/LslIARM.java index 7a7014f4..94d3e25b 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/LslIARM.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/LslIARM.java @@ -1,42 +1,33 @@ package com.seaofnodes.simple.node.cpus.arm; - -import com.seaofnodes.simple.*; +import com.seaofnodes.simple.SB; import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.Encoding; import com.seaofnodes.simple.codegen.RegMask; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.Type; -import com.seaofnodes.simple.type.TypeInteger; -import java.io.ByteArrayOutputStream; -import java.util.BitSet; -import java.lang.StringBuilder; - +import com.seaofnodes.simple.node.MachConcreteNode; +import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.node.Node; -// Logical Shift Left (immediate) public class LslIARM extends MachConcreteNode implements MachNode { - final TypeInteger _ti; - LslIARM(Node lsli, TypeInteger ti) {super(lsli); _inputs.pop(); _ti = ti;} - - // Register mask allowed on input i. - // This is the normal calling convention - @Override public RegMask regmap(int i) { - // assert i==1; - return arm.RMASK; } - - // Register mask allowed as a result. 0 for no register. - @Override public RegMask outregmap() { return arm.RMASK; } - - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); + final int _imm; + LslIARM(Node lsl, int imm) { + super(lsl); + _inputs.pop(); + _imm = imm; } - - // General form - // General form: "lsli rd, rs1, imm" + @Override public String op() { return "lsli"; } + @Override public RegMask regmap(int i) { return arm.RMASK; } + @Override public RegMask outregmap() { return arm.WMASK; } + @Override public void encoding( Encoding enc ) { + short rd = enc.reg(this); + short rn = enc.reg(in(1)); + assert _imm > 0; + // UBFM <Xd>, <Xn>, #(-<shift> MOD 64), #(63-<shift>) + // immr must be (-<shift> MOD 64) = 64 - shift + enc.add4(arm.imm_shift(arm.OPI_LSL, 64 - _imm, (64 - _imm) - 1, rn, rd)); + } + // General form: "lsli rd = rs1 << imm" @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" << #"); - _ti.print(sb); + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" << #").p(_imm); } - - @Override public String op() { return "lsli"; } -} +}
\ No newline at end of file diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/LsrARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/LsrARM.java index e6168dee..a691e0fe 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/LsrARM.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/LsrARM.java @@ -1,40 +1,19 @@ package com.seaofnodes.simple.node.cpus.arm; - import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.Type; -import com.seaofnodes.simple.type.TypeInteger; -import java.io.ByteArrayOutputStream; -import java.util.BitSet; -import java.lang.StringBuilder; // Logical Shift Right (register) public class LsrARM extends MachConcreteNode implements MachNode { LsrARM(Node asr) {super(asr);} - // Register mask allowed on input i. - // This is the normal calling convention - @Override public RegMask regmap(int i) { - // assert i==1; - return arm.RMASK; } - // Register mask allowed as a result. 0 for no register. - @Override public RegMask outregmap() { return arm.RMASK; } - - // Output is same register as input#1 - @Override public int twoAddress() { return 1; } - - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); - } + @Override public String op() { return "shr"; } + @Override public RegMask regmap(int i) { return arm.RMASK; } + @Override public RegMask outregmap() { return arm.WMASK; } - // General form - // General form: "lsr rd, rs1, rs2" + @Override public void encoding( Encoding enc ) { arm.shift_reg(enc,this,arm.OP_LSR); } @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" >>> ").p(code.reg(in(2))); } - @Override public String op() { return "lsr"; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/LsrIARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/LsrIARM.java index 8fb20cb2..094cd30f 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/LsrIARM.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/LsrIARM.java @@ -2,40 +2,28 @@ package com.seaofnodes.simple.node.cpus.arm; import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.Type; -import com.seaofnodes.simple.type.TypeInteger; -import java.io.ByteArrayOutputStream; -import java.util.BitSet; -import java.lang.StringBuilder; -// Logical Shift Right (immediate) -public class LsrIARM extends MachConcreteNode implements MachNode{ - final TypeInteger _ti; - LsrIARM(Node lsri, TypeInteger ti) {super(lsri); _inputs.pop(); _ti = ti;} - - // Register mask allowed on input i. - // This is the normal calling convention - @Override public RegMask regmap(int i) { - // assert i==1; - return arm.RMASK; } - - // Register mask allowed as a result. 0 for no register. - @Override public RegMask outregmap() { return arm.RMASK; } - - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); +// logical right shift immediate +public class LsrIARM extends MachConcreteNode implements MachNode { + final int _imm; + LsrIARM(Node lsr, int imm) { + super(lsr); + _inputs.pop(); + _imm = imm; } - - // General form - // General form: "lsrl rd, rs1, imm" + @Override public String op() { return "lsri"; } + @Override public RegMask regmap(int i) { return arm.RMASK; } + @Override public RegMask outregmap() { return arm.WMASK; } + @Override public void encoding( Encoding enc ) { + short rd = enc.reg(this); + short rn = enc.reg(in(1)); + assert _imm > 0; + enc.add4(arm.imm_shift(arm.OPI_LSR,_imm, 0b111111, rn,rd)); + } + // General form: "lsri rd = rs1 >>> imm" @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" >>> #"); - _ti.print(sb); + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" >>> #").p(_imm); } - - @Override public String op() { return "lsrl"; } -} +}
\ No newline at end of file diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/MemOpARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/MemOpARM.java index adffa501..6d8344a2 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/MemOpARM.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/MemOpARM.java @@ -1,57 +1,68 @@ package com.seaofnodes.simple.node.cpus.arm; import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; import com.seaofnodes.simple.type.*; -import java.io.ByteArrayOutputStream; + import java.lang.StringBuilder; import java.util.BitSet; -public class MemOpARM extends MemOpNode implements MachNode { - final int _off; // Limit 32 bits - final int _imm; // Limit 32 bits +public abstract class MemOpARM extends MemOpNode implements MachNode { + final int _off; // Limit 9 bits sized, or (13 bits<<logsize) unsigned + final int _imm; // Limit ? bits final char _sz = (char)('0'+(1<<_declaredType.log_size())); - MemOpARM(MemOpNode mop, Node base, Node idx, int off, int imm) { + MemOpARM(MemOpNode mop, Node ptr, Node idx, int off, int imm) { super(mop,mop); - assert base._type instanceof TypeMemPtr && !(base instanceof AddNode); - assert ptr() == base; - _inputs.setX(3,idx); + assert ptr._type instanceof TypeMemPtr && !(ptr instanceof AddNode); + assert ptr() == ptr; + _inputs.setX(2, ptr); + _inputs.setX(3, idx); _off = off; _imm = imm; } // Store-based flavors have a value edge - MemOpARM( MemOpNode mop, Node base, Node idx, int off, int imm, Node val ) { - this(mop,base,idx,off,imm); + MemOpARM( MemOpNode mop, Node ptr, Node idx, int off, int imm, Node val ) { + this(mop,ptr,idx,off,imm); _inputs.setX(4,val); } - Node idx() { return in(3); } - Node val() { return in(4); } // Only for stores, includin + @Override public String label() { return op();} + Node val() { return in(4); } // Only for stores @Override public StringBuilder _printMach(StringBuilder sb, BitSet visited) { return sb.append(".").append(_name); } - @Override public String label() { return op(); } @Override public Type compute() { throw Utils.TODO(); } @Override public Node idealize() { throw Utils.TODO(); } + int size() { return 1<<_declaredType.log_size(); } - - // Register mask allowed on input i. + // Wider mask to store both GPRs and FPRs @Override public RegMask regmap(int i) { - throw Utils.TODO(); + // 0 - ctrl + if( i==1 ) return null; // memory + if( i==2 ) return arm.RMASK; // ptr/base + if( i==3 ) return arm.RMASK; // off/index + if( i==4 ) return arm.RMASK; // value + return null; // Anti-dependence } - // Register mask allowed as a result. 0 for no register. - @Override public RegMask outregmap() { throw Utils.TODO(); } - @Override public int encoding(ByteArrayOutputStream bytes) { throw Utils.TODO(); } + // Shared encoding for loads and stores(int and float) + public void ldst_encode( Encoding enc, int opcode_imm, int opcode_reg, Node xval, int size) { + short ptr = enc.reg(ptr()); + short off = enc.reg(off()); + short val = enc.reg(xval); + int body = off() == null + ? arm.load_str_imm(opcode_imm, _off, ptr, val, size) + : arm.indr_adr(opcode_reg, off, arm.STORE_LOAD_OPTION.SXTX, 0, ptr, val); + enc.add4(body); + } SB asm_address(CodeGen code, SB sb) { sb.p("[").p(code.reg(ptr())).p("+"); - if( idx() != null ) sb.p(code.reg(idx())); + if( off() != null ) sb.p(code.reg(off())); else sb.p(_off); return sb.p("]"); } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/MulARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/MulARM.java index 582b97d4..3514db9b 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/MulARM.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/MulARM.java @@ -1,34 +1,19 @@ package com.seaofnodes.simple.node.cpus.arm; import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.TypeInteger; -import java.io.ByteArrayOutputStream; - // mulh signed multiply instruction(no-imm form) -public class MulARM extends MachConcreteNode implements MachNode{ +public class MulARM extends MachConcreteNode implements MachNode { MulARM(Node mul) {super(mul);} + @Override public String op() { return "mul"; } + @Override public RegMask regmap(int i) { return arm.RMASK; } + @Override public RegMask outregmap() { return arm.WMASK; } - // Register mask allowed on input i. - @Override public RegMask regmap(int i) { assert i==1 || i==2; return arm.RMASK; } - // Register mask allowed as a result. 0 for no register. - @Override public RegMask outregmap() { return arm.RMASK; } - // Output is same register as input#1 - @Override public int twoAddress() { return 1; } - - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); - } - + @Override public void encoding( Encoding enc ) { arm.madd(enc,this,arm.OP_MUL,31); } // General form: "rd = rs1 * rs2" @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" * ").p(code.reg(in(2))); } - - @Override public String op() { return "mul"; } - } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/MulFARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/MulFARM.java index b71b412d..89dba172 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/MulFARM.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/MulFARM.java @@ -1,30 +1,18 @@ package com.seaofnodes.simple.node.cpus.arm; import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; -import java.io.ByteArrayOutputStream; public class MulFARM extends MachConcreteNode implements MachNode{ MulFARM(Node mulf) {super(mulf);} - - // Register mask allowed on input i. - @Override public RegMask regmap(int i) { assert i==1 || i==2; return arm.DMASK; } - // Register mask allowed as a result. 0 for no register. + @Override public String op() { return "mulf"; } + @Override public RegMask regmap(int i) { return arm.DMASK; } @Override public RegMask outregmap() { return arm.DMASK; } - - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); - } - + @Override public void encoding( Encoding enc ) { arm.f_scalar(enc,this,arm.OPF_MUL); } // Default on double precision for now(64 bits) // General form: "VMUL.f32 rd = src1 * src2 @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" * ").p(code.reg(in(2))); } - - @Override public String op() { return "mulf"; } - } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/MulIARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/MulIARM.java deleted file mode 100644 index c118b00a..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/MulIARM.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.seaofnodes.simple.node.cpus.arm; - -import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.TypeInteger; -import java.io.ByteArrayOutputStream; - -public class MulIARM extends MachConcreteNode implements MachNode{ - final TypeInteger _ti; - MulIARM(Node mul, TypeInteger ti) { - super(mul); - _inputs.pop(); - _ti = ti; - } - - // Register mask allowed on input i. - // This is the normal calling convention - @Override public RegMask regmap(int i) { - // assert i==1; - return arm.RMASK; } - - // Register mask allowed as a result. 0 for no register. - @Override public RegMask outregmap() { return arm.RMASK; } - - // Output is same register as input#1 - @Override public int twoAddress() { return 1; } - - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); - } - - // General form - // General form: "muli dst * #imm" - @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" * #"); - _ti.print(sb); - } - - @Override public String op() { return "muli"; } -} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/NegARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/NegARM.java new file mode 100644 index 00000000..b7c3c803 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/NegARM.java @@ -0,0 +1,25 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.node.MachConcreteNode; +import com.seaofnodes.simple.type.TypeInteger; + +public class NegARM extends MachConcreteNode implements MachNode { + NegARM(Node sub) { + super(sub); + //_inputs.pop(); + } + @Override public String op() { return "neg"; } + @Override public RegMask regmap(int i) { return arm.RMASK; } + @Override public RegMask outregmap() { return arm.WMASK; } + @Override public void encoding( Encoding enc ) { + // reverse subtract with immediate 0 + arm.imm_inst(enc,this, in(1), 0b00100110, 0); + } + // General form: "subi rd = rs1 - imm" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = -").p(code.reg(in(1))); + } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/NewARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/NewARM.java index bd88fa83..cb51eea2 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/NewARM.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/NewARM.java @@ -1,31 +1,36 @@ package com.seaofnodes.simple.node.cpus.arm; - import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; -import java.io.ByteArrayOutputStream; - -public class NewARM extends NewNode implements MachNode { +public class NewARM extends NewNode implements MachNode, RIPRelSize { // A pre-zeroed chunk of memory. NewARM(NewNode nnn) { super(nnn); } - // Size and pointer result in standard calling convention; null for all the - // memory aliases edges - @Override public RegMask regmap(int i) { return i == 1 ? arm. X0_MASK : null; } - @Override public RegMask outregmap(int i) { return i == 1 ? arm. X0_MASK : null; } - @Override public RegMask outregmap() { return null; } - - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); + @Override public void encoding( Encoding enc ) { + // BL(branch with link) + enc.external(this,"calloc"). + add4(arm.mov(arm.OP_MOVZ, 0, 1, 0)). // movz x0,#1 + add4(arm.b(arm.OP_CALL, 0)); } - // General form: "alloc #bytes" + // Patch is for running "new" in a JIT. + // Delta is from opcode start + @Override public byte encSize(int delta) { return 4*2; } + + // Patch is for running "new" in a JIT. + // Delta is from opcode start + @Override public void patch(Encoding enc, int opStart, int opLen, int delta ) { + // Negative patches are JIT emulator targets. + if( opStart+delta < 0 ) { + enc.patch4(opStart+4, arm.b_calloc(arm.OP_CALL, delta-4)); + } else { + throw Utils.TODO(); + } + } + // General form: "alloc #bytes PC" @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(size())); + sb.p("ldi x0=#1\n"); + sb.p("call #calloc"); } - - @Override public String op() { return "alloc"; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/NotARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/NotARM.java index 28ebd60d..5a8d882d 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/NotARM.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/NotARM.java @@ -1,26 +1,28 @@ package com.seaofnodes.simple.node.cpus.arm; import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.LRG; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.TypeInteger; -import java.io.ByteArrayOutputStream; import com.seaofnodes.simple.node.MachConcreteNode; public class NotARM extends MachConcreteNode implements MachNode{ NotARM(NotNode not) {super(not);} + @Override public String op() { return "not"; } @Override public RegMask regmap(int i) { return arm.RMASK; } - @Override public RegMask outregmap() { return arm.RMASK; } - - @Override public int twoAddress( ) { return 0; } - - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); + @Override public RegMask outregmap() { return arm.WMASK; } + @Override public RegMask killmap() { return arm.FLAGS_MASK; } + @Override public void encoding( Encoding enc ) { + // subs xzr, rs, #0 + // cset rd, eq // Set rd to 1 if rs == 0 (equal), else 0 + // subtracting zero from rs will just yield rs, it sets the zero flag and then it's used in cset + short self = enc.reg(this ); + short reg1 = enc.reg(in(1)); + int subs = arm.imm_inst(arm.OP_SUBS, 0, reg1, self); + enc.add4(subs); + int cset = arm.cond_set(arm.OP_CSET, 31, arm.COND.EQ, 63, reg1); + enc.add4(cset); } @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(this)); } - @Override public String op() { return "not"; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/OrARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/OrARM.java index 12ea8265..b5cfa8cf 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/OrARM.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/OrARM.java @@ -1,42 +1,20 @@ package com.seaofnodes.simple.node.cpus.arm; import com.seaofnodes.simple.SB; -import com.seaofnodes.simple.Utils; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; -import com.seaofnodes.simple.node.MachConcreteNode; -import com.seaofnodes.simple.node.MachNode; -import com.seaofnodes.simple.node.Node; - -import java.io.ByteArrayOutputStream; +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; // Not using the ORRS variant. public class OrARM extends MachConcreteNode implements MachNode { - OrARM(Node and) { - super(and); - } - - // Register mask allowed on input i. - // This is the normal calling convention - @Override public RegMask regmap(int i) { - assert i==1 || i==2; - return arm.RMASK; - } - - // Register mask allowed as a result. 0 for no register. - @Override public RegMask outregmap() { return arm.RMASK; } - - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); - } - - // General form + OrARM(Node and) { super(and); } + @Override public String op() { return "or"; } + @Override public String glabel() { return "|"; } + @Override public RegMask regmap(int i) { return arm.RMASK; } + @Override public RegMask outregmap() { return arm.WMASK; } + @Override public void encoding( Encoding enc ) { arm.r_reg(enc,this,arm.OP_OR); } // General form: #rd = rs1 & rs2 @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" | ").p(code.reg(in(2))); } - @Override public String op() { return "orr"; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/OrIARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/OrIARM.java index f257a867..2a2d199a 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/OrIARM.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/OrIARM.java @@ -1,40 +1,24 @@ package com.seaofnodes.simple.node.cpus.arm; -import com.seaofnodes.simple.SB; -import com.seaofnodes.simple.Utils; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; -import com.seaofnodes.simple.node.MachConcreteNode; -import com.seaofnodes.simple.node.MachNode; -import com.seaofnodes.simple.node.Node; +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.Type; -import com.seaofnodes.simple.type.TypeInteger; -import java.io.ByteArrayOutputStream; - -public class OrIARM extends MachConcreteNode implements MachNode{ - final TypeInteger _ti; - OrIARM(Node or, TypeInteger ti) {super(or); _inputs.pop(); _ti = ti;} - // Register mask allowed on input i. - // This is the normal calling convention - @Override public RegMask regmap(int i) { - assert i==1 || i==2; - return arm.RMASK; +public class OrIARM extends MachConcreteNode implements MachNode { + final int _imm; + OrIARM(Node or, int imm) { + super(or); + _inputs.pop(); + _imm = imm; } - // Register mask allowed as a result. 0 for no register. - @Override public RegMask outregmap() { return arm.RMASK; } - - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); + @Override public String op() { return "ori"; } + @Override public RegMask regmap(int i) { return arm.RMASK; } + @Override public RegMask outregmap() { return arm.WMASK; } + @Override public void encoding( Encoding enc ) { + arm.imm_inst_n(enc, this, in(1), arm.OPI_OR, _imm); } - - // General form - // General form: "rd = rs1 | imm" + // General form: "ori rd = rs1 | imm" @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" | #"); - _ti.print(sb); + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" | #").p(_imm); } - - @Override public String op() { return "ori"; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/ParmARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/ParmARM.java index c3fa132c..b419fe0a 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/ParmARM.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/ParmARM.java @@ -1,25 +1,17 @@ package com.seaofnodes.simple.node.cpus.arm; -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.RegMask; import com.seaofnodes.simple.node.MachNode; import com.seaofnodes.simple.node.ParmNode; -import java.io.ByteArrayOutputStream; public class ParmARM extends ParmNode implements MachNode { final RegMask _rmask; ParmARM(ParmNode parm) { super(parm); - // Assume int - _rmask = arm.callInMask(fun().sig(),_idx); + _rmask = arm.callInMask(fun().sig(),_idx,0); } - // Register mask allowed on input i. 0 for no register. @Override public RegMask regmap(int i) { return null; } - // Register mask allowed as a result. Calling convention register @Override public RegMask outregmap() { return _rmask; } - - // Encoding is appended into the byte array. Returns size - @Override public int encoding(ByteArrayOutputStream bytes) { return 0; } + @Override public void encoding( Encoding enc ) { } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/ProjARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/ProjARM.java index b2add015..4b7d6f13 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/ProjARM.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/ProjARM.java @@ -1,23 +1,15 @@ package com.seaofnodes.simple.node.cpus.arm; -import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.codegen.Encoding; import com.seaofnodes.simple.codegen.RegMask; import com.seaofnodes.simple.node.MachNode; import com.seaofnodes.simple.node.ProjNode; -import java.io.ByteArrayOutputStream; public class ProjARM extends ProjNode implements MachNode { ProjARM( ProjNode p ) { super(p); } - - // Register mask allowed on input i. 0 for no register. @Override public RegMask regmap(int i) { return null; } - // Register mask allowed as a result. 0 for no register. @Override public RegMask outregmap() { return ((MachNode)in(0)).outregmap(_idx); } - - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); - } + @Override public void encoding( Encoding enc ) { } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/RetARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/RetARM.java index c7d0dd4c..2136ebf5 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/RetARM.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/RetARM.java @@ -1,49 +1,15 @@ package com.seaofnodes.simple.node.cpus.arm; import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; -import com.seaofnodes.simple.node.FunNode; -import com.seaofnodes.simple.node.ReturnNode; -import com.seaofnodes.simple.node.MachNode; -import com.seaofnodes.simple.type.TypeFloat; - -import java.io.ByteArrayOutputStream; - +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; public class RetARM extends ReturnNode implements MachNode { RetARM(ReturnNode ret, FunNode fun) { super(ret, fun); fun.setRet(this); } - - // Register mask allowed on input i. - // This is the normal calling convention - @Override public RegMask regmap(int i) { return arm.retMask(_fun.sig(),i); } - // Register mask allowed as a result. 0 for no register. - @Override public RegMask outregmap() { return null; } - - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); - } - - @Override public void asm(CodeGen code, SB sb) { - // Post code-gen, just print the "ret" - if( code._phase.ordinal() <= CodeGen.Phase.RegAlloc.ordinal() ) - // Prints return reg (either X0 or D0), RPC (always R30) and - // then the callee-save registers. - for( int i=2; i<nIns(); i++ ) - sb.p(code.reg(in(i))).p(" "); - - // Post-allocation, if we did not get the expected return register, print what we got - else if( code._regAlloc.regnum(in(3)) != arm.X30 ) - sb.p("[").p(code.reg(in(3))).p("]"); + @Override public void encoding( Encoding enc ) { + int frameAdjust = ((FunARM)fun())._frameAdjust; + if( frameAdjust > 0 ) + enc.add4(arm.imm_inst(arm.OPI_ADD, (frameAdjust*-8)&0xFFF, arm.RSP, arm.RSP)); + enc.add4(arm.ret(arm.OP_RET)); } - - // Correct Nodes outside the normal edges - @Override public void postSelect() { - FunNode fun = (FunNode)rpc().in(0); - _fun = fun; - fun.setRet(this); - } - - @Override public String op() { return "ret"; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/SetARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/SetARM.java index c3945d1a..246e482a 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/SetARM.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/SetARM.java @@ -1,20 +1,13 @@ package com.seaofnodes.simple.node.cpus.arm; import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.Type; -import com.seaofnodes.simple.type.TypeInteger; -import java.io.ByteArrayOutputStream; - // conditional set? // e.g CSET, stores flag info into GPRS after compare - public class SetARM extends MachConcreteNode implements MachNode { final String _bop; // One of <,<=,== - SetARM(Node cmp, String bop) { super(cmp); _inputs.setLen(1); // Pop the cmp inputs @@ -22,13 +15,13 @@ public class SetARM extends MachConcreteNode implements MachNode { _inputs.push(cmp); _bop = bop; } - + @Override public String op() { return "set"+_bop; } @Override public RegMask regmap(int i) { assert i==1; return arm.FLAGS_MASK; } - @Override public RegMask outregmap() { return arm.RMASK; } + @Override public RegMask outregmap() { return arm.WMASK; } - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); + @Override public void encoding( Encoding enc ) { + int body = arm.cset(arm.OP_CSET,0b11111, arm.make_condition(_bop), 0b011111, enc.reg(this)); + enc.add4(body); } @Override public void asm(CodeGen code, SB sb) { @@ -36,6 +29,4 @@ public class SetARM extends MachConcreteNode implements MachNode { String src = code.reg(in(1)); if( src!="flags" ) sb.p(" = ").p(src); } - - @Override public String op() { return "set"+_bop; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitARM.java index 85771842..62bb75aa 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitARM.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitARM.java @@ -1,22 +1,66 @@ package com.seaofnodes.simple.node.cpus.arm; +import com.seaofnodes.simple.SB; import com.seaofnodes.simple.Utils; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.FunNode; import com.seaofnodes.simple.node.Node; import com.seaofnodes.simple.node.SplitNode; -import java.io.ByteArrayOutputStream; +import com.seaofnodes.simple.node.cpus.riscv.riscv; public class SplitARM extends SplitNode { SplitARM(String kind, byte round) { super(kind,round,new Node[2]);} - - // Register mask allowed on input i. @Override public RegMask regmap(int i) { return arm.SPLIT_MASK; } - - // Register mask allowed as a result. 0 for no register. @Override public RegMask outregmap() { return arm.SPLIT_MASK; } - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); + @Override public void encoding( Encoding enc ) { + short dst = enc.reg(this ); + short src = enc.reg(in(1)); + + boolean dstX = dst >= arm.D_OFFSET; + boolean srcX = src >= arm.D_OFFSET; + + if (dst >= arm.MAX_REG) { + // store to sp + if(src >= arm.MAX_REG) { + throw Utils.TODO(); + } + int off = enc._fun.computeStackOffset(enc._code,dst); + if( srcX ) src -= arm.D_OFFSET; + enc.add4(arm.load_str_imm(arm.OP_STORE_IMM_64, off, arm.RSP, src, 8)); + return; + } + + if(src >= arm.MAX_REG) { + // Load from SP + int off = enc._fun.computeStackOffset(enc._code,src); + if( dstX ) dst -= arm.D_OFFSET; + enc.add4(arm.load_str_imm(arm.OP_LOAD_IMM_64, off, arm.RSP, dst, 8)); + return; + } + + // pick opcode based on regs + if(!dstX && !srcX) { + // GPR->GPR + enc.add4(arm.mov_reg(arm.OP_MOV, src, dst)); + } else if(dstX && srcX) { + // FPR->FPR + // fmov reg + enc.add4(arm.f_mov_reg(arm.OP_FMOV_REG, src - arm.D_OFFSET,dst - arm.D_OFFSET)); + } else if(!srcX && dstX) { + // GPR->FPR + // FMOV(general) 64 bits to DOUBLE-PRECISION + enc.add4(arm.f_mov_general(arm.OP_FMOV, 0b01, 0, 0b111, src, dst - arm.D_OFFSET)); + } else if( srcX && !dstX) { + //FPF->GPR + // FMOV(general) DOUBLE-PRECISION to 64 bits + enc.add4(arm.f_mov_general(arm.OP_FMOV, 0b01, 0, 0b110, src - arm.D_OFFSET, dst)); + } + } + + // General form: "mov dst = src" + @Override public void asm(CodeGen code, SB sb) { + FunNode fun = code._encoding==null ? null : code._encoding._fun; + sb.p(code.reg(this,fun)).p(" = ").p(code.reg(in(1),fun)); } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/StoreARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/StoreARM.java index 49045b28..688d8731 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/StoreARM.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/StoreARM.java @@ -1,49 +1,40 @@ package com.seaofnodes.simple.node.cpus.arm; import com.seaofnodes.simple.SB; -import com.seaofnodes.simple.Utils; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; -import com.seaofnodes.simple.node.MachConcreteNode; -import com.seaofnodes.simple.node.MachNode; -import com.seaofnodes.simple.node.Node; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.StoreNode; -import java.io.ByteArrayOutputStream; -import java.util.BitSet; +import com.seaofnodes.simple.node.Node; +import com.seaofnodes.simple.type.TypeFloat; // Store memory addressing on ARM // Support imm, reg(direct), or reg+off(indirect) addressing -// Base = base - base pointer, offset is added to base -// idx = null -// off = off - offset added to base) -// imm = imm - immediate value to store -// val = Node of immediate value to store(null if its a constant immediate) +// base - base pointer, offset is added to base +// null - index never allowed (no [reg+reg] mode) +// off - offset added to base +// imm - immediate value to store, only if val is null +// val - value to store or null //e.g s.cs[0] = 67; // C // base = s.cs, off = 4, imm = 67, val = null public class StoreARM extends MemOpARM { - StoreARM(StoreNode st, Node base, Node idx, int off, int imm, Node val) { - super(st, base, idx, off, imm, val); + StoreARM(StoreNode st, Node base, Node idx, int off, Node val) { + super(st, base, idx, off, 0, val); } - @Override public String op() { return "st"+_sz; } - - // Wider mask to store both GPRs and FPRs - @Override public RegMask regmap(int i) { - if( i==1 ) return arm.MEM_MASK; - if( i==2 ) return arm.RMASK; - if( i==3 ) return arm.RMASK; - if( i==4 ) return arm.RMASK; - throw Utils.TODO(); - } - // Register mask allowed as a result. 0 for no register. @Override public RegMask outregmap() { return null; } - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); + private static final int[] OP_STORES = new int[]{ arm.OP_STORE_IMM_8, arm.OP_STORE_IMM_16, arm.OP_STORE_IMM_32, arm.OP_STORE_IMM_64, }; + private int imm_op() { + return _declaredType == TypeFloat.F32 ? arm.OPF_STORE_IMM_32 + : _declaredType == TypeFloat.F64 ? arm.OPF_STORE_IMM_64 + : OP_STORES[_declaredType.log_size()]; } + private static final int[] OP_STORE_RS = new int[]{ arm.OP_STORE_R_8, arm.OP_STORE_R_16, arm.OP_STORE_R_32, arm.OP_STORE_R_64, }; + + @Override public void encoding( Encoding enc ) { + ldst_encode(enc, imm_op(), OP_STORE_RS[_declaredType.log_size()], val(), size()); + } @Override public void asm(CodeGen code, SB sb) { asm_address(code,sb).p(","); if( val()==null ) sb.p("#").p(_imm); diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/SubARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/SubARM.java index 27555b53..552b46df 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/SubARM.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/SubARM.java @@ -1,30 +1,19 @@ package com.seaofnodes.simple.node.cpus.arm; -import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.SB; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.TypeInteger; -import java.io.ByteArrayOutputStream; - public class SubARM extends MachConcreteNode implements MachNode { - SubARM(Node sub) {super(sub);} - - // Register mask allowed on input i. - @Override public RegMask regmap(int i) { assert i==1 || i==2; return arm.RMASK; } - // Register mask allowed as a result. 0 for no register. - @Override public RegMask outregmap() { return arm.RMASK; } + SubARM(Node sub) { super(sub); } + @Override public String op() { return "sub"; } + @Override public RegMask regmap(int i) { return arm.RMASK; } + @Override public RegMask outregmap() { return arm.WMASK; } - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); - } + @Override public void encoding( Encoding enc ) { arm.r_reg(enc,this,arm.OP_SUB); } // General form: "sub # rd = rs1 - rs2" @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" - ").p(code.reg(in(2))); } - - @Override public String op() { return "sub"; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/SubFARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/SubFARM.java index 8ae05b40..0d8d3c10 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/SubFARM.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/SubFARM.java @@ -1,32 +1,18 @@ package com.seaofnodes.simple.node.cpus.arm; - -import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.SB; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.TypeInteger; -import java.io.ByteArrayOutputStream; -public class SubFARM extends MachConcreteNode implements MachNode{ +public class SubFARM extends MachConcreteNode implements MachNode { SubFARM(Node subf) {super(subf);} - - // Register mask allowed on input i. - @Override public RegMask regmap(int i) { assert i==1 || i==2; return arm.DMASK; } - // Register mask allowed as a result. 0 for no register. + @Override public String op() { return "subf"; } + @Override public RegMask regmap(int i) { return arm.DMASK; } @Override public RegMask outregmap() { return arm.DMASK; } - - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); - } - + @Override public void encoding( Encoding enc ) { arm.f_scalar(enc,this,arm.OPF_SUB); } // Default on double precision for now(64 bits) // General form: "vsub.f32 rd = src1 + src2 @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" - ").p(code.reg(in(2))); } - - @Override public String op() { return "subf"; } - } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/SubIARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/SubIARM.java index d19210cb..b882c5a4 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/SubIARM.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/SubIARM.java @@ -1,42 +1,29 @@ package com.seaofnodes.simple.node.cpus.arm; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.MachConcreteNode; import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.LRG; -import com.seaofnodes.simple.codegen.RegMask; import com.seaofnodes.simple.node.*; import com.seaofnodes.simple.type.TypeInteger; -import java.io.ByteArrayOutputStream; public class SubIARM extends MachConcreteNode implements MachNode { - final TypeInteger _ti; - SubIARM(Node sub, TypeInteger ti) { + final int _imm; + SubIARM(Node sub, int imm) { super(sub); _inputs.pop(); - _ti = ti; + _imm = imm; } - - // Register mask allowed on input i. + @Override public String op() { return _imm == -1 ? "dec" : "subi"; } @Override public RegMask regmap(int i) { return arm.RMASK; } + @Override public RegMask outregmap() { return arm.WMASK; } - // Register mask allowed as a result. 0 for no register. - @Override public RegMask outregmap() { return arm.RMASK; } - - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); + @Override public void encoding( Encoding enc ) { + arm.imm_inst(enc,this, in(1), arm.OPI_SUB,_imm); } // General form: "subi rd = rs1 - imm" @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" - #"); - _ti.print(sb); + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" - #").p(_imm); } - - @Override public String op() { - return (_ti.value() == -1 ? "dec" : "subi"); - } - } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/TFPARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/TFPARM.java index cdb2b6ec..16b56553 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/TFPARM.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/TFPARM.java @@ -1,37 +1,53 @@ package com.seaofnodes.simple.node.cpus.arm; import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.ConstantNode; import com.seaofnodes.simple.node.MachNode; -import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.node.cpus.riscv.riscv; import com.seaofnodes.simple.type.TypeFunPtr; -import java.io.ByteArrayOutputStream; -public class TFPARM extends ConstantNode implements MachNode { +public class TFPARM extends ConstantNode implements MachNode, RIPRelSize { TFPARM( ConstantNode con ) { super(con); } @Override public String op() { return "ldx"; } - // Register mask allowed on input i. 0 for no register. @Override public RegMask regmap(int i) { return null; } - // General int registers @Override public RegMask outregmap() { return arm.WMASK; } - @Override public boolean isClone() { return true; } @Override public TFPARM copy() { return new TFPARM(this); } + @Override public void encoding( Encoding enc ) { + enc.relo(this); + short self = enc.reg(this); + // adrp x0, 0 + int adrp = arm.adrp(1,0, arm.OP_ADRP, 0,self); + // add x0, x0, 0 + enc.add4(adrp); + arm.imm_inst(enc,arm.OPI_ADD,0, 0); + } - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { + @Override public byte encSize(int delta) { + if( -(1L<<12) <= delta && delta < (1L<<12) ) return 4; throw Utils.TODO(); } - // Human-readable form appended to the SB. Things like the encoding, - // indentation, leading address or block labels not printed here. - // Just something like "ld4\tR17=[R18+12] // Load array base". - // General form: "op\tdst=src+src" + // Delta is from opcode start + @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { + short rpc = enc.reg(this); + if(opLen == 8 ) { + // opstart of add + int next = opStart + opLen; + int adrp_delta = delta >> 12; + // patch upper 20 bits via adrp + enc.patch4(opStart, arm.adrp(1, adrp_delta & 0b11, 0b10000, adrp_delta >> 2, rpc)); + // low 12 bits via add + enc.patch4(next, arm.imm_inst_l(arm.OPI_ADD, delta & 0xfff, rpc)); + } else { + // should not happen as one instruction is 4 byte, and TFP arm encodes 2. + throw Utils.TODO(); + } + } + @Override public void asm(CodeGen code, SB sb) { String reg = code.reg(this); _con.print(sb.p(reg).p(" #")); } - } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/UJmpARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/UJmpARM.java new file mode 100644 index 00000000..a69c3c7c --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/UJmpARM.java @@ -0,0 +1,48 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.Type; +import java.util.BitSet; + +// unconditional jump +public class UJmpARM extends CFGNode implements MachNode, RIPRelSize { + UJmpARM() { } + @Override public String op() { return "jmp"; } + @Override public String label() { return op(); } + @Override public StringBuilder _print1( StringBuilder sb, BitSet visited ) { + return sb.append("jmp "); + } + @Override public RegMask regmap(int i) {return null; } + @Override public RegMask outregmap() { return null; } + @Override public Type compute() { throw Utils.TODO(); } + @Override public Node idealize() { throw Utils.TODO(); } + @Override public void encoding( Encoding enc ) { + enc.jump(this,uctrl()); + int body = arm.b(arm.OP_UJMP, 0); + enc.add4(body); + } + + // Delta is from opcode start + @Override public byte encSize(int delta) { + if( -(1<<26) <= delta && delta < (1<<26) ) return 4; + // 2 word encoding needs a tmp register, must teach RA + throw Utils.TODO(); + } + + // Delta is from opcode start + @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { + if( opLen==4 ) { + enc.patch4(opStart,arm.b(arm.OP_UJMP, delta)); + } else { + throw Utils.TODO(); + } + } + + @Override public void asm(CodeGen code, SB sb) { + CFGNode target = uctrl(); + //assert target.nOuts() > 1; // Should optimize jmp to empty targets + sb.p(label(target)); + } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/XorARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/XorARM.java index fd127b18..ec86cfa5 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/XorARM.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/XorARM.java @@ -1,41 +1,19 @@ package com.seaofnodes.simple.node.cpus.arm; import com.seaofnodes.simple.SB; -import com.seaofnodes.simple.Utils; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; -import com.seaofnodes.simple.node.MachConcreteNode; -import com.seaofnodes.simple.node.MachNode; -import com.seaofnodes.simple.node.Node; -import com.seaofnodes.simple.type.TypeInteger; -import java.io.ByteArrayOutputStream; +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; public class XorARM extends MachConcreteNode implements MachNode{ - XorARM(Node xor) {super(xor); _inputs.pop();} - - // Register mask allowed on input i. - // This is the normal calling convention - @Override public RegMask regmap(int i) { - assert i==1 || i==2; - - return arm.RMASK; } - - // Register mask allowed as a result. 0 for no register. - @Override public RegMask outregmap() { return arm.RMASK; } - - // Output is same register as input#1 - @Override public int twoAddress() { return 1; } - - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); - } - - // General form + XorARM(Node xor) {super(xor);} + @Override public String op() { return "xor"; } + @Override public String glabel() { return "^"; } + @Override public RegMask regmap(int i) { return arm.RMASK; } + @Override public RegMask outregmap() { return arm.WMASK; } + @Override public void encoding( Encoding enc ) { arm.r_reg(enc,this,arm.OP_XOR); } // General form: "rd = x1 ^ x2" @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" ^ ").p(code.reg(in(2))); } - @Override public String op() { return "eor"; } -}
\ No newline at end of file +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/XorIARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/XorIARM.java index d230ea47..e2582d22 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/XorIARM.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/XorIARM.java @@ -1,43 +1,27 @@ package com.seaofnodes.simple.node.cpus.arm; -import com.seaofnodes.simple.SB; -import com.seaofnodes.simple.Utils; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; import com.seaofnodes.simple.node.MachConcreteNode; import com.seaofnodes.simple.node.MachNode; -import com.seaofnodes.simple.node.Node; -import com.seaofnodes.simple.type.TypeInteger; -import java.io.ByteArrayOutputStream; -public class XorIARM extends MachConcreteNode implements MachNode{ - final TypeInteger _ti; - XorIARM(Node xor, TypeInteger ti) {super(xor); _inputs.pop(); _ti = ti;} +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; - // Register mask allowed on input i. - // This is the normal calling convention - @Override public RegMask regmap(int i) { - assert i==1 || i==2; - - return arm.RMASK; } - - // Register mask allowed as a result. 0 for no register. - @Override public RegMask outregmap() { return arm.RMASK; } - - // Output is same register as input#1 - @Override public int twoAddress() { return 1; } - - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); +public class XorIARM extends MachConcreteNode implements MachNode { + final int _imm; + XorIARM(Node xor, int imm) { + super(xor); + _inputs.pop(); + _imm = imm; } - // General form - // General form: "rd = rs1 ^ imm" + @Override public String op() { return "xori"; } + @Override public RegMask regmap(int i) { return arm.RMASK; } + @Override public RegMask outregmap() { return arm.WMASK; } + + // General form: "xori rd = rs1 ^ imm" + @Override public void encoding( Encoding enc ) { arm.imm_inst_n(enc,this, in(1), arm.OPI_XOR,_imm); } @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" ^ #"); - _ti.print(sb); + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" ^ #").p(_imm); } - - @Override public String op() { return "eor"; } -}
\ No newline at end of file +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java index fd207157..3e39adc0 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java @@ -1,30 +1,34 @@ package com.seaofnodes.simple.node.cpus.arm; import com.seaofnodes.simple.Utils; -import com.seaofnodes.simple.codegen.LRG; -import com.seaofnodes.simple.codegen.Machine; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; import com.seaofnodes.simple.type.*; public class arm extends Machine { + public arm( CodeGen code ) { + if( !"SystemV".equals(code._callingConv) ) + throw new IllegalArgumentException("Unknown calling convention "+code._callingConv); + } - // X86-64 V2. Includes e.g. SSE4.2 and POPCNT. + // ARM64 @Override public String name() { return "arm"; } // GPR(S) - static final int X0 = 0, X1 = 1, X2 = 2, X3 = 3, X4 = 4, X5 = 5, X6 = 6, X7 = 7; - static final int X8 = 8, X9 = 9, X10 = 10, X11 = 11, X12 = 12, X13 = 13, X14 = 14, X15 = 15; - static final int X16 = 16, X17 = 17, X18 = 18, X19 = 19, X20 = 20, X21 = 21, X22 = 22, X23 = 23; - static final int X24 = 24, X25 = 25, X26 = 26, X27 = 27, X28 = 28, X29 = 29, X30 = 30, RSP = 31; + public static final int X0 = 0, X1 = 1, X2 = 2, X3 = 3, X4 = 4, X5 = 5, X6 = 6, X7 = 7; + public static final int X8 = 8, X9 = 9, X10 = 10, X11 = 11, X12 = 12, X13 = 13, X14 = 14, X15 = 15; + public static final int X16 = 16, X17 = 17, X18 = 18, X19 = 19, X20 = 20, X21 = 21, X22 = 22, X23 = 23; + public static final int X24 = 24, X25 = 25, X26 = 26, X27 = 27, X28 = 28, X29 = 29, X30 = 30, RSP = 31; // Floating point registers - static final int D0 = 32, D1 = 33, D2 = 34, D3 = 35, D4 = 36, D5 = 37, D6 = 38, D7 = 39; - static final int D8 = 40, D9 = 41, D10 = 42, D11 = 43, D12 = 44, D13 = 45, D14 = 46, D15 = 47; - static final int D16 = 48, D17 = 49, D18 = 50, D19 = 51, D20 = 52, D21 = 53, D22 = 54, D23 = 55; - static final int D24 = 56, D25 = 57, D26 = 58, D27 = 59, D28 = 60, D29 = 61, D30 = 62, D31 = 63; + public static final int D0 = 32, D1 = 33, D2 = 34, D3 = 35, D4 = 36, D5 = 37, D6 = 38, D7 = 39; + public static final int D8 = 40, D9 = 41, D10 = 42, D11 = 43, D12 = 44, D13 = 45, D14 = 46, D15 = 47; + public static final int D16 = 48, D17 = 49, D18 = 50, D19 = 51, D20 = 52, D21 = 53, D22 = 54, D23 = 55; + public static final int D24 = 56, D25 = 57, D26 = 58, D27 = 59, D28 = 60, D29 = 61, D30 = 62, D31 = 63; static final int FLAGS = 64; + static final int MAX_REG = 65; + public static final int D_OFFSET = 32; static final String[] REGS = new String[] { "X0", "X1", "X2", "X3", "X4", "X5", "X6", "X7", @@ -37,9 +41,7 @@ public class arm extends Machine { "D24", "D25", "D26", "D27", "D28", "D29", "D30", "D31", "flags" }; - @Override public String reg( int reg ) { - return reg < REGS.length ? REGS[reg] : "[rsp+"+(reg-REGS.length)*4+"]"; - } + @Override public String[] regs() { return REGS; } // from (x0-x30) // General purpose register mask: pointers and ints, not floats @@ -55,11 +57,9 @@ public class arm extends Machine { // Load/store mask; both GPR and FPR static final RegMask MEM_MASK = new RegMask(WR_BITS | FP_BITS); - static final RegMask SPLIT_MASK = new RegMask(WR_BITS | FP_BITS, -1L); + static final RegMask SPLIT_MASK = new RegMask(WR_BITS | FP_BITS, -2L/*skip flags*/); static final RegMask FLAGS_MASK = new RegMask(FLAGS); - // x30 (LR): Procedure link register, used to return from subroutines. - static final RegMask RPC_MASK = new RegMask(1L << X30); // Arguments masks static final RegMask X0_MASK = new RegMask(X0); @@ -81,6 +81,102 @@ public class arm extends Machine { static final RegMask D6_MASK = new RegMask(D6); static final RegMask D7_MASK = new RegMask(D7); + // major opcode: OP + public static int OP_ADD = 0b10_001_011; + public static int OPF_ADD = 0b00_011_110; + public static int OPF_OP_ADD = 0b00_101_0; + public static int OPI_ADD = 0b10_010_00100; + + public static int OPI_SUB = 0b11_010_00100; + public static int OP_UJMP = 0b000101; + + public static int OP_ADRP = 0b10000; + + public static int OP_ASR = 0b1010; + public static int OPI_ASR = 0b10_0100_1101; + + public static int OP_LSL = 0b1000; + public static int OPI_LSL =0b1101001101; + + public static int OP_LSR = 0b1001; + public static int OPI_LSR = 0b1101001101; + + public static int OP_BRANCH = 0b01_0101_00; + public static int OP_CALL = 0b10_010_1; + + public static int OP_CALLRARM = 0b1101011000111111000000; + + public static int OP_SUBS = 0b1111000100; + public static int OP_CSET = 0b10011010100; + public static int OP_CMP = 0b11101011; + public static int OPI_CMP = 0b1111000100; + + public static int OP_DIV = 0b10011010110; + public static int OPF_DIV = 0b000110; + + public static int OP_MUL = 0b10011011000; + public static int OPF_MUL = 0b10; + + public static int OPF_ARM = 0b01011100; + + public static int OP_FLOAT_C = 0b10011110; + + public static int OP_XOR = 0b11001010; + public static int OPI_XOR = 0b110100100; + + public static int OP_AND = 0b10_0010_10; + public static int OPI_AND = 0b10_0100_100; + + public static int OP_OR = 0b10101010; + public static int OPI_OR = 0b101100100; + + public static int OP_SUB = 0b11001011; + public static int OPF_SUB = 0b1110; + + public static int OP_MOVK = 0b111100101; + public static int OP_MOVN = 0b100100101; + public static int OP_MOVZ = 0b110100101; + + public static int OP_RET = 0b1101011001011111000000; + // Store/load opcodes + public static int OP_LOAD_R_64 = 0b11111000011; + public static int OP_LOAD_R_32 = 0b10111000011; + public static int OP_LOAD_R_16 = 0b01111000010; + public static int OP_LOAD_R_8 = 0b00111000011; + + public static int OP_LOAD_IMM_64 = 0b1111100101; + public static int OP_LOAD_IMM_32 = 0b1011100101; + public static int OP_LOAD_IMM_16 = 0b0111100101; + public static int OP_LOAD_IMM_8 = 0b0011100101; + + // load + public static int OPF_LOAD_R_64 = 0b11111100011; + public static int OPF_LOAD_R_32 = 0b10111100011; + + public static int OPF_LOAD_IMM_64 = 0b1111110101; + public static int OPF_LOAD_IMM_32 = 0b1011110101; + + public static int OP_STORE_R_64 = 0b11111000001; + public static int OP_STORE_R_32 = 0b10111000001; + public static int OP_STORE_R_16 = 0b01111000001; + public static int OP_STORE_R_8 = 0b00111000001; + + public static int OP_STORE_IMM_64 = 0b1111100100; + public static int OP_STORE_IMM_32 = 0b1011100100; + public static int OP_STORE_IMM_16 = 0b0111100100; + public static int OP_STORE_IMM_8 = 0b0011100100; + + public static int OPF_STORE_R_64 = 0b11111100001; + public static int OPF_STORE_R_32 = 0b10111100001; + + public static int OPF_STORE_IMM_32 = 0b1111110100; + public static int OPF_STORE_IMM_64 = 0b1111110100; + + + public static int OP_FMOV = 0b10011110; + public static int OP_FMOV_REG = 0b00011110; + // https://docsmirror.github.io/A64/2023-06/mov_orr_log_shift.html + public static int OP_MOV = 0b10101010000; // Calling convention; returns a machine-specific register // for incoming argument idx. // index 0 for control, 1 for memory, real args start at index 2 @@ -105,13 +201,376 @@ public class arm extends Machine { D7_MASK, }; - static RegMask callInMask( TypeFunPtr tfp, int idx ) { - if( idx==0 ) return RPC_MASK; + // ARM ENCODING + public enum OPTION { + UXTB, + UXTH, + UXTX, + SXTB, + SXTH, + SXTW, + SXTX, + } + + static public int cset(int opcode, int rm, COND cond, int rn, int rd) { + assert 0 <= rm && rm < 32; + assert 0 <= rn && rn < 32; + assert 0 <= rd && rd < 32; + return (opcode << 21) | (rm << 15) | (cond.ordinal() << 12) | (rn << 5) | rd; + } + + static public int cset(int opcode, COND cond, int rn, int rd) { + assert 0 <= rn && rn < 32; + assert 0 <= rd && rd < 32; + return (opcode << 16) | (cond.ordinal() << 12) | (rn << 5) | rd; + } + + public enum STORE_LOAD_OPTION { + UXTW, // base+ u32 index [<< logsize] + UXTX, // base+ u64 index [<< logsize] + SXTW, // base+ s32 index [<< logsize] + SXTX, // base+ s64 index [<< logsize] + } + + public enum COND { + EQ, + NE, + CS, + CC, + MI, + PL, + VS, + VC, + HI, + LS, + GE, + LT, + GT, + LE, + AL, + NV + } + + // True if signed 9-bit immediate + private static boolean imm9(TypeInteger ti) { + // 55 = 64-9 + return ti.isConstant() && ((ti.value()<<55)>>55) == ti.value(); + } + // True if signed 12-bit immediate + private static boolean imm12(TypeInteger ti) { + // 52 = 64-12 + return ti.isConstant() && ((ti.value()<<52)>>52) == ti.value(); + } + + + // Can we encode this in ARM's 12-bit LOGICAL immediate form? + // Some combination of shifted bit-masks. + private static int imm12Logical(TypeInteger ti) { + if( !ti.isConstant() ) return -1; + if( !ti.isConstant() ) return -1; + long val = ti.value(); + if (val == 0 || val == -1) return -1; // Special cases are not allowed + int immr = 0; + // Rotate until we have 0[...]1 + + // Rotate until: + // The number is negative (MSB is 1) + // Or the LSB is not 1 + while (val < 0 || (val & 1)==0) { + // circular rotation + val = (val >>> 63) | (val << 1); + immr++; + } + // Start by assuming that val might be made of two 32-bit chunks that repeat. + int size = 32; + long pattern = val; + // Is upper half of pattern the same as the lower? + while ((pattern & ((1L<<size)-1)) == (pattern >> size)) { + // Then only take one half + pattern >>= size; + size >>= 1; + } + size <<= 1; + int imms = Long.bitCount(pattern); + // Pattern should now be zeros followed by ones 0000011111 + if (pattern != (1L<<imms)-1) return -1; + imms--; + if (size == 64) return 0x1000 | immr << 6 | imms; + return (32-size)<<1 | immr << 6 | imms; + } + + // sh is encoded in opcode + public static int imm_inst(int opcode, int imm12, int rn, int rd) { + assert 0 <= rn && rn < 32; + assert 0 <= rd && rd < 32; + assert opcode >=0 && imm12 >= 0; // Caller zeros high order bits + return (opcode << 22) | (imm12 << 10) | (rn << 5) | rd; + } + + public static int imm_shift(int opcode, int imm, int imms, int rn, int rd) { + assert 0 <= rn && rn < 32; + assert 0 <= rd && rd < 32; + return (opcode << 22) | (1 << 22) | (imm << 16) | (imms << 10) | (rn << 5) | rd; + } + + public static void imm_inst(Encoding enc, Node n,Node n2, int opcode, int imm12) { + short self = enc.reg(n); + short reg1 = enc.reg(n2); + + int body = imm_inst(opcode, imm12&0xFFF, reg1, self); + enc.add4(body); + } + + public static void imm_inst_subs(Encoding enc, Node n,Node n2, int opcode, int imm12) { + short reg1 = enc.reg(n2); + // 31 = 11111 + int body = imm_inst(opcode, imm12&0xFFF, reg1, 31); + enc.add4(body); + } + + // for cases where rs1 and dst are the same, eg add x0, x0, 1 + public static void imm_inst(Encoding enc, int opcode, int imm12, int self) { + int body = imm_inst(opcode, imm12&0xFFF, self, self); + enc.add4(body); + } + + public static void imm_inst_n(Encoding enc, Node n, Node n2, int opcode, int imm13) { + short self = enc.reg(n); + short reg1 = enc.reg(n2); + int body = imm_inst_n(opcode, imm13, reg1, self); + enc.add4(body); + } + + // nth bit comes from immediate and not opcode + public static int imm_inst_n(int opcode, int imm13, int rn, int rd) { + assert 0 <= rn && rn < 32; + assert 0 <= rd && rd < 32; + assert 0 <= imm13 && imm13 <= 0x1FFF; + return (opcode << 23) | (imm13 << 10) | (rn << 5) | rd; + } + + public static int imm_inst_l(int opcode, int imm12, int self) { + int body = imm_inst(opcode, imm12&0xFFF, self, self); + return body; + } + + // for cases where rs1 and dst are the same, eg add x0, x0, 1 + public static int imm_inst_l(Encoding enc, Node n, int opcode, int imm12) { + short self = enc.reg(n); + short reg1 = enc.reg(n.in(1)); + int body = imm_inst(opcode, imm12&0xFFF, reg1, self); + return body; + } + + // for normal add, reg1, reg2 cases (reg-to-reg) + // using shifted-reg form + public static int r_reg(int opcode, int shift, int rm, int imm6, int rn, int rd) { + assert 0 <= rm && rm < 32; + assert 0 <= rn && rn < 32; + assert 0 <= rd && rd < 32; + return (opcode << 24) | (shift << 21) | (rm << 16) | (imm6 << 10) | (rn << 5) | rd; + } + public static void r_reg(Encoding enc, Node n, int opcode) { + short self = enc.reg(n); + short reg1 = enc.reg(n.in(1)); + short reg2 = enc.reg(n.in(2)); + int body = r_reg(opcode, 0, reg2, 0, reg1, self >= 32 ? reg1: self); + enc.add4(body); + } + + public static void r_reg_subs(Encoding enc, Node n, int opcode) { + short reg1 = enc.reg(n.in(1)); + short reg2 = enc.reg(n.in(2)); + // 31 = 11111 + int body = r_reg(opcode, 0, reg2, 0, reg1, 31); + enc.add4(body); + } + + public static int shift_reg(int opcode, int rm, int op2, int rn, int rd) { + assert 0 <= rn && rn < 32; + assert 0 <= rm && rm < 32; + assert 0 <= rd && rd < 32; + return (opcode << 21) | (rm << 16) | (op2 << 10) | (rn << 5) | rd; + } + public static void shift_reg(Encoding enc, Node n, int op2) { + short self = enc.reg(n); + short reg1 = enc.reg(n.in(1)); + short reg2 = enc.reg(n.in(2)); + int body = shift_reg(0b10011010110, reg2, op2, reg1, self); + enc.add4(body); + } + + // MUL can be considered an alias for MADD with the third operand Ra being set to 0 + public static int madd(int opcode, int rm, int ra, int rn, int rd) { + assert 0 <= rn && rn < 32; + assert 0 <= rd && rd < 32; + assert 0 <= rm && rm < 32; + return (opcode << 21) | (rm << 16) | (ra << 10) | (rn << 5) | rd; + } + public static void madd(Encoding enc, Node n, int opcode, int ra) { + short self = enc.reg(n); + short reg1 = enc.reg(n.in(1)); + short reg2 = enc.reg(n.in(2)); + int body = madd(opcode, reg2, ra, reg1, self); + enc.add4(body); + } + + // encodes movk, movn, and movz + public static int mov(int opcode, int shift, int imm16, int rd) { + assert 0 <= rd && rd < 32; + return (opcode << 23) | (shift << 21) | (imm16 << 5) | rd; + } + + public static int mov_reg(int opcode, int src, int rd) { + assert 0 <= rd && rd < 32; + return (opcode << 21) | (src << 16) | 0b11111 << 5 | rd; + } + + public static int ret(int opcode) { + return (opcode << 10); + } + + // FMOV (scalar, immediate) + public static int f_scalar(int opcode, int ftype, int rm, int op, int rn, int rd) { + assert 0 <= rn && rn < 32; + assert 0 <= rd && rd < 32; + assert 0 <= rm && rm < 32; + return (opcode << 24) | (ftype << 22) | (1 << 21) | (rm << 16) | (op << 10) | (rn << 5) | rd; + } + + public static int f_mov_reg(int opcode, int rn, int rd) { + assert 0 <= rn && rn < 32; + assert 0 <= rd && rd < 32; + return (opcode << 24) | (0b01100000010000 << 10) | (rn << 5) | rd; + } + public static int f_mov_general(int opcode, int ftype, int rmode, int opcode1, int rn, int rd) { + assert 0 <= rn && rn < 32; + assert 0 <= rd && rd < 32; + return (opcode << 24) |(ftype << 22) | (1 << 21) | (rmode << 19) | (opcode1 << 16) | (rn << 5) | rd; + } + public static void f_scalar(Encoding enc, Node n, int op ) { + short self = (short)(enc.reg(n) -D_OFFSET); + short reg1 = (short)(enc.reg(n.in(1))-D_OFFSET); + short reg2 = (short)(enc.reg(n.in(2))-D_OFFSET); + int body = f_scalar(0b00011110, 1, reg2, op, reg1, self); + enc.add4(body); + } + + // share same encoding with LDR (literal, SIMD&FP) flavour + public static int load_pc(int opcode, int offset, int rt) { + offset>>=2; + return (opcode << 24) | (offset << 5) | rt; + } + // int l + public static int adrp(int op, int imlo,int opcode, int imhi, int rd) { + assert 0 <= rd && rd < 32; + return (op << 31) | (imlo << 29) |(opcode << 24) | (imhi << 5) | rd; + } + + // [Rptr+Roff] + public static int indr_adr(int opcode, int off, STORE_LOAD_OPTION option, int s, int ptr, int rt) { + assert 0 <= ptr && ptr < 32; + assert 0 <= rt && rt < 32; + return (opcode << 21) | (off << 16) | (option.ordinal() << 13) | (s << 12) | (2 << 10) | (ptr << 5) | rt; + } + // [Rptr+imm9] + public static int load_str_imm(int opcode, int imm12, int ptr, int rt, int size) { + assert 0 <= ptr && ptr < 32; + assert 0 <= rt && rt < 32; + + if(size == 8) imm12 = imm12 >> 3; + if(size == 4) imm12 = imm12 >> 2; + if(size == 2) imm12 = imm12 >> 1; + // size == 1 imm12 = imm12 + return (opcode << 22) | ((imm12) << 10) | (ptr << 5) | rt; + } + + // encoding for vcvt, size is encoded in operand + // <Qd>, <Qm> + // F32.S32 + //encoded as op = 0b00, size = 0b10. + // VCVT<c>.<Td>.<Tm> <Dd>, <Dm> + // opcode is broken down into 4 pieces + public static int f_convert(int opcode_1, int opcode_2, int opcode_3, int opcode_4, int vd, int vm) { + return (opcode_1 << 28) | (opcode_2 << 24) | (opcode_3 << 20) | (opcode_4 << 16) | + (vd << 12) | (0x01100010 << 4) | vm; + } + public static int float_cast(int opcode, int ftype, int rn, int rd) { + assert 0 <= rd && rd < 32; + assert 0 <= rn && rn < 32; + return (opcode << 24) | (ftype << 22) | (2176 << 10) | (rn << 5) | rd; + } + + + // ftype = 3 + public static int f_cmp(int opcode, int ftype, int rm, int rn) { + assert 0 <= rn && rn < 32; + assert 0 <= rm && rm < 32; + // Todo: |8 is not needed + return (opcode << 24) | (ftype << 21) | (rm << 16) | (8 << 10) | (rn << 5); + } + public static void f_cmp(Encoding enc, Node n) { + short reg1 = (short)(enc.reg(n.in(1))-D_OFFSET); + short reg2 = (short)(enc.reg(n.in(2))-D_OFFSET); + int body = f_cmp(0b00011110, 3, reg1, reg2); + enc.add4(body); + } + + public static COND make_condition(String bop) { + return switch (bop) { + case "==" -> COND.EQ; + case "!=" -> COND.NE; + case "<" -> COND.LT; + case "<=" -> COND.LE; + case ">=" -> COND.GE; + case ">" -> COND.GT; + default -> throw Utils.TODO(); + }; + } + + public static int b_cond(int opcode, int delta, COND cond) { + // 24-5 == 19bits offset range + assert -(1<<19) <= delta && delta < (1<<19); + assert (delta&3)==0; + delta>>=2; + delta &= (1L<<19)-1; // Zero extend + return (opcode << 24) | ((delta)<< 5) | cond.ordinal(); + } + + public static int cond_set(int opcode, int rm, COND cond, int rn, int rd) { + assert 0 <= rd && rd < 32; + assert 0 <= rn && rn < 32; + return (opcode << 21) | (rm << 16) | (cond.ordinal() << 12) | (rn << 5) | rd; + } + + // Branch with Link to Register calls a subroutine at an address in a register, setting register X30 to PC+4. + public static int blr(int opcode, int rd) { + assert 0 <= rd && rd < 32; + return opcode << 10 | rd << 5; + } + public static int b(int opcode, int delta) { + assert -(1<<26) <= delta && delta < (1<<26); + assert (delta&3)==0; + delta>>=2; + delta &= (1L<<26)-1; // Zero extend + return (opcode << 26) | delta; + } + // no aligned assert(assert (delta&3)==0;) + public static int b_calloc(int opcode, int delta) { + assert -(1<<26) <= delta && delta < (1<<26); + delta>>=2; + delta &= (1L<<26)-1; // Zero extend + return (opcode << 26) | delta; + } + + @Override public RegMask callArgMask(TypeFunPtr tfp, int idx, int maxArgSlot ) { return callInMask(tfp,idx,maxArgSlot); } + static RegMask callInMask(TypeFunPtr tfp, int idx, int maxArgSlot ) { + if( idx==0 ) return CodeGen.CODE._rpcMask; if( idx==1 ) return null; // Count floats in signature up to index int fcnt=0; for( int i=2; i<idx; i++ ) - if( tfp.arg(i-2) instanceof TypeFloat ) + if( tfp.arg(i-2) instanceof TypeFloat) fcnt++; // Floats up to XMMS in XMM registers if( tfp.arg(idx-2) instanceof TypeFloat ) { @@ -125,121 +584,119 @@ public class arm extends Machine { throw Utils.TODO(); // Pass on stack slot } - static final long CALLEE_SAVE = + public static long decodeImm12(int imm12) { + int immr = (imm12 >> 6) & 0x3F; + int imms = imm12 & 0x3F; + int size; + if ((imm12 & 0x1000) != 0) { + size = 64; + } else { + size = 31-(imms >> 1); + size |= size >> 1; + size |= size >> 2; + size |= size >> 4; + size++; + imms &= ~((32-size) << 1); + } + long val = (2L << imms)-1; + while (size < 64) { + val |= val << size; + size <<= 1; + } + val = (val >>> immr) | val << (64-immr); + return val; + } + + // Return the max stack slot used by this signature, or 0 + @Override public short maxArgSlot( TypeFunPtr tfp ) { + int icnt=0, fcnt=0; // Count of ints, floats + for( int i=0; i<tfp.nargs(); i++ ) { + if( tfp.arg(i) instanceof TypeFloat ) fcnt++; + else icnt++; + } + int nstk = Math.max(icnt-8,0)+Math.max(fcnt-8,0); + return (short)nstk; + } + + private static final long CALLEE_SAVE = 1L<<X19 | 1L<<X20 | 1L<<X21 | 1L<<X22 | 1L<<X23 | 1L<<X24 | 1L<<X25 | 1L<<X26 | 1L<<X27 | 1L<<X28 | 1L<<D9 | 1L<<D10 | 1L<<D11 | 1L<<D12 | 1L<<D13 | 1L<<D14 | 1L<<D15; - - static final RegMask CALLER_SAVE_MASK; - static { - long caller = ~CALLEE_SAVE; - caller &= ~(1L<<RSP); - CALLER_SAVE_MASK = new RegMask(caller,1L<<(FLAGS-64)); - } - static RegMask armCallerSave() { return CALLER_SAVE_MASK; } - @Override public RegMask callerSave() { return armCallerSave(); } - - static final RegMask CALLEE_SAVE_MASK = new RegMask(CALLEE_SAVE); - static RegMask armCalleeSave() { return CALLEE_SAVE_MASK; } - @Override public RegMask calleeSave() { return armCalleeSave(); } - - - // Return single int/ptr register. Used by CallEnd output and Return input. - static final RegMask[] RET_MASKS; - static { - int nSaves = CALLEE_SAVE_MASK.size(); - RET_MASKS = new RegMask[4 + nSaves]; - RET_MASKS[0] = null; - RET_MASKS[1] = null; // Memory - RET_MASKS[2] = null; // Varies, either XMM0 or RAX - RET_MASKS[3] = RMASK; // Expected R30 but could be any register - short reg = CALLEE_SAVE_MASK.firstReg(); - for( int i=0; i<nSaves; i++ ) { - RET_MASKS[i+4] = new RegMask(reg); - reg = CALLEE_SAVE_MASK.nextReg(reg); - } - } - static RegMask retMask( TypeFunPtr tfp, int i ) { - return i==2 - ? (tfp.ret() instanceof TypeFloat ? D0_MASK : X0_MASK) - : RET_MASKS[i]; + static final long CALLER_SAVE = ~CALLEE_SAVE & ~(1L<<RSP); + @Override public long callerSave() { return CALLER_SAVE; } + @Override public long neverSave() { return 1L<<RSP; } + @Override public RegMask retMask( TypeFunPtr tfp ) { + return tfp.ret() instanceof TypeFloat ? D0_MASK : X0_MASK; } + @Override public int rpc() { return X30; } // Create a split op; any register to any register, including stack slots @Override public SplitNode split(String kind, byte round, LRG lrg) { return new SplitARM(kind,round); } // Return a MachNode unconditional branch @Override public CFGNode jump() { - throw Utils.TODO(); - } - - // Break an infinite loop - @Override public IfNode never( CFGNode ctrl ) { - throw Utils.TODO(); + return new UJmpARM(); } // Instruction selection - @Override public Node instSelect( Node n ) { + @Override public Node instSelect(Node n ) { return switch( n ) { - case AddFNode addf -> addf(addf); - case AddNode add -> add(add); - case AndNode and -> and(and); - case BoolNode bool -> cmp(bool); - case CallNode call -> call(call); - case CastNode cast -> new CastNode(cast); - case CallEndNode cend -> new CallEndARM(cend); - case CProjNode c -> new CProjNode(c); - case ConstantNode con -> con(con); - case DivFNode divf -> new DivFARM(divf); - case DivNode div -> new DivARM(div); - case FunNode fun -> new FunARM(fun); - case IfNode iff -> jmp(iff); - case LoadNode ld -> ld(ld); - case MemMergeNode mem -> new MemMergeNode(mem); - case MulFNode mulf -> new MulFARM(mulf); - case MulNode mul -> mul(mul); - case NewNode nnn -> new NewARM(nnn); - case OrNode or -> or(or); - case ParmNode parm -> new ParmARM(parm); - case PhiNode phi -> new PhiNode(phi); - case ProjNode prj -> prj(prj); + case AddFNode addf -> new AddFARM(addf); + case AddNode add -> add(add); + case AndNode and -> and(and); + case BoolNode bool -> cmp(bool); + case CallNode call -> call(call); + case CastNode cast -> new CastARM(cast); + case CallEndNode cend -> new CallEndARM(cend); + case CProjNode c -> new CProjNode(c); + case ConstantNode con -> con(con); + case DivFNode divf -> new DivFARM(divf); + case DivNode div -> new DivARM(div); + case FunNode fun -> new FunARM(fun); + case IfNode iff -> jmp(iff); + case LoadNode ld -> ld(ld); + case MemMergeNode mem -> new MemMergeNode(mem); + case MinusNode neg -> new NegARM(neg); + case MulFNode mulf -> new MulFARM(mulf); + case MulNode mul -> new MulARM(mul); + case NewNode nnn -> new NewARM(nnn); + case NotNode not -> new NotARM(not); + case OrNode or -> or(or); + case ParmNode parm -> new ParmARM(parm); + case PhiNode phi -> new PhiNode(phi); + case ProjNode prj -> new ProjARM(prj); case ReadOnlyNode read -> new ReadOnlyNode(read); - case ReturnNode ret -> new RetARM(ret,ret.fun()); - case SarNode sar -> sar(sar); - case ShlNode shl -> shl(shl); - case ShrNode shr -> shr(shr); - case StartNode start -> new StartNode(start); - case StopNode stop -> new StopNode(stop); - case StoreNode st -> st(st); - case SubFNode subf -> new SubFARM(subf); - case SubNode sub -> sub(sub); - case ToFloatNode tfn -> i2f8(tfn); - case XorNode xor -> xor(xor); - - case LoopNode loop -> new LoopNode(loop); - case RegionNode region-> new RegionNode(region); + case ReturnNode ret -> new RetARM(ret,ret.fun()); + case SarNode sar -> asr(sar); + case ShlNode shl -> lsl(shl); + case ShrNode shr -> lsr(shr); + case StartNode start -> new StartNode(start); + case StopNode stop -> new StopNode(stop); + case StoreNode st -> st(st); + case SubFNode subf -> new SubFARM(subf); + case SubNode sub -> sub(sub); + case ToFloatNode tfn-> new I2F8ARM(tfn); + case XorNode xor -> xor(xor); + + case LoopNode loop -> new LoopNode(loop); + case RegionNode region-> new RegionNode(region); default -> throw Utils.TODO(); }; } - private Node mul(MulNode mul) { - Node rhs = mul.in(2); - if( rhs instanceof ConstantNode off && off._con instanceof TypeInteger toff ) { - return new MulIARM(mul, toff); - } - return new MulARM(mul); - } private Node cmp(BoolNode bool){ Node cmp = _cmp(bool); - return new SetARM(cmp, bool.op()); + return new SetARM(cmp, IfNode.negate(bool.op())); } - - private Node i2f8(ToFloatNode tfn) { - assert tfn.in(1)._type instanceof TypeInteger ti; - return new I2F8ARM(tfn); + private Node _cmp(BoolNode bool) { + if( bool.isFloat() ) + return new CmpFARM(bool); + return bool.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti + ? new CmpIARM(bool, (int)ti.value()) + : new CmpARM(bool); } private Node ld(LoadNode ld) { @@ -250,129 +707,101 @@ public class arm extends Machine { // If/Bool combos will match to a Cmp/Set which sets flags. // Most general arith ops will also set flags, which the Jmp needs directly. // Loads do not set the flags, and will need an explicit TEST - if( !(iff.in(1) instanceof BoolNode) ) - iff.setDef(1,new BoolNode.EQ(iff.in(1),new ConstantNode(TypeInteger.ZERO))); - return new BranchARM(iff, ((BoolNode)iff.in(1)).op()); - } - - private Node _cmp(BoolNode bool) { - if( bool instanceof BoolNode.EQF || - bool instanceof BoolNode.LTF || - bool instanceof BoolNode.LEF ) - return new CmpFARM(bool); - - - return bool.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti - ? new CmpIARM(bool, ti) - : new CmpARM(bool); - } - - - private Node addf(AddFNode addf) { - return new AddFARM(addf); + String op = "!="; + if( iff.in(1) instanceof BoolNode bool ) op = bool.op(); + else if( iff.in(1)==null ) op = "=="; // Never-node cutout + else iff.setDef(1, new BoolNode.NE(iff.in(1), new ConstantNode(TypeInteger.ZERO))); + return new BranchARM(iff, op); } private Node add(AddNode add) { - Node rhs = add.in(2); - if( rhs instanceof ConstantNode off && off._con instanceof TypeInteger toff ) { - return new AddIARM(add, toff); - } - return new AddARM(add); + return add.in(2) instanceof ConstantNode off && off._con instanceof TypeInteger ti && imm12(ti) + ? new AddIARM(add, (int)ti.value()) + : new AddARM(add); } private Node sub(SubNode sub) { - return sub.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti - ? new AddIARM(sub, TypeInteger.constant(-ti.value())) + return sub.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti) + ? new SubIARM(sub, (int)(ti.value())) : new SubARM(sub); } - private Node and(AndNode and) { - if( and.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti ) - return new AndIARM(and, ti); - return new AndARM(and); - } - - private Node or(OrNode or) { - if(or.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti) - return new OrIARM(or, ti); - - return new OrARM(or); - } - - private Node xor(XorNode xor) { - if(xor.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti) - return new XorIARM(xor, ti); - return new XorARM(xor); - } - - private Node con(ConstantNode con) { + private Node con( ConstantNode con ) { if( !con._con.isConstant() ) return new ConstantNode( con ); // Default unknown caller inputs return switch( con._con ) { case TypeInteger ti -> new IntARM(con); case TypeFloat tf -> new FloatARM(con); case TypeFunPtr tfp -> new TFPARM(con); - case TypeMemPtr tmp -> new ConstantNode(con); - case TypeNil tn -> throw Utils.TODO(); + case TypeMemPtr tmp -> new ConstantNode(con); + case TypeNil tn -> throw Utils.TODO(); // TOP, BOTTOM, XCtrl, Ctrl, etc. Never any executable code. - case Type t -> new ConstantNode(con); + case Type t -> t==Type.NIL ? new IntARM(con) : new ConstantNode(con); }; } - private Node sar(SarNode sar){ - if( sar.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti) - return new AsrIARM(sar, ti); - return new AsrARM(sar); + private Node call(CallNode call){ + return call.fptr() instanceof ConstantNode con && con._con instanceof TypeFunPtr tfp + ? new CallARM(call, tfp) + : new CallRRARM(call); + } + + private Node or(OrNode or) { + int imm12; + return or.in(2) instanceof ConstantNode off && off._con instanceof TypeInteger ti && (imm12 = imm12Logical(ti)) != -1 + ? new OrIARM(or, imm12) + : new OrARM(or); } - private Node shl(ShlNode shl) { - if( shl.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti) - return new LslIARM(shl, ti); - return new LslARM(shl); + private Node xor(XorNode xor) { + int imm12; + return xor.in(2) instanceof ConstantNode off && off._con instanceof TypeInteger ti && (imm12 = imm12Logical(ti)) != -1 + ? new XorIARM(xor, imm12) + : new XorARM(xor); + } + private Node and(AndNode and) { + int imm12; + return and.in(2) instanceof ConstantNode off && off._con instanceof TypeInteger ti && (imm12 = imm12Logical(ti)) != -1 + ? new AndIARM(and, imm12) + : new AndARM(and); } - private Node shr(ShrNode shr) { - if( shr.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti) - return new LsrIARM(shr, ti); - return new LsrARM(shr); + private Node asr(SarNode asr) { + return asr.in(2) instanceof ConstantNode off && off._con instanceof TypeInteger ti && ti.value() >= 0 && ti.value() < 63 + ? new AsrIARM(asr, (int)ti.value()) + : new AsrARM(asr); } - private Node call(CallNode call){ - if(call.fptr() instanceof ConstantNode con && con._con instanceof TypeFunPtr tfp) - return new CallARM(call, tfp); - return new CallRRARM(call); + private Node lsl(ShlNode lsl) { + return lsl.in(2) instanceof ConstantNode off && off._con instanceof TypeInteger ti && ti.value() >= 0 && ti.value() < 63 + ? new LslIARM(lsl, (int)ti.value()) + : new LslARM(lsl); } - private Node prj(ProjNode prj) { - return new ProjARM(prj); + private Node lsr(ShrNode lsr) { + return lsr.in(2) instanceof ConstantNode off && off._con instanceof TypeInteger ti && ti.value() >= 0 && ti.value() < 63 + ? new LsrIARM(lsr, (int)ti.value()) + : new LsrARM(lsr); } private static int off; private static Node idx; private Node st(StoreNode st) { - int imm=0; - Node xval = st.val(); - // e.g store this s.cs[0] = 67; // C - if( xval instanceof ConstantNode con && con._con instanceof TypeInteger ti ) { - xval = null; - imm = (int)ti.value(); - assert imm == ti.value(); // In 32-bit range - } - return new StoreARM(address(st),st.ptr(),idx,off,imm,xval); + return new StoreARM(address(st),st.ptr(),idx,off,st.val()); } // Gather addressing mode bits prior to constructing. This is a builder // pattern, but saving the bits in a *local* *global* here to keep mess // contained. - private <N extends MemOpNode> N address( N mop ) { + private <N extends MemOpNode> N address(N mop ) { off = 0; // Reset idx = null; Node base = mop.ptr(); // Skip/throw-away a ReadOnly, only used to typecheck if( base instanceof ReadOnlyNode read ) base = read.in(1); assert !(base instanceof AddNode) && base._type instanceof TypeMemPtr; // Base ptr always, not some derived - if( mop.off() instanceof ConstantNode con && con._con instanceof TypeInteger ti ) { + if( mop.off() instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm9(ti) ) { off = (int)ti.value(); assert off == ti.value(); // In 32-bit range } else { diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/i2f8.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/i2f8.java deleted file mode 100644 index e0792669..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/i2f8.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.seaofnodes.simple.node.cpus.arm; - -public class i2f8 { -} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AUIPC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AUIPC.java new file mode 100644 index 00000000..37383608 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AUIPC.java @@ -0,0 +1,41 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.ConstantNode; +import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.type.Type; + +// Add upper 20bits to PC. Immediate comes from the relocation info. +public class AUIPC extends ConstantNode implements MachNode, RIPRelSize { + AUIPC( Type tfp ) { super(tfp); } + @Override public RegMask regmap(int i) { return null; } + @Override public RegMask outregmap() { return riscv.WMASK; } + @Override public boolean isClone() { return true; } + @Override public AUIPC copy() { return new AUIPC(_con); } + @Override public String op() { return "auipc"; } + @Override public void encoding( Encoding enc ) { + enc.relo(this); + short dst = enc.reg(this); + enc.add4(riscv.u_type(riscv.OP_AUIPC, dst, 0)); + } + + // Delta is from opcode start, but X86 measures from the end of the 5-byte encoding + @Override public byte encSize(int delta) { return 4; } + + // Delta is from opcode start + @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { + //short rpc = enc.reg(this); + //// High half is where the TFP constant used to be, the last input + //short auipc = enc.reg(in(_inputs._len-1)); + //enc.patch4(opStart,riscv.i_type(0x67, rpc, 0, auipc, delta)); + throw Utils.TODO(); + } + + @Override public void asm(CodeGen code, SB sb) { + String reg = code.reg(this); + sb.p(reg).p(" = PC+#"); + if( _con == null ) sb.p("---"); + else _con.print(sb); + } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddFRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddFRISC.java index 2417aeb6..4d2ac916 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddFRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddFRISC.java @@ -1,30 +1,17 @@ package com.seaofnodes.simple.node.cpus.riscv; -import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.SB; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.TypeInteger; -import java.io.ByteArrayOutputStream; -public class AddFRISC extends MachConcreteNode implements MachNode{ +// fadd.d +public class AddFRISC extends MachConcreteNode implements MachNode { AddFRISC(Node addf) {super(addf);} - - // Register mask allowed on input i. + @Override public String op() { return "addf"; } @Override public RegMask regmap(int i) { assert i==1 || i==2; return riscv.FMASK; } - // Register mask allowed as a result. 0 for no register. @Override public RegMask outregmap() { return riscv.FMASK; } - - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); - } - - // Default on double precision for now(64 bits) - // General form: "fadd.d rd = src1 + src2 + @Override public void encoding( Encoding enc ) { riscv.rf_type(enc,this,riscv.RM.RNE,1); } @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" + ").p(code.reg(in(2))); } - - @Override public String op() { return "addf"; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddIRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddIRISC.java index e92a7b08..a85ce5d0 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddIRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddIRISC.java @@ -4,8 +4,18 @@ import com.seaofnodes.simple.Utils; import com.seaofnodes.simple.node.Node; public class AddIRISC extends ImmRISC { - AddIRISC( Node add, int imm ) { super(add,imm); } - @Override int opcode() { throw Utils.TODO(); } - @Override public String glabel() { return "+"; } + // Used to inst-selection as a direct match against an ideal Add/Sub + public AddIRISC( Node add, int imm12, boolean pop ) { super(add,imm12,pop); } @Override public String op() { return "addi"; } + @Override public String glabel() { return "+"; } + @Override int opcode() { return riscv.OP_IMM; } + @Override int func3() {return 0;} + @Override public AddIRISC copy() { + // Clone the AddI, using the same inputs-only code used during inst select. + // Output edges are missing. + AddIRISC add = new AddIRISC(in(1),_imm12,false); + // Copys happen when output edges should be valid, so correct missing output edges. + in(1)._outputs.add(add); + return add; + } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddRISC.java index 344c217d..df392a0e 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddRISC.java @@ -1,29 +1,21 @@ package com.seaofnodes.simple.node.cpus.riscv; -import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.SB; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.TypeInteger; -import java.io.ByteArrayOutputStream; public class AddRISC extends MachConcreteNode implements MachNode { - AddRISC( Node add) {super(add); } + public AddRISC( Node add ) { super(add); } + AddRISC( Node base, Node off ) { + super(new Node[3]); + _inputs.set(1,base); + _inputs.set(2,off ); + } @Override public String op() { return "add"; } - - // Register mask allowed on input i. @Override public RegMask regmap(int i) { return riscv.RMASK; } - // Register mask allowed as a result. 0 for no register. @Override public RegMask outregmap() { return riscv.WMASK; } - - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); - } - - // General form: "rd = rs1 + rs2" + @Override public void encoding( Encoding enc ) { riscv.r_type(enc,this,0,0); } @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" + ").p(code.reg(in(2))); } - } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndIRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndIRISC.java index ddbf5aa4..ba88bb48 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndIRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndIRISC.java @@ -4,8 +4,9 @@ import com.seaofnodes.simple.Utils; import com.seaofnodes.simple.node.Node; public class AndIRISC extends ImmRISC { - AndIRISC( Node and, int imm) { super(and,imm); } - @Override int opcode() { throw Utils.TODO(); } - @Override public String glabel() { return "&"; } + public AndIRISC( Node and, int imm) { super(and,imm); } @Override public String op() { return "andi"; } + @Override public String glabel() { return "&"; } + @Override int opcode() { return riscv.OP_IMM; } + @Override int func3() {return 7;} } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndRISC.java index ca00bc98..93ef95c0 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndRISC.java @@ -1,37 +1,15 @@ package com.seaofnodes.simple.node.cpus.riscv; -import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.SB; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.Type; -import com.seaofnodes.simple.type.TypeInteger; -import java.io.ByteArrayOutputStream; -import java.util.BitSet; -import java.lang.StringBuilder; -public class AndRISC extends MachConcreteNode implements MachNode{ - AndRISC(Node and) { - super(and); - } - - // Register mask allowed on input i. - // This is the normal calling convention - @Override public RegMask regmap(int i) { - assert i==1 || i==2; - return riscv.RMASK; - } - - // Register mask allowed as a result. 0 for no register. +public class AndRISC extends MachConcreteNode implements MachNode { + public AndRISC(Node and) { super(and); } + @Override public String op() { return "and"; } + @Override public RegMask regmap(int i) { return riscv.RMASK; } @Override public RegMask outregmap() { return riscv.WMASK; } - - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); - } - - // General form - // General form: #rd = rs1 & rs2 + @Override public void encoding( Encoding enc ) { riscv.r_type(enc,this,0b111,0); } @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" & ").p(code.reg(in(2))); } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/BranchRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/BranchRISC.java index 6a1cf7dc..44ff4e8b 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/BranchRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/BranchRISC.java @@ -1,47 +1,92 @@ package com.seaofnodes.simple.node.cpus.riscv; import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; -import java.io.ByteArrayOutputStream; import java.util.BitSet; // Conditional branch such as: BEQ -public class BranchRISC extends IfNode implements MachNode { - final String _bop; +public class BranchRISC extends IfNode implements MachNode, RIPRelSize { + String _bop; // label is obtained implicitly - BranchRISC( IfNode iff, String bop, Node n1, Node n2 ) { + public BranchRISC( IfNode iff, String bop, Node n1, Node n2 ) { super(iff); _bop = bop; _inputs.setX(1,n1); _inputs.setX(2,n2); } + @Override public String op() { return "b"+_bop; } @Override public String label() { return op(); } - + @Override public String comment() { return "L"+cproj(1)._nid; } @Override public RegMask regmap(int i) { return riscv.RMASK; } @Override public RegMask outregmap() { return null; } + @Override public void negate() { + _bop = negate(_bop); + // Cannot encode ">" or "<=", so flip these + if( _bop.equals(">") || _bop.equals("<=") ) { + swap12(); // Cannot negate the test, so swap the operands + _bop = swap(_bop); // Swap test to match + } + } @Override public StringBuilder _print1(StringBuilder sb, BitSet visited) { - in(1)._print0(sb.append("if( "),visited).append(_bop); + sb.append("if( "); + if( in(1)==null ) sb.append("0"); + else in(1)._print0(sb,visited); + sb.append(_bop); if( in(2)==null ) sb.append("0"); else in(2)._print0(sb,visited); return sb.append(" )"); } - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); + @Override public void encoding( Encoding enc ) { + if( in(1)==null && _bop=="!=" ) return; // Inverted never-node, no code + enc.jump(this,cproj(0)); + if( in(1)==null ) // Never node + enc.add4(riscv.j_type(riscv.OP_JAL, 0, 0)); + else { + // Todo: relocs (for offset - immf) + short src1 = enc.reg(in(1)); + short src2 = in(2)==null ? (short)riscv.ZERO : enc.reg(in(2)); + enc.add4(riscv.b_type(riscv.OP_BRANCH, riscv.jumpop(_bop), src1, src2, 0)); + } } - @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(in(1))).p(" ").p(_bop).p(" ").p(in(2)==null ? "#0" : code.reg(in(2))); + // Delta is from opcode start + @Override public byte encSize(int delta) { + if( in(1)==null && _bop=="!=" ) return 0; // Inverted never-node, no code + if( -4*1024 <= delta && delta < 4*1024 ) return 4; + // 2 word encoding needs a tmp register, must teach RA + throw Utils.TODO(); } - @Override public String op() { return "b"+_bop; } + // Delta is from opcode start + @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { + assert !( in(1)==null && _bop=="!=" ); // Inverted never-node, no code no patch + if( in(1)==null ) { // Never node + enc.patch4(opStart,riscv.j_type(riscv.OP_JAL, 0, delta)); + } else { + short src1 = enc.reg(in(1)); + short src2 = in(2)==null ? (short)riscv.ZERO : enc.reg(in(2)); + if( opLen==4 ) { + enc.patch4(opStart,riscv.b_type(riscv.OP_BRANCH, riscv.jumpop(_bop), src1, src2, delta)); + } else { + throw Utils.TODO(); + } + } + } - @Override public String comment() { - return "L"+cproj(1)._nid+", L"+cproj(0)._nid; + @Override public void asm(CodeGen code, SB sb) { + if( in(1)==null && _bop=="!=" ) { + sb.p("never"); + return; + } + String src1 = in(1)==null ? "#0" : code.reg(in(1)); + String src2 = in(2)==null ? "#0" : code.reg(in(2)); + sb.p(src1).p(" ").p(_bop).p(" ").p(src2).p(" "); + CFGNode prj = cproj(0).uctrlSkipEmpty(); + if( !prj.blockHead() ) prj = prj.cfg0(); + sb.p(label(prj)); } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallEndRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallEndRISC.java index fd1ba7c3..7df68e77 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallEndRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallEndRISC.java @@ -1,35 +1,8 @@ package com.seaofnodes.simple.node.cpus.riscv; -import com.seaofnodes.simple.SB; -import com.seaofnodes.simple.Utils; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; import com.seaofnodes.simple.node.CallEndNode; import com.seaofnodes.simple.node.MachNode; -import com.seaofnodes.simple.type.TypeFloat; -import com.seaofnodes.simple.type.TypeFunPtr; -import java.io.ByteArrayOutputStream; public class CallEndRISC extends CallEndNode implements MachNode { - final TypeFunPtr _tfp; - CallEndRISC( CallEndNode cend ) { - super(cend); - _tfp = (TypeFunPtr)(cend.call().fptr()._type); - } - - @Override public String label() { return op(); } - @Override public RegMask regmap(int i) { return null; } - @Override public RegMask outregmap() { return null; } - @Override public RegMask outregmap(int idx) { return idx == 2 ? riscv.retMask(_tfp,2) : null; } - @Override public RegMask killmap() { return riscv.riscCallerSave(); } - - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); - } - - @Override public void asm(CodeGen code, SB sb) { } - - @Override public String op() { return "cend"; } - + CallEndRISC( CallEndNode cend ) { super(cend); } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRISC.java index 54f4fba9..7a0f3313 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRISC.java @@ -1,44 +1,58 @@ package com.seaofnodes.simple.node.cpus.riscv; import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; import com.seaofnodes.simple.type.TypeFunPtr; -import java.io.ByteArrayOutputStream; -public class CallRISC extends CallNode implements MachNode{ +public class CallRISC extends CallNode implements MachNode, RIPRelSize { final TypeFunPtr _tfp; final String _name; - CallRISC( CallNode call, TypeFunPtr tfp ) { super(call); - _inputs.pop(); // Pop constant target assert tfp.isConstant(); + _inputs.pop(); // Pop constant target _tfp = tfp; _name = CodeGen.CODE.link(tfp)._name; } + @Override public String op() { return "call"; } @Override public String label() { return op(); } @Override public RegMask regmap(int i) { - return riscv.callInMask(_tfp,i); // Normal argument + return riscv.callInMask(_tfp,i,fun()._maxArgSlot); } - @Override public RegMask outregmap() { return null; } - + @Override public RegMask outregmap() { return riscv.RPC_MASK; } @Override public String name() { return _name; } @Override public TypeFunPtr tfp() { return _tfp; } - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { + @Override public void encoding( Encoding enc ) { + // Short form +/-4K: beq r0,r0,imm12 + // Long form: auipc rX,imm20/32; jal r0,[rX+imm12/32] + enc.relo(this); + enc.add4(riscv.j_type(riscv.OP_JAL, riscv.RPC, 0)); + } + + // Delta is from opcode start + @Override public byte encSize(int delta) { + if( -(1L<<20) <= delta && delta < (1L<<20) ) return 4; + // 2 word encoding needs a tmp register, must teach RA throw Utils.TODO(); } + // Delta is from opcode start + @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { + if( opLen==4 ) { + enc.patch4(opStart,riscv.j_type(riscv.OP_JAL, riscv.RPC, delta)); + } else { + throw Utils.TODO(); + } + } + @Override public void asm(CodeGen code, SB sb) { sb.p(_name).p(" "); for( int i=0; i<nargs(); i++ ) - sb.p(code.reg(arg(i))).p(" "); - sb.unchar(2); - } + sb.p(code.reg(arg(i+2))).p(", "); + sb.unchar(2).p(" ").p(code.reg(fptr())); - @Override public String op() { return "call"; } + } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRRISC.java index 3bae656d..dcc53cbf 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRRISC.java @@ -1,26 +1,25 @@ package com.seaofnodes.simple.node.cpus.riscv; import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; -import java.io.ByteArrayOutputStream; -public class CallRRISC extends CallNode implements MachNode{ +public class CallRRISC extends CallNode implements MachNode { CallRRISC( CallNode call ) { super(call); } - + @Override public String op() { return "callr"; } @Override public String label() { return op(); } @Override public RegMask regmap(int i) { - // Todo: float or int? - return i==_inputs._len + return i==_inputs._len-1 ? riscv.RMASK // Function call target - : riscv.callInMask(tfp(),i); // Normal argument + : riscv.callInMask(tfp(),i,fun()._maxArgSlot); // Normal argument } @Override public RegMask outregmap() { return null; } - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); + @Override public void encoding( Encoding enc ) { + short rpc = enc.reg(this); + short src = enc.reg(in(_inputs._len-1)); + int body = riscv.i_type(riscv.OP_JALR, rpc, 0, src, 0); + enc.add4(body); } @Override public void asm(CodeGen code, SB sb) { @@ -29,7 +28,4 @@ public class CallRRISC extends CallNode implements MachNode{ sb.p(code.reg(arg(i))).p(" "); sb.unchar(2); } - - @Override public String op() { return "callr"; } - } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CastRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CastRISC.java index a0d0df90..c404aaa5 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CastRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CastRISC.java @@ -2,17 +2,17 @@ package com.seaofnodes.simple.node.cpus.riscv; import com.seaofnodes.simple.SB; import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.Encoding; import com.seaofnodes.simple.codegen.RegMask; import com.seaofnodes.simple.node.CastNode; import com.seaofnodes.simple.node.MachNode; -import java.io.ByteArrayOutputStream; public class CastRISC extends CastNode implements MachNode { - CastRISC( CastNode cast ) { super(cast); } + public CastRISC( CastNode cast ) { super(cast); } + @Override public String op() { return label(); } @Override public RegMask regmap(int i) { assert i==1; return RegMask.FULL; } @Override public RegMask outregmap() { return RegMask.FULL; } @Override public int twoAddress( ) { return 1; } - @Override public int encoding(ByteArrayOutputStream bytes) { return 0; /*no encoding*/ } + @Override public void encoding( Encoding enc ) { } @Override public void asm(CodeGen code, SB sb) { _t.print(sb.p(code.reg(in(1))).p(" isa ")); } - @Override public String op() { return label(); } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivFRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivFRISC.java index d360bea6..c67d7b3d 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivFRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivFRISC.java @@ -1,30 +1,19 @@ package com.seaofnodes.simple.node.cpus.riscv; -import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.SB; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; -import java.io.ByteArrayOutputStream; +// fdiv.d public class DivFRISC extends MachConcreteNode implements MachNode{ DivFRISC(Node divf) {super(divf);} - - // Register mask allowed on input i. + @Override public String op() { return "divf"; } @Override public RegMask regmap(int i) { assert i==1 || i==2; return riscv.FMASK; } - // Register mask allowed as a result. 0 for no register. @Override public RegMask outregmap() { return riscv.FMASK; } - - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); - } - + @Override public void encoding( Encoding enc ) { riscv.rf_type(enc,this,riscv.RM.RUP,13); } // Default on double precision for now(64 bits) // General form: "fdiv.d rd = src1 / src2 @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" / ").p(code.reg(in(2))); } - - @Override public String op() { return "divf"; } - } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivRISC.java index ee49a883..8367ee68 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivRISC.java @@ -1,32 +1,17 @@ package com.seaofnodes.simple.node.cpus.riscv; -import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.SB; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.TypeInteger; -import java.io.ByteArrayOutputStream; public class DivRISC extends MachConcreteNode implements MachNode{ - DivRISC(Node div) {super(div);} - - // Register mask allowed on input i. - @Override public RegMask regmap(int i) { - assert i==1 || i==2; - return riscv.RMASK; } - // Register mask allowed as a result. 0 for no register. + public DivRISC(Node div) {super(div);} + @Override public String op() { return "div"; } + @Override public RegMask regmap(int i) { return riscv.RMASK; } @Override public RegMask outregmap() { return riscv.WMASK; } - - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); - } - - // Signed division - // General form: "rd = rs1 / rs" + @Override public void encoding( Encoding enc ) { riscv.r_type(enc,this,4,1); } @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" / ").p(code.reg(in(2))); } - @Override public String op() { return "div"; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FltRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FltRISC.java index 2832ac52..35de7e04 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FltRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FltRISC.java @@ -2,30 +2,46 @@ package com.seaofnodes.simple.node.cpus.riscv; import com.seaofnodes.simple.SB; import com.seaofnodes.simple.Utils; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.ConstantNode; import com.seaofnodes.simple.node.MachNode; -import java.io.ByteArrayOutputStream; +import com.seaofnodes.simple.type.TypeFloat; -public class FltRISC extends ConstantNode implements MachNode{ - FltRISC(ConstantNode con) { super(con); } - @Override public String op() { return "flw"; } - // Register mask allowed on input i. 0 for no register. +public class FltRISC extends ConstantNode implements MachNode, RIPRelSize { + FltRISC(ConstantNode con) { super(con); } + @Override public String op() { return "ld8"; } @Override public RegMask regmap(int i) { return null; } - // General int registers! Must-split into the floats - @Override public RegMask outregmap() { return riscv.RMASK; } + @Override public RegMask outregmap() { return riscv.FMASK; } + @Override public boolean isClone() { return true; } + @Override public FltRISC copy() { return new FltRISC(this); } - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); + @Override public void encoding( Encoding enc ) { + enc.largeConstant(this,_con, 0, -1/*TODO: RISC5 style patching*/); + short dst = (short)(enc.reg(this) - riscv.F_OFFSET); + short tmp = (short)riscv.T6; + // AUIPC dst,#hi20_constant_pool + enc.add4(riscv.u_type(riscv.OP_AUIPC, tmp, 0)); + // Load dst,[dst+#low12_constant_pool] + enc.add4(riscv.i_type(riscv.OP_LOADFP, dst, 0b11, tmp, 0)); + } + @Override public RegMask killmap() { return new RegMask(riscv.T6); } + + // Delta is from opcode start. + // TODO: always size 8? + @Override public byte encSize(int delta) { return 8; } + + // Delta is from opcode start + @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { + short dst = (short)(enc.reg(this) - riscv.F_OFFSET); + short tmp = (short)riscv.T6; + // AUIPC dst,#hi20_constant_pool + enc.patch4(opStart , riscv.u_type(riscv.OP_AUIPC, tmp, delta>>12)); + // Load dst,[dst+#low12_constant_pool] + enc.patch4(opStart+4, riscv.i_type(riscv.OP_LOADFP, dst, 0b11, tmp, delta & 0xFFF)); } - // Human-readable form appended to the SB. Things like the encoding, - // indentation, leading address or block labels not printed here. - // Just something like "ld4\tR17=[R18+12] // Load array base". - // General form: "op\tdst=src+src" @Override public void asm(CodeGen code, SB sb) { - _con.print(sb.p(code.reg(this)).p(" #")); + _con.print(sb.p(code.reg(this)).p(" = #")); } + } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FunRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FunRISC.java index 3b7d9a34..84f0f4c7 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FunRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FunRISC.java @@ -1,21 +1,22 @@ package com.seaofnodes.simple.node.cpus.riscv; +import com.seaofnodes.simple.SB; import com.seaofnodes.simple.Utils; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.FunNode; import com.seaofnodes.simple.node.MachNode; -import java.io.ByteArrayOutputStream; public class FunRISC extends FunNode implements MachNode { FunRISC(FunNode fun) {super(fun);} - // Register mask allowed on input i. 0 for no register. - @Override public RegMask regmap(int i) { return null; } - // Register mask allowed as a result. 0 for no register. - @Override public RegMask outregmap() { return null; } - - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); + @Override public void computeFrameAdjust(CodeGen code, int maxReg) { + super.computeFrameAdjust(code,maxReg); + if( _hasCalls ) // If non-leaf, pad to 16b + _frameAdjust = ((_frameAdjust+8) & -16); + } + @Override public void encoding( Encoding enc ) { + int sz = _frameAdjust; + if( sz == 0 ) return; // Skip if no frame adjust + if( sz >= 1L<<12 ) throw Utils.TODO(); + enc.add4(riscv.i_type(riscv.OP_IMM, riscv.RSP, 0, riscv.RSP, -sz & 0xFFF)); } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/I2F8RISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/I2F8RISC.java index 560d1b38..5519fe5b 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/I2F8RISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/I2F8RISC.java @@ -1,30 +1,23 @@ package com.seaofnodes.simple.node.cpus.riscv; import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.TypeInteger; -import java.io.ByteArrayOutputStream; // fcvt.d.w +// Converts a 32-bit signed integer, in integer register rs1 into a double-precision floating-point number in floating-point register rd. public class I2F8RISC extends MachConcreteNode implements MachNode { I2F8RISC(Node i2f8) {super(i2f8);} - - // Register mask allowed on input i. + @Override public String op() { return "cvtf"; } @Override public RegMask regmap(int i) { assert i==1; return riscv.RMASK; } - // Register mask allowed as a result. 0 for no register. @Override public RegMask outregmap() { return riscv.FMASK; } - - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); + @Override public void encoding( Encoding enc ) { + short dst = (short)(enc.reg(this )-riscv.F_OFFSET); + short src1 = enc.reg(in(1)); + int body = riscv.r_type(riscv.OP_FP,dst,riscv.RM.RNE.ordinal(),src1,0,0x69); + enc.add4(body); } - - // General form: "i2f8 (flt)int_value" @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(this)).p(" = ").p("(flt)").p(code.reg(in(1))); } - - @Override public String op() { return "i2f8"; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ImmRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ImmRISC.java index 02191d05..9303e566 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ImmRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ImmRISC.java @@ -1,40 +1,54 @@ package com.seaofnodes.simple.node.cpus.riscv; import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; import com.seaofnodes.simple.type.TypeInteger; import java.util.BitSet; -import java.io.ByteArrayOutputStream; -public abstract class ImmRISC extends MachConcreteNode implements MachNode { - final int _imm; - ImmRISC( Node add, int imm ) { +abstract public class ImmRISC extends MachConcreteNode implements MachNode { + public final int _imm12; + ImmRISC( Node add, int imm12, boolean pop ) { super(add); - _inputs.pop(); - _imm = imm; + _imm12 = imm12; + // If pop, we are moving from an ideal Node to MachNode; the ideal Node + // inputs got copied here, but we are absorbing the immediate into the + // MachNode and so do not want the constant input. + if( pop ) _inputs.pop(); + // if NOT pop, we are the 2nd-half of a 2-part expansion, reset the + // input edges to no-control + else { _inputs.set(0,null); _inputs.setX(1,add); } } - - // Register mask allowed on input i. + ImmRISC( Node add, int imm12 ) { this(add,imm12,true); } @Override public RegMask regmap(int i) { return riscv.RMASK; } - // Register mask allowed as a result. 0 for no register. @Override public RegMask outregmap() { return riscv.WMASK; } + @Override public boolean isClone() { + // Allow cloning of the 2nd half of a 2-part constant; this might + // trigger cloning the first part also. + return in(1) instanceof LUI || in(1) instanceof AUIPC; + } @Override public StringBuilder _print1(StringBuilder sb, BitSet visited) { + int imm12 = (_imm12<<20)>>20; // Sign extend 12 bits in(1)._print0(sb.append("( "), visited); - return sb.append(String.format(" %s #%d )",glabel(),_imm)); + return sb.append(String.format(" %s #%d )",glabel(),imm12)); } abstract int opcode(); + abstract int func3(); - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); + @Override public void encoding( Encoding enc ) { + short dst = enc.reg(this ); + short src = enc.reg(in(1)); + int body = riscv.i_type(opcode(), dst, func3(), src, _imm12 & 0xFFF); + enc.add4(body); } // General form: "addi rd = rs1 + imm" @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" ").p(glabel()).p(" #").p(_imm); + int imm12 = (_imm12<<20)>>20; // Sign extend 12 bits + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" ").p(glabel()).p(" #").p(imm12); + if( in(1) instanceof LUI lui ) + sb.p(" // #").hex4((int)(((TypeInteger)lui._con).value()) + imm12); } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/IntRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/IntRISC.java index e792b055..70d5c18f 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/IntRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/IntRISC.java @@ -1,33 +1,28 @@ package com.seaofnodes.simple.node.cpus.riscv; import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.ConstantNode; import com.seaofnodes.simple.node.MachNode; -import java.io.ByteArrayOutputStream; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeInteger; -// Integer constants +// 12-bit integer constant. Larger constants are made up in the instruction +// selection by adding with a LUI. public class IntRISC extends ConstantNode implements MachNode { - IntRISC(ConstantNode con) { super(con); } + public IntRISC(ConstantNode con) { super(con); } @Override public String op() { return "ldi"; } - // Register mask allowed on input i. 0 for no register. @Override public RegMask regmap(int i) { return null; } - // General int registers @Override public RegMask outregmap() { return riscv.WMASK; } - @Override public boolean isClone() { return true; } @Override public IntRISC copy() { return new IntRISC(this); } - - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); + @Override public void encoding( Encoding enc ) { + short dst = enc.reg(this); + int val = _con==Type.NIL ? 0 : (int)(((TypeInteger)_con).value() & 0xFFF); + // Explicit truncation of larger immediates; this will sign-extend on + // load and this is handled during instruction selection. + enc.add4(riscv.i_type(riscv.OP_IMM, dst, 0, riscv.ZERO, val)); } - - // Human-readable form appended to the SB. Things like the encoding, - // indentation, leading address or block labels not printed here. - // Just something like "ld4\tR17=[R18+12] // Load array base". - // General form: "op\tdst=src+src" @Override public void asm(CodeGen code, SB sb) { String reg = code.reg(this); _con.print(sb.p(reg).p(" = #")); diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/LUI.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/LUI.java new file mode 100644 index 00000000..75ad6496 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/LUI.java @@ -0,0 +1,32 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.ConstantNode; +import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.type.TypeInteger; + +// Load upper 20bits. +public class LUI extends ConstantNode implements MachNode { + public LUI( int imm20 ) { + super(TypeInteger.constant(imm20)); + assert riscv.imm20Exact((TypeInteger)_con); + } + @Override public RegMask regmap(int i) { return null; } + @Override public RegMask outregmap() { return riscv.WMASK; } + @Override public boolean isClone() { return true; } + @Override public LUI copy() { return new LUI((int)((TypeInteger)_con).value()); } + @Override public String op() { return "lui"; } + @Override public void encoding( Encoding enc ) { + long x = ((TypeInteger)_con).value(); + int imm20 = (int)(x>>12) & 0xFFFFF; + short dst = enc.reg(this); + int lui = riscv.u_type(riscv.OP_LUI, dst, imm20); + enc.add4(lui); + } + + @Override public void asm(CodeGen code, SB sb) { + String reg = code.reg(this); + sb.p(reg).p(" = #").hex4((int)(((TypeInteger)_con).value())); + } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/LoadRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/LoadRISC.java index 3fe8f44e..7b0e9138 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/LoadRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/LoadRISC.java @@ -1,40 +1,29 @@ package com.seaofnodes.simple.node.cpus.riscv; import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.TypeInteger; -import java.io.ByteArrayOutputStream; - // Load memory addressing on RISC // Support imm, reg(direct), or reg+off(indirect) addressing // Base = base - base pointer, offset is added to base // idx = null -// off = off - offset added to base - +// off = off - imm12 added to base public class LoadRISC extends MemOpRISC { - LoadRISC(LoadNode ld, Node base, Node idx, int off) { - super(ld, base, idx, off, 0); - } - - @Override public RegMask regmap(int i) { - return riscv.RMASK; - } - // Wide mask loads both ints and floats; encoding varies. + public LoadRISC(LoadNode ld, Node base, int off) { super(ld, base, off, null); } + @Override public String op() { return "ld" +_sz; } + @Override public RegMask regmap(int i) { return riscv.RMASK; } @Override public RegMask outregmap() { return riscv.MEM_MASK; } - - - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); + @Override public void encoding( Encoding enc ) { + short dst = enc.reg(this ); + short ptr = enc.reg(ptr()); + int op = dst >= riscv.F_OFFSET ? riscv.OP_LOADFP : riscv.OP_LOAD; + if( dst >= riscv.F_OFFSET ) dst -= riscv.F_OFFSET; + enc.add4(riscv.i_type(op, dst, func3(), ptr, _off)); } @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(this)).p(","); asm_address(code,sb); } - - @Override public String op() { return "ld" +_sz; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MemOpRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MemOpRISC.java index bcd7924e..b8eb7644 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MemOpRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MemOpRISC.java @@ -1,57 +1,74 @@ package com.seaofnodes.simple.node.cpus.riscv; -import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.SB; +import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; import com.seaofnodes.simple.type.*; -import java.io.ByteArrayOutputStream; -import java.lang.StringBuilder; import java.util.BitSet; - -public class MemOpRISC extends MemOpNode implements MachNode { - final int _off; // Limit 32 bits - final int _imm; // Limit 32 bits - final char _sz = (char)('0'+(1<<_declaredType.log_size())); - MemOpRISC( MemOpNode mop, Node base, Node idx, int off, int imm) { +public abstract class MemOpRISC extends MemOpNode implements MachNode { + final int _off; // Limit 12 bits + final char _sz; + MemOpRISC(MemOpNode mop, Node base, int off, Node val) { super(mop,mop); - assert base._type instanceof TypeMemPtr && !(base instanceof AddNode); - assert ptr() == base; - _inputs.setX(3,idx); + assert base._type instanceof TypeMemPtr; + _inputs.setX(2, base ); // Base can be an Add, is no longer raw object base + _inputs.setX(3, null); // Never an index + _inputs.setX(4, val ); _off = off; - _imm = imm; - } - // Store-based flavors have a value edge - MemOpRISC( MemOpNode mop, Node base, Node idx, int off, int imm, Node val ) { - this(mop,base,idx,off,imm); - _inputs.setX(4,val); + _sz = (char)('0'+(1<<_declaredType.log_size())); } - Node idx() { return in(3); } - Node val() { return in(4); } // Only for stores, including op-to-memory + @Override public String label() { return op(); } + Node val() { return in(4); } // Only for stores - @Override public StringBuilder _printMach(StringBuilder sb, BitSet visited) { return sb.append(".").append(_name); } + @Override public StringBuilder _printMach(StringBuilder sb, BitSet visited) { return sb.append(".").append(_name); } - @Override public String label() { return op(); } @Override public Type compute() { throw Utils.TODO(); } @Override public Node idealize() { throw Utils.TODO(); } + // func3 is based on load/store size and extend + int func3() { + int func3 = -1; + // no unsigned flavour for store, so both signed and unsigned trigger the same + if(this instanceof StoreRISC) { + if( _declaredType == TypeInteger. I8 || _declaredType == TypeInteger.U8 || _declaredType == TypeInteger.BOOL) func3=0; // SB + if( _declaredType == TypeInteger.I16 || _declaredType == TypeInteger.U16 ) func3=1; // SH + if( _declaredType == TypeInteger.I32 || _declaredType == TypeInteger.U32 || _declaredType instanceof TypeMemPtr) func3=2; // SW + if( _declaredType == TypeInteger.BOT ) func3=3; // SD + if( func3 == -1 ) throw Utils.TODO(); + return func3; + } + if( _declaredType == TypeInteger. I8 ) func3=0; // LB + if( _declaredType == TypeInteger.I16 ) func3=1; // LH + if( _declaredType == TypeInteger.I32 ) func3=2; // LW + if( _declaredType == TypeInteger.BOT ) func3=3; // LD + if( _declaredType == TypeInteger. U8 ) func3=4; // LBU + if( _declaredType == TypeInteger.BOOL) func3=4; // LBU + if( _declaredType == TypeInteger.U16 ) func3=5; // LHU + if( _declaredType == TypeInteger.U32 ) func3=6; // LWU + + // float + if(_declaredType == TypeFloat.F32) func3 = 2; // fLW fSW + if(_declaredType == TypeFloat.F64) func3 = 3; // fLD fSD + if( _declaredType instanceof TypeMemPtr ) func3=3; // 8 byte pointers + if( func3 == -1 ) throw Utils.TODO(); + return func3; + } // Register mask allowed on input i. @Override public RegMask regmap(int i) { + // 0 - ctrl + // 1 - memory + if( i==2 ) return riscv.RMASK; // base + // 2 - index + if( i==4 ) return riscv.MEM_MASK; // value throw Utils.TODO(); } - // Register mask allowed as a result. 0 for no register. - @Override public RegMask outregmap() { throw Utils.TODO(); } - - @Override public int encoding(ByteArrayOutputStream bytes) { throw Utils.TODO(); } - SB asm_address(CodeGen code, SB sb) { sb.p("[").p(code.reg(ptr())).p("+"); - if( idx() != null ) sb.p(code.reg(idx())); - else sb.p(_off); - return sb.p("]"); + return sb.p(_off).p("]"); } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulFRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulFRISC.java index 8a066ab1..c9917b4f 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulFRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulFRISC.java @@ -1,30 +1,17 @@ package com.seaofnodes.simple.node.cpus.riscv; -import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.SB; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; -import java.io.ByteArrayOutputStream; -public class MulFRISC extends MachConcreteNode implements MachNode{ +// fmul.d +public class MulFRISC extends MachConcreteNode implements MachNode { MulFRISC(Node mulf) {super(mulf);} - - // Register mask allowed on input i. + @Override public String op() { return "mulf"; } @Override public RegMask regmap(int i) { assert i==1 || i==2; return riscv.FMASK; } - // Register mask allowed as a result. 0 for no register. @Override public RegMask outregmap() { return riscv.FMASK; } - - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); - } - - // Default on double precision for now(64 bits) - // General form: "fmul.d rd = src1 * src2 + @Override public void encoding( Encoding enc ) { riscv.rf_type(enc,this,riscv.RM.RNE,9); } @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" * ").p(code.reg(in(2))); } - - @Override public String op() { return "mulf"; } - } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulRISC.java index 0e2d35a3..a7961627 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulRISC.java @@ -1,32 +1,17 @@ package com.seaofnodes.simple.node.cpus.riscv; -import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.SB; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.TypeInteger; -import java.io.ByteArrayOutputStream; - // mulh signed multiply instruction(no-imm form) public class MulRISC extends MachConcreteNode implements MachNode{ - MulRISC(Node mul) {super(mul);} - - // Register mask allowed on input i. + public MulRISC(Node mul) {super(mul);} + @Override public String op() { return "mul"; } @Override public RegMask regmap(int i) { assert i==1 || i==2; return riscv.RMASK; } - // Register mask allowed as a result. 0 for no register. @Override public RegMask outregmap() { return riscv.WMASK; } - - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); - } - - // General form: "rd = rs1 * rs2" + @Override public void encoding( Encoding enc ) { riscv.r_type(enc,this,0,1); } @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" * ").p(code.reg(in(2))); } - - @Override public String op() { return "mul"; } - } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NegRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NegRISC.java new file mode 100644 index 00000000..77f4a497 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NegRISC.java @@ -0,0 +1,20 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.SB; +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; + +public class NegRISC extends MachConcreteNode implements MachNode { + public NegRISC( Node neg ) { super(neg); } + @Override public String op() { return "neg"; } + @Override public RegMask regmap(int i) { assert i==1; return riscv.RMASK; } + @Override public RegMask outregmap() { return riscv.WMASK; } + @Override public void encoding( Encoding enc ) { + short dst = enc.reg(this ); + short src = enc.reg(in(1)); + riscv.r_type(riscv.OP,dst,0,0,src,0x20); + } + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = -").p(code.reg(in(1))); + } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NewRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NewRISC.java index 53ad0af2..e395a811 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NewRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NewRISC.java @@ -1,29 +1,46 @@ package com.seaofnodes.simple.node.cpus.riscv; import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; -import java.io.ByteArrayOutputStream; +import com.seaofnodes.simple.print.ASMPrinter; -public class NewRISC extends NewNode implements MachNode { +public class NewRISC extends NewNode implements MachNode, RIPRelSize { // A pre-zeroed chunk of memory. NewRISC( NewNode nnn ) { super(nnn); } - // Size and pointer result in standard calling convention; null for all the - // memory aliases edges - @Override public RegMask regmap(int i) { return i == 1 ? riscv.A0_MASK : null; } - @Override public RegMask outregmap(int i) { return i == 1 ? riscv.A0_MASK : null; } - @Override public RegMask outregmap() { return null; } + @Override public void encoding( Encoding enc ) { + // Generic external encoding; 2 ops. + enc.external(this,"calloc"); + // A1 is a caller-save, allowed to crush building external address + // auipc + enc.add4(riscv.i_type(riscv.OP_IMM , _arg2Reg , 0, riscv.ZERO, 1)); + enc.add4(riscv.u_type(riscv.OP_AUIPC, riscv.A2 , 0)); + enc.add4(riscv.i_type(riscv.OP_JALR , riscv.RPC, 0, riscv.A2, 0)); + } + + // Patch is for running "new" in a JIT. + // Delta is from opcode start + @Override public byte encSize(int delta) { return 4*3; } - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); + // Patch is for running "new" in a JIT. + // Delta is from opcode start + @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { + // Negative patches are JIT emulator targets. + if( opStart+delta < 0 ) { + delta -= 4; // Offset from the AUIPC start + int imm20 = delta>>12; + if( ((delta>>11)&1)==1 ) imm20++; // Correct accidental sign extension + int imm12 = delta&0xFFF; + enc.patch4(opStart+4,riscv.u_type(riscv.OP_AUIPC, riscv.A2 , imm20)); + enc.patch4(opStart+8,riscv.i_type(riscv.OP_JALR , riscv.RPC, 0, riscv.A2, imm12)); + } else + throw Utils.TODO(); } - // General form: "alloc #bytes" + // General form: "alloc #bytes PC" @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(size())); + sb.p("ldi a1=#1\n"); + sb.p("auipc a2=#calloc\n"); + sb.p("call a2+#calloc, a0"); } - - @Override public String op() { return "alloc"; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NotRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NotRISC.java index 6d30cd1c..9cbd5025 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NotRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NotRISC.java @@ -1,25 +1,20 @@ package com.seaofnodes.simple.node.cpus.riscv; -import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.LRG; -import com.seaofnodes.simple.codegen.RegMask; -import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.SB; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.MachConcreteNode; -import com.seaofnodes.simple.type.TypeInteger; -import java.io.ByteArrayOutputStream; +import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.node.NotNode; public class NotRISC extends MachConcreteNode implements MachNode { - NotRISC(NotNode not) {super(not);} + public NotRISC(NotNode not) { super(not); } + @Override public String op() { return "not"; } @Override public RegMask regmap(int i) { return riscv.RMASK; } - @Override public RegMask outregmap() { return riscv.RMASK; } - - @Override public int twoAddress( ) { return 0; } - - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); + @Override public RegMask outregmap() { return riscv.WMASK; } + @Override public void encoding( Encoding enc ) { + short dst = enc.reg(this ); + short src = enc.reg(in(1)); + enc.add4(riscv.i_type(riscv.OP_IMM, dst, 2, src, 1)); } - @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(this)); } - @Override public String op() { return "not"; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrIRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrIRISC.java index f41d7162..f6a888d9 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrIRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrIRISC.java @@ -4,8 +4,9 @@ import com.seaofnodes.simple.Utils; import com.seaofnodes.simple.node.Node; public class OrIRISC extends ImmRISC { - OrIRISC( Node and, int imm) { super(and,imm); } - @Override int opcode() { throw Utils.TODO(); } + public OrIRISC( Node and, int imm) { super(and,imm); } + @Override int opcode() { return riscv.OP_IMM; } + @Override int func3() { return 6; } @Override public String glabel() { return "|"; } @Override public String op() { return "ori"; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrRISC.java index 7f808f90..103d4c7d 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrRISC.java @@ -1,39 +1,16 @@ package com.seaofnodes.simple.node.cpus.riscv; import com.seaofnodes.simple.SB; -import com.seaofnodes.simple.Utils; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; -import com.seaofnodes.simple.node.MachConcreteNode; -import com.seaofnodes.simple.node.MachNode; -import com.seaofnodes.simple.node.Node; -import java.io.ByteArrayOutputStream; +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; public class OrRISC extends MachConcreteNode implements MachNode { - OrRISC(Node or) { - super(or); - } - - // Register mask allowed on input i. - // This is the normal calling convention - @Override public RegMask regmap(int i) { - assert i==1 || i==2; - return riscv.RMASK; - } - - // Register mask allowed as a result. 0 for no register. + public OrRISC(Node or) { super(or); } + @Override public String op() { return "or"; } + @Override public RegMask regmap(int i) { return riscv.RMASK; } @Override public RegMask outregmap() { return riscv.WMASK; } - - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); - } - - // General form - // General form: # rd = rs1 | rs2 + @Override public void encoding( Encoding enc ) { riscv.r_type(enc,this,6,0); } @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" | ").p(code.reg(in(2))); } - - @Override public String op() { return "or"; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ParmRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ParmRISC.java index 75674f37..0f8db4e5 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ParmRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ParmRISC.java @@ -1,26 +1,17 @@ package com.seaofnodes.simple.node.cpus.riscv; -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.RegMask; import com.seaofnodes.simple.node.MachNode; import com.seaofnodes.simple.node.ParmNode; -import java.io.ByteArrayOutputStream; public class ParmRISC extends ParmNode implements MachNode { final RegMask _rmask; ParmRISC(ParmNode parm) { super(parm); - // Assume int - _rmask = riscv.callInMask(fun().sig(),_idx); + _rmask = riscv.callInMask(fun().sig(),_idx,0); } - - // Register mask allowed on input i. 0 for no register. - @Override public RegMask regmap(int i) { return _rmask; } - // Register mask allowed as a result. Calling convention register + @Override public RegMask regmap(int i) { return null; } @Override public RegMask outregmap() { return _rmask; } - - // Encoding is appended into the byte array. Returns size - @Override public int encoding(ByteArrayOutputStream bytes) { return 0; } + @Override public void encoding( Encoding enc ) { } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ProjRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ProjRISC.java index 42d5dc9b..2f9ac469 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ProjRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ProjRISC.java @@ -1,25 +1,15 @@ package com.seaofnodes.simple.node.cpus.riscv; -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.RegMask; import com.seaofnodes.simple.node.MachNode; import com.seaofnodes.simple.node.ProjNode; -import java.io.ByteArrayOutputStream; public class ProjRISC extends ProjNode implements MachNode { ProjRISC(ProjNode p) { super(p); } - - // Register mask allowed on input i. 0 for no register. @Override public RegMask regmap(int i) { return null; } - // Register mask allowed as a result. @Override public RegMask outregmap() { return ((MachNode)in(0)).outregmap(_idx); } - - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); - } + @Override public void encoding( Encoding enc ) { } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/RetRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/RetRISC.java index c9fa7fac..8a8186cd 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/RetRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/RetRISC.java @@ -2,47 +2,20 @@ package com.seaofnodes.simple.node.cpus.riscv; import com.seaofnodes.simple.*; import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.Encoding; import com.seaofnodes.simple.codegen.RegMask; import com.seaofnodes.simple.node.FunNode; import com.seaofnodes.simple.node.ReturnNode; import com.seaofnodes.simple.node.MachNode; -import com.seaofnodes.simple.type.TypeFloat; -import java.io.ByteArrayOutputStream; - -public class RetRISC extends ReturnNode implements MachNode{ - RetRISC(ReturnNode ret, FunNode fun) { super(ret, fun); fun.setRet(this); } - - // Register mask allowed on input i. - // This is the normal calling convention - @Override public RegMask regmap(int i) { return riscv.retMask(_fun.sig(),i); } - // Register mask allowed as a result. 0 for no register. - @Override public RegMask outregmap() { return null; } - - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); +public class RetRISC extends ReturnNode implements MachNode { + public RetRISC(ReturnNode ret, FunNode fun) { super(ret, fun); fun.setRet(this); } + @Override public void encoding( Encoding enc ) { + int sz = fun()._frameAdjust; + if( sz >= 1L<<12 ) throw Utils.TODO(); + if( sz != 0 ) + enc.add4(riscv.i_type(riscv.OP_IMM, riscv.RSP, 0, riscv.RSP, sz & 0xFFF)); + short rpc = enc.reg(rpc()); + enc.add4(riscv.i_type(riscv.OP_JALR, riscv.ZERO, 0, rpc, 0)); } - - @Override public void asm(CodeGen code, SB sb) { - // Post code-gen, just print the "ret" - if( code._phase.ordinal() <= CodeGen.Phase.RegAlloc.ordinal() ) - // Prints return reg (either A0 or FA0), RPC (always R1) and then - // the callee-save registers. - for( int i=2; i<nIns(); i++ ) - sb.p(code.reg(in(i))).p(" "); - - // Post-allocation, if we did not get the expected return register, print what we got - else if( code._regAlloc.regnum(in(3)) != riscv.RPC ) - sb.p("[").p(code.reg(in(3))).p("]"); - } - - // Correct Nodes outside the normal edges - @Override public void postSelect() { - FunNode fun = (FunNode)rpc().in(0); - _fun = fun; - fun.setRet(this); - } - - @Override public String op() { return "ret"; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetFRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetFRISC.java index 3f9fc192..9ee19e42 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetFRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetFRISC.java @@ -1,15 +1,14 @@ package com.seaofnodes.simple.node.cpus.riscv; import com.seaofnodes.simple.SB; -import com.seaofnodes.simple.Utils; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.BoolNode; import com.seaofnodes.simple.node.MachConcreteNode; import com.seaofnodes.simple.node.MachNode; -import java.io.ByteArrayOutputStream; -// corresponds to slt,sltu,slti,sltiu, seqz +// corresponds to feq.d = equal +// fle.d = less than or equal +// flt.d = less than public class SetFRISC extends MachConcreteNode implements MachNode { final String _bop; // One of <,<=,== SetFRISC( BoolNode bool ) { @@ -17,17 +16,18 @@ public class SetFRISC extends MachConcreteNode implements MachNode { assert bool.isFloat(); _bop = bool.op(); } + @Override public String op() { return "set"+_bop; } @Override public RegMask regmap(int i) { return riscv.FMASK; } @Override public RegMask outregmap() { return riscv.WMASK; } - - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); + @Override public void encoding( Encoding enc ) { + short dst = enc.reg(this ); + short src1 = (short)(enc.reg(in(1))-riscv.F_OFFSET); + short src2 = (short)(enc.reg(in(2))-riscv.F_OFFSET); + int body = riscv.r_type(riscv.OP_FP,dst,riscv.fsetop(_bop),src1,src2,0b1010001/*FCMP*/); + enc.add4(body); } @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(in(1))).p(" ").p(_bop).p(" ").p(code.reg(in(2))); + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" ").p(_bop).p(" ").p(code.reg(in(2))); } - - @Override public String op() { return "set"+_bop; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetIRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetIRISC.java index 3c0f76ae..f270bf12 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetIRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetIRISC.java @@ -1,17 +1,17 @@ package com.seaofnodes.simple.node.cpus.riscv; import com.seaofnodes.simple.Utils; -import com.seaofnodes.simple.node.BoolNode; +import com.seaofnodes.simple.node.Node; -// corresponds to slt,sltu,slti,sltiu, seqz +// corresponds to slti,sltiu public class SetIRISC extends ImmRISC { - final String _bop; // One of <,<=,== - SetIRISC( BoolNode bool, int imm ) { - super(bool,imm); - assert !bool.isFloat(); - _bop = bool.op(); + final boolean _unsigned; // slti vs sltiu + public SetIRISC( Node src, int imm12, boolean unsigned ) { + super(src,imm12); + _unsigned = unsigned; } - @Override int opcode() { throw Utils.TODO(); } - @Override public String glabel() { return _bop; } - @Override public String op() { return "set"+_bop; } + @Override public String op() { return "slt" + (_unsigned ? "u":"") + "i"; } + @Override public String glabel() { return "<"+ (_unsigned ? "u":""); } + @Override int opcode() { return 0b0010011; } + @Override int func3() { return _unsigned ? 0b011 : 0b010; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetRISC.java index fb7650c7..8d4a2f71 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetRISC.java @@ -1,33 +1,20 @@ package com.seaofnodes.simple.node.cpus.riscv; import com.seaofnodes.simple.SB; -import com.seaofnodes.simple.Utils; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; -import com.seaofnodes.simple.node.BoolNode; -import com.seaofnodes.simple.node.MachConcreteNode; -import com.seaofnodes.simple.node.MachNode; -import java.io.ByteArrayOutputStream; +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; -// corresponds to slt,sltu,slti,sltiu, seqz +// corresponds to slt sltu public class SetRISC extends MachConcreteNode implements MachNode { - final String _bop; // One of <,<=,== - SetRISC( BoolNode bool ) { - super(bool); - assert !bool.isFloat(); - _bop = bool.op(); - } - @Override public RegMask regmap(int i) { throw Utils.TODO(); } + final boolean _unsigned; + public SetRISC( Node cmp, boolean unsigned ) { super(cmp); _unsigned=unsigned; } + @Override public String op() { return "slt" + (_unsigned ? "u":""); } + @Override public RegMask regmap(int i) { return riscv.RMASK; } @Override public RegMask outregmap() { return riscv.WMASK; } - - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); + @Override public void encoding( Encoding enc ) { + riscv.r_type(enc,this,_unsigned ? 0b011 : 0b010,0); } - @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(in(1))).p(" ").p(_bop).p(" ").p(code.reg(in(2))); + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" < ").p(code.reg(in(2))); } - - @Override public String op() { return "set"+_bop; } -} +}
\ No newline at end of file diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllIRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllIRISC.java index b5202908..4edecc04 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllIRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllIRISC.java @@ -4,8 +4,9 @@ import com.seaofnodes.simple.Utils; import com.seaofnodes.simple.node.Node; public class SllIRISC extends ImmRISC { - SllIRISC( Node and, int imm) { super(and,imm); } - @Override int opcode() { throw Utils.TODO(); } + public SllIRISC( Node and, int imm) { super(and,imm); } + @Override int opcode() { return riscv.OP_IMM; } + @Override int func3() { return 1; } @Override public String glabel() { return "<<"; } @Override public String op() { return "slli"; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllRISC.java index 447f604a..87e6693e 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllRISC.java @@ -1,37 +1,17 @@ package com.seaofnodes.simple.node.cpus.riscv; -import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.SB; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.Type; -import com.seaofnodes.simple.type.TypeInteger; -import java.io.ByteArrayOutputStream; -import java.util.BitSet; -public class SllRISC extends MachConcreteNode implements MachNode{ - SllRISC(Node sll) {super(sll);} - - // Register mask allowed on input i. - // This is the normal calling convention - @Override public RegMask regmap(int i) { - //assert i==1; - return riscv.RMASK; } - // Register mask allowed as a result. 0 for no register. +// Shift left logical +public class SllRISC extends MachConcreteNode implements MachNode { + public SllRISC(Node sll) { super(sll); } + @Override public String op() { return "shl"; } + @Override public RegMask regmap(int i) { return riscv.RMASK; } @Override public RegMask outregmap() { return riscv.WMASK; } - - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); - } - - // General form - // General form: "sll rd, rs1, rs2" + @Override public void encoding( Encoding enc ) { riscv.r_type(enc,this,1,0); } @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" << ").p(code.reg(in(2))); - } - - @Override public String op() { return "sll"; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SplitRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SplitRISC.java index b5076ba0..569fa2e0 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SplitRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SplitRISC.java @@ -1,21 +1,66 @@ package com.seaofnodes.simple.node.cpus.riscv; +import com.seaofnodes.simple.SB; import com.seaofnodes.simple.Utils; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.FunNode; import com.seaofnodes.simple.node.Node; import com.seaofnodes.simple.node.SplitNode; -import java.io.ByteArrayOutputStream; public class SplitRISC extends SplitNode { SplitRISC( String kind, byte round ) { super(kind,round, new Node[2]); } - - // Register mask allowed on input i. @Override public RegMask regmap(int i) { return riscv.SPLIT_MASK; } - // Register mask allowed as a result. 0 for no register. @Override public RegMask outregmap() { return riscv.SPLIT_MASK; } + @Override public void encoding( Encoding enc ) { + short dst = enc.reg(this ); + short src = enc.reg(in(1)); + + // Flag for being either XMM or stack + boolean dstX = dst >= riscv.F_OFFSET; + boolean srcX = src >= riscv.F_OFFSET; + + if( dst >= riscv.MAX_REG ) { + // Store to SP + if( src >= riscv.MAX_REG ) { + throw Utils.TODO(); // Very rare stack-stack move + } + int off = enc._fun.computeStackOffset(enc._code,dst); + int op = srcX ? riscv.OP_STOREFP : riscv.OP_STORE; + if( srcX ) src -= riscv.F_OFFSET; + enc.add4(riscv.s_type(op, 0b011, riscv.RSP, src, off)); + return; + } + if( src >= riscv.MAX_REG ) { + // Load from SP + int off = enc._fun.computeStackOffset(enc._code,src); + int op = dstX ? riscv.OP_LOADFP : riscv.OP_LOAD; + if( dstX ) dst -= riscv.F_OFFSET; + enc.add4(riscv.i_type(op, dst, 0b011, riscv.RSP, off)); + return; + } + // pick opcode based on regs + if( !dstX && !srcX ) { + // GPR->GPR + enc.add4(riscv.r_type(riscv.OP,dst,0,src,riscv.ZERO,0)); + } else if( dstX && srcX ) { + // FPR->FPR + src -= riscv.F_OFFSET; + dst -= riscv.F_OFFSET; + enc.add4(riscv.r_type(riscv.OP_FP, dst, 0b000, src, src, 0b0010101)); // dst = FMIN(src,src) + } else if(!srcX && dstX) { + //GPR->FPR + // fmv.d.x + enc.add4(riscv.r_type(riscv.OP_FP, dst - riscv.F_OFFSET, 0, src, 0, 0b1111001)); + } else if(srcX && !dstX) { + //FPR->GPR + //fmv.x.d + enc.add4(riscv.r_type(riscv.OP_FP, dst, 0, src - riscv.F_OFFSET, 0, 0b1110001)); + } + } - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); + // General form: "mov dst = src" + @Override public void asm(CodeGen code, SB sb) { + FunNode fun = code._encoding==null ? null : code._encoding._fun; + sb.p(code.reg(this,fun)).p(" = ").p(code.reg(in(1),fun)); } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraIRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraIRISC.java index d5ab5e08..26404e95 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraIRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraIRISC.java @@ -4,8 +4,9 @@ import com.seaofnodes.simple.Utils; import com.seaofnodes.simple.node.Node; public class SraIRISC extends ImmRISC { - SraIRISC( Node and, int imm) { super(and,imm); } - @Override int opcode() { throw Utils.TODO(); } + public SraIRISC( Node and, int imm) { super(and,imm); } + @Override int opcode() { return riscv.OP_IMM; } + @Override int func3() { return 5;} @Override public String glabel() { return ">>"; } @Override public String op() { return "srai"; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraRISC.java index 0726e292..1e930e72 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraRISC.java @@ -1,38 +1,17 @@ package com.seaofnodes.simple.node.cpus.riscv; import com.seaofnodes.simple.SB; -import com.seaofnodes.simple.Utils; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; -import com.seaofnodes.simple.node.MachConcreteNode; -import com.seaofnodes.simple.node.MachNode; -import com.seaofnodes.simple.node.Node; -import com.seaofnodes.simple.type.TypeInteger; -import java.io.ByteArrayOutputStream; +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; // Right Shift Arithmetic public class SraRISC extends MachConcreteNode implements MachNode { - - SraRISC(Node sra) {super(sra);} - - // Register mask allowed on input i. - // This is the normal calling convention - @Override public RegMask regmap(int i) { - // assert i==1; - return riscv.RMASK; } - // Register mask allowed as a result. 0 for no register. + public SraRISC(Node sra) { super(sra); } + @Override public String op() { return "sar"; } + @Override public RegMask regmap(int i) { return riscv.RMASK; } @Override public RegMask outregmap() { return riscv.WMASK; } - - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); - } - - // General form - // General form: "sra rd, rs1, rs2" + @Override public void encoding( Encoding enc ) { riscv.r_type(enc,this,0b101,0b00100000); } @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" >> ").p(code.reg(in(2))); } - - @Override public String op() { return "sra"; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlIRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlIRISC.java index a4eb99bc..ed697678 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlIRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlIRISC.java @@ -4,8 +4,9 @@ import com.seaofnodes.simple.Utils; import com.seaofnodes.simple.node.Node; public class SrlIRISC extends ImmRISC { - SrlIRISC( Node and, int imm) { super(and,imm); } - @Override int opcode() { throw Utils.TODO(); } + public SrlIRISC( Node and, int imm, boolean pop) { super(and,imm,pop); } + @Override int opcode() { return riscv.OP_IMM; } + @Override int func3() { return 5; } @Override public String glabel() { return ">>>"; } @Override public String op() { return "srli"; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlRISC.java index d1435540..47712585 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlRISC.java @@ -1,38 +1,17 @@ package com.seaofnodes.simple.node.cpus.riscv; -import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.SB; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.Type; -import com.seaofnodes.simple.type.TypeInteger; -import java.io.ByteArrayOutputStream; -import java.util.BitSet; -import java.lang.StringBuilder; // Right Shift Logical public class SrlRISC extends MachConcreteNode implements MachNode { - SrlRISC(Node srli) {super(srli);} - - // Register mask allowed on input i. - // This is the normal calling convention - @Override public RegMask regmap(int i) { - // assert i==1; - return riscv.RMASK; } - // Register mask allowed as a result. 0 for no register. + public SrlRISC(Node srli) { super(srli); } + @Override public String op() { return "shr"; } + @Override public RegMask regmap(int i) { return riscv.RMASK; } @Override public RegMask outregmap() { return riscv.WMASK; } - - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); - } - - // General form - // General form: "srl rd, rs1, rs2" + @Override public void encoding( Encoding enc ) { riscv.r_type(enc,this,7,0); } @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" >>> ").p(code.reg(in(2))); } - - @Override public String op() { return "srl"; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/StoreRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/StoreRISC.java index c57cf11d..7305c4d9 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/StoreRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/StoreRISC.java @@ -2,14 +2,9 @@ package com.seaofnodes.simple.node.cpus.riscv; import com.seaofnodes.simple.SB; import com.seaofnodes.simple.Utils; -import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.codegen.RegMask; -import com.seaofnodes.simple.node.MachConcreteNode; -import com.seaofnodes.simple.node.MachNode; -import com.seaofnodes.simple.node.Node; -import com.seaofnodes.simple.node.StoreNode; -import java.io.ByteArrayOutputStream; -import java.util.BitSet; +import com.seaofnodes.simple.node.*; // Store memory addressing on ARM // Support imm, reg(direct), or reg+off(indirect) addressing @@ -24,33 +19,29 @@ import java.util.BitSet; // sw rs2,offset(rs1) public class StoreRISC extends MemOpRISC { - StoreRISC( StoreNode st, Node base, Node idx, int off, int imm, Node val ) { - super(st, base, idx, off, imm, val); - } - - // Wider mask to store both GPRs and FPRs + StoreRISC( StoreNode st, Node base, int off, Node val ) { super(st, base, off, val); } + @Override public String op() { return "st"+_sz; } @Override public RegMask regmap(int i) { - if( i==1 ) return riscv.MEM_MASK; - if( i==2 ) return riscv.RMASK; - if( i==3 ) return riscv.RMASK; - if( i==4 ) return riscv.RMASK; - throw Utils.TODO(); + // 0 - ctrl + if( i==1 ) return null; // mem + if( i==2 ) return riscv.RMASK; // ptr + // 2 - index + if( i==4 ) return riscv.MEM_MASK; // Wide mask to store GPR and FPR + return null; // Anti-dependence } - - - // Register mask allowed as a result. 0 for no register. @Override public RegMask outregmap() { return null; } - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); + @Override public void encoding( Encoding enc ) { + short val = enc.reg(val()); + short ptr = enc.reg(ptr()); + int op = val >= riscv.F_OFFSET ? riscv.OP_STOREFP : riscv.OP_STORE; + if( val >= riscv.F_OFFSET ) val -= riscv.F_OFFSET; + enc.add4(riscv.s_type(op, func3()&7, ptr, val == -1 ? riscv.ZERO : val, _off)); } @Override public void asm(CodeGen code, SB sb) { asm_address(code,sb).p(","); - if( val()==null ) sb.p("#").p(_imm); + if( val()==null ) sb.p("#").p("0"); else sb.p(code.reg(val())); } - - @Override public String op() { return "st"+_sz; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubFRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubFRISC.java index e6fe3012..1e1b43c7 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubFRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubFRISC.java @@ -1,30 +1,17 @@ package com.seaofnodes.simple.node.cpus.riscv; -import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.SB; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.TypeInteger; -import java.io.ByteArrayOutputStream; -public class SubFRISC extends MachConcreteNode implements MachNode{ +// fsub.d +public class SubFRISC extends MachConcreteNode implements MachNode { SubFRISC(Node subf) {super(subf);} - - // Register mask allowed on input i. + @Override public String op() { return "subf"; } @Override public RegMask regmap(int i) { assert i==1 || i==2; return riscv.FMASK; } - // Register mask allowed as a result. 0 for no register. @Override public RegMask outregmap() { return riscv.FMASK; } - - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); - } - - // Default on double precision for now(64 bits) - // General form: "fsub.d rd = src1 - src2 + @Override public void encoding( Encoding enc ) { riscv.rf_type(enc,this,riscv.RM.RNE,5); } @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" + ").p(code.reg(in(2))); } - - @Override public String op() { return "subf"; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubRISC.java index c37aea98..7ab4f904 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubRISC.java @@ -1,29 +1,16 @@ package com.seaofnodes.simple.node.cpus.riscv; import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.TypeInteger; -import java.io.ByteArrayOutputStream; public class SubRISC extends MachConcreteNode implements MachNode { - SubRISC( Node sub ) { super(sub); } - - // Register mask allowed on input i. + public SubRISC( Node sub ) { super(sub); } + @Override public String op() { return "sub"; } @Override public RegMask regmap(int i) { assert i==1 || i==2; return riscv.RMASK; } - // Register mask allowed as a result. 0 for no register. @Override public RegMask outregmap() { return riscv.WMASK; } - - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); - } - - // General form: "sub # rd = rs1 - rs2" + @Override public void encoding( Encoding enc ) { riscv.r_type(enc,this,0,0x20); } @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" - ").p(code.reg(in(2))); } - - @Override public String op() { return "sub"; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/TFPRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/TFPRISC.java index 23fb6a74..17613009 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/TFPRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/TFPRISC.java @@ -1,39 +1,55 @@ package com.seaofnodes.simple.node.cpus.riscv; import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.ConstantNode; import com.seaofnodes.simple.node.MachNode; -import com.seaofnodes.simple.type.Type; import com.seaofnodes.simple.type.TypeFunPtr; -import java.io.ByteArrayOutputStream; -public class TFPRISC extends ConstantNode implements MachNode{ - TFPRISC(ConstantNode con) {super(con);} - @Override public String op() { return _con == Type.NIL ? "xor" : "ldx"; } - // Register mask allowed on input i. 0 for no register. +public class TFPRISC extends ConstantNode implements MachNode, RIPRelSize { + TFPRISC(ConstantNode con) { super(con); } + @Override public String op() { return "ldx"; } @Override public RegMask regmap(int i) { return null; } - // General int registers @Override public RegMask outregmap() { return riscv.WMASK; } - @Override public boolean isClone() { return true; } @Override public TFPRISC copy() { return new TFPRISC(this); } + @Override public void encoding( Encoding enc ) { + enc.relo(this); + // TODO: 1 op encoding, plus a TODO if it does not fit + short dst = enc.reg(this); + TypeFunPtr tfp = (TypeFunPtr)_con; + // auipc t0,0 + int auipc = riscv.u_type(riscv.OP_AUIPC, dst, 0); + // addi t1,t0 + #0 + int addi = riscv.i_type(riscv.OP_IMM, dst, 0, dst, 0); + enc.add4(auipc); + enc.add4(addi); + } - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { + @Override public byte encSize(int delta) { + if( -(1L<<11) <= delta && delta < (1L<<11) ) return 4; throw Utils.TODO(); } - // Human-readable form appended to the SB. Things like the encoding, - // indentation, leading address or block labels not printed here. - // Just something like "ld4\tR17=[R18+12] // Load array base". - // General form: "op\tdst=src+src" + // Delta is from opcode start + @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { + short rpc = enc.reg(this); + if( opLen==8 ) { + // AUIPC (upper 20 bits) + // opstart of add + int next = opStart + opLen; + enc.patch4(opStart,riscv.u_type(riscv.OP_AUIPC, rpc, delta)); + // addi(low 12 bits) + enc.patch4(next,riscv.i_type(riscv.OP_IMM, rpc, 0, rpc, delta & 0xFFF)); + // addi + } else { + // should not happen as one instruction is 4 byte, and TFP arm encodes 2. + throw Utils.TODO(); + } + } + @Override public void asm(CodeGen code, SB sb) { - String reg = code.reg(this); - if( _con == Type.NIL ) - sb.p(reg).p(",").p(reg); - else - _con.print(sb.p(reg).p(" #")); + _con.print(sb.p(code.reg(this)).p(" #")); } + } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/UJmpRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/UJmpRISC.java new file mode 100644 index 00000000..6473d81c --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/UJmpRISC.java @@ -0,0 +1,46 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.Type; +import java.util.BitSet; + +// unconditional jump +public class UJmpRISC extends CFGNode implements MachNode, RIPRelSize { + @Override public String op() { return "jmp"; } + @Override public String label() { return op(); } + @Override public StringBuilder _print1( StringBuilder sb, BitSet visited ) { return sb.append("jmp "); } + @Override public RegMask regmap(int i) {return null; } + @Override public RegMask outregmap() { return null; } + @Override public Type compute() { throw Utils.TODO(); } + @Override public Node idealize() { throw Utils.TODO(); } + @Override public void encoding( Encoding enc ) { + // Short form +/-4K: beq r0,r0,imm12 + // Long form: auipc rX,imm20/32; jal r0,[rX+imm12/32] + enc.jump(this,uctrl()); + enc.add4(riscv.j_type(riscv.OP_JAL, 0, 0)); + } + + // Delta is from opcode start + @Override public byte encSize(int delta) { + if( -(1L<<20) <= delta && delta < (1L<<20) ) return 4; + // 2 word encoding needs a tmp register, must teach RA + throw Utils.TODO(); + } + + // Delta is from opcode start + @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { + if( opLen==4 ) { + enc.patch4(opStart,riscv.j_type(riscv.OP_JAL, 0, delta)); + } else { + throw Utils.TODO(); + } + } + + @Override public void asm(CodeGen code, SB sb) { + CFGNode target = uctrl(); + //assert target.nOuts() > 1; // Should optimize jmp to empty targets + sb.p(label(target)); + } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorIRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorIRISC.java index a2e32f06..61f25be2 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorIRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorIRISC.java @@ -4,8 +4,9 @@ import com.seaofnodes.simple.Utils; import com.seaofnodes.simple.node.Node; public class XorIRISC extends ImmRISC { - XorIRISC( Node and, int imm) { super(and,imm); } - @Override int opcode() { throw Utils.TODO(); } + public XorIRISC( Node and, int imm) { super(and,imm); } + @Override int opcode() { return riscv.OP_IMM; } + @Override int func3() { return 4; } @Override public String glabel() { return "^"; } @Override public String op() { return "xori"; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorRISC.java index f4ecb430..725d2b78 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorRISC.java @@ -1,41 +1,16 @@ package com.seaofnodes.simple.node.cpus.riscv; import com.seaofnodes.simple.SB; -import com.seaofnodes.simple.Utils; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; -import com.seaofnodes.simple.node.MachConcreteNode; -import com.seaofnodes.simple.node.MachNode; -import com.seaofnodes.simple.node.Node; -import com.seaofnodes.simple.type.TypeInteger; -import java.io.ByteArrayOutputStream; +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; public class XorRISC extends MachConcreteNode implements MachNode { - - XorRISC(Node or) { - super(or); - } - - // Register mask allowed on input i. - // This is the normal calling convention - @Override public RegMask regmap(int i) { - assert i==1 || i==2; - return riscv.RMASK; - } - - // Register mask allowed as a result. 0 for no register. + public XorRISC(Node or) { super(or); } + @Override public String op() { return "xor"; } + @Override public RegMask regmap(int i) { return riscv.RMASK; } @Override public RegMask outregmap() { return riscv.WMASK; } - - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); - } - - // General form - // General form: # # rd = rs1 ^ rs2 + @Override public void encoding( Encoding enc ) { riscv.r_type(enc,this,4,0); } @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" ^ ").p(code.reg(in(2))); - } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java index 83706cbf..54c6b828 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java @@ -1,49 +1,63 @@ package com.seaofnodes.simple.node.cpus.riscv; import com.seaofnodes.simple.Utils; -import com.seaofnodes.simple.codegen.LRG; -import com.seaofnodes.simple.codegen.Machine; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; import com.seaofnodes.simple.type.*; public class riscv extends Machine { + + public riscv(CodeGen code) { + if( !"SystemV".equals(code._callingConv) ) + throw new IllegalArgumentException("Unknown calling convention "+code._callingConv); + } + @Override public String name() {return "riscv";} + //@Override public int maxReg() { return MAX_REG; } // Using ABI names instead of register names - static final int RPC= 1, SP = 2, GP = 3, TP = 4, T0 = 5, T1 = 6, T2 = 7; - static final int S0 = 8, S1 = 9, A0 = 10, A1 = 11, A2 = 12, A3 = 13, A4 = 14, A5 = 15; - static final int A6 = 16, A7 = 17, S2 = 18, S3 = 19, S4 = 20, S5 = 21, S6 = 22, S7 = 23; - static final int S8 = 24, S9 = 25, S10 = 26, S11 = 27, T3 = 28, T4 = 29, T5 = 30, T6 = 31; + public static int ZERO = 0, RPC= 1, RSP= 2, S12= 3, S13= 4, T0 = 5, T1 = 6, T2 = 7; + public static int S0 = 8, S1 = 9, A0 = 10, A1 = 11, A2 = 12, A3 = 13, A4 = 14, A5 = 15; + public static int A6 = 16, A7 = 17, S2 = 18, S3 = 19, S4 = 20, S5 = 21, S6 = 22, S7 = 23; + public static int S8 = 24, S9 = 25, S10= 26, S11= 27, T3 = 28, T4 = 29, T5 = 30, T6 = 31; // FP registers - static final int F0 = 32, F1 = 33, F2 = 34, F3 = 35, F4 = 36, F5 = 37, F6 = 38, F7 = 39; - static final int FS0 = 40, FS1 = 41, FA0 = 42, FA1 = 43, FA2 = 44, FA3 = 45, FA4 = 46, FA5 = 47; - static final int FA6 = 48, FA7 = 49, FS2 = 50, FS3 = 51, FS4 = 52, FS5 = 53, FS6 = 54, FS7 = 55; - static final int FS8 = 56, FS9 = 57, FS10 = 58, FS11 = 59, FT8 = 60, FT9 = 61, FT10 = 62, FT11 = 63; + public static int F0 = 32, F1 = 33, F2 = 34, F3 = 35, F4 = 36, F5 = 37, F6 = 38, F7 = 39; + public static int FS0 = 40, FS1 = 41, FA0 = 42, FA1 = 43, FA2 = 44, FA3 = 45, FA4 = 46, FA5 = 47; + public static int FA6 = 48, FA7 = 49, FS2 = 50, FS3 = 51, FS4 = 52, FS5 = 53, FS6 = 54, FS7 = 55; + public static int FS8 = 56, FS9 = 57, FS10 = 58, FS11 = 59, FT8 = 60, FT9 = 61, FT10 = 62, FT11 = 63; + + static final int MAX_REG = 64; - static final int MAX_REG = 61; + public static final int F_OFFSET = 32; static final String[] REGS = new String[] { - "zero", "r1" , "rsp" , "r3" , "r4" , "r5" , "r6" , "r7" , - "r8" , "r9" , "r10" , "r11" , "r12" , "r13" , "r14" , "r15" , - "r16" , "r17" , "r18" , "r19" , "r20" , "r21" , "r22" , "r23" , - "r24" , "r25" , "r26" , "r27" , "r28" , "r29" , "r30" , "r31" , + "zero","rpc" , "rsp" , "s12" , "s13" , "t0" , "t1" , "t2" , + "s0" , "s1" , "a0" , "a1" , "a2" , "a3" , "a4" , "a5" , + "a6" , "a7" , "s2" , "s3" , "s4" , "s5" , "s6" , "s7" , + "s8" , "s9" , "s10" , "s11" , "t3" , "t4" , "t5" , "t6" , "f0" , "f1" , "f2" , "f3" , "f4" , "f5" , "f6" , "f7" , "fs0" , "fs1" , "fa0" , "fa1" , "fa2" , "fa3" , "fa4" , "fa5" , "fa6" , "fa7" , "fs2" , "fs3" , "fs4" , "fs5" , "fs6" , "fs7" , "fs8" , "fs9" , "fs10", "fs11", "ft8" , "ft9" , "ft10", "ft11" + // Register-based naming hella easier to debug than "official" register + // names when single-stepping in debugger. + //"zero", "r1" , "rsp" , "r3" , "r4" , "r5" , "r6" , "r7" , + //"r8" , "r9" , "r10" , "r11", "r12", "r13", "r14", "r15", + //"r16" , "r17" , "r18" , "r19", "r20", "r21", "r22", "r23", + //"r24" , "r25" , "r26" , "r27", "r28", "r29", "r30", "r31", + //"f0" , "f1" , "f2" , "f3" , "f4" , "f5" , "f6" , "f7" , + //"fs0" , "fs1" , "fa0" , "fa1" , "fa2" , "fa3" , "fa4" , "fa5" , + //"fa6" , "fa7" , "fs2" , "fs3" , "fs4" , "fs5" , "fs6" , "fs7" , + //"fs8" , "fs9" , "fs10", "fs11", "ft8" , "ft9" , "ft10", "ft11" }; - @Override public String reg( int reg ) { - return reg < REGS.length ? REGS[reg] : "[rsp+"+(reg-REGS.length)*4+"]"; - } - + @Override public String[] regs() { return REGS; } // General purpose register mask: pointers and ints, not floats static final long RD_BITS = 0b11111111111111111111111111111111L; // All the GPRs static final RegMask RMASK = new RegMask(RD_BITS); - static final long WR_BITS = 0b11111111111111111111111111111010L; // All the GPRs, minus ZERO and SP + static final long WR_BITS = 0b11111111111111111111111111111010L; // All the GPRs, minus ZERO and RSP static final RegMask WMASK = new RegMask(WR_BITS); // Float mask from(f0-ft10). TODO: ft10,ft11 needs a larger RegMask static final long FP_BITS = 0b11111111111111111111111111111111L<<F0; @@ -56,26 +70,26 @@ public class riscv extends Machine { // Arguments masks - static final RegMask A0_MASK = new RegMask(A0); - static final RegMask A1_MASK = new RegMask(A1); - static final RegMask A2_MASK = new RegMask(A2); - static final RegMask A3_MASK = new RegMask(A3); - static final RegMask A4_MASK = new RegMask(A4); - static final RegMask A5_MASK = new RegMask(A5); - static final RegMask A6_MASK = new RegMask(A6); - static final RegMask A7_MASK = new RegMask(A7); + public static RegMask A0_MASK = new RegMask(A0); + public static RegMask A1_MASK = new RegMask(A1); + public static RegMask A2_MASK = new RegMask(A2); + public static RegMask A3_MASK = new RegMask(A3); + public static RegMask A4_MASK = new RegMask(A4); + public static RegMask A5_MASK = new RegMask(A5); + public static RegMask A6_MASK = new RegMask(A6); + public static RegMask A7_MASK = new RegMask(A7); - static final RegMask RPC_MASK = new RegMask(RPC); + public static final RegMask RPC_MASK = new RegMask(RPC); // Float arguments masks - static final RegMask FA0_MASK = new RegMask(FA0); - static final RegMask FA1_MASK = new RegMask(FA1); - static final RegMask FA2_MASK = new RegMask(FA2); - static final RegMask FA3_MASK = new RegMask(FA3); - static final RegMask FA4_MASK = new RegMask(FA4); - static final RegMask FA5_MASK = new RegMask(FA5); - static final RegMask FA6_MASK = new RegMask(FA6); - static final RegMask FA7_MASK = new RegMask(FA7); + public static RegMask FA0_MASK = new RegMask(FA0); + public static RegMask FA1_MASK = new RegMask(FA1); + public static RegMask FA2_MASK = new RegMask(FA2); + public static RegMask FA3_MASK = new RegMask(FA3); + public static RegMask FA4_MASK = new RegMask(FA4); + public static RegMask FA5_MASK = new RegMask(FA5); + public static RegMask FA6_MASK = new RegMask(FA6); + public static RegMask FA7_MASK = new RegMask(FA7); // Int arguments calling conv static RegMask[] CALLINMASK = new RegMask[] { @@ -90,17 +104,157 @@ public class riscv extends Machine { }; static RegMask[] XMMS = new RegMask[] { - FA0_MASK, - FA1_MASK, - FA2_MASK, - FA3_MASK, - FA4_MASK, - FA5_MASK, - FA6_MASK, - FA7_MASK + FA0_MASK, + FA1_MASK, + FA2_MASK, + FA3_MASK, + FA4_MASK, + FA5_MASK, + FA6_MASK, + FA7_MASK }; - static RegMask callInMask( TypeFunPtr tfp, int idx ) { + // major opcode: OP + public static int OP_LOAD = 0b00_000_11; + public static int OP_LOADFP = 0b00_001_11; + public static int OP_CUSTOM0 = 0b00_010_11; + public static int OP_IMM = 0b00_100_11; + public static int OP_AUIPC = 0b00_101_11; + + public static int OP_STORE = 0b01_000_11; + public static int OP_STOREFP = 0b01_001_11; + public static int OP_CUSTOM1 = 0b01_010_11; + public static int OP = 0b01_100_11; + public static int OP_LUI = 0b01_101_11; + + public static int OP_CUSTOM2 = 0b10_010_11; + public static int OP_FP = 0b10_100_11; + + public static int OP_BRANCH = 0b11_000_11; + public static int OP_JALR = 0b11_001_11; + public static int OP_RESERVED= 0b11_010_11; + public static int OP_JAL = 0b11_011_11; + + + // Since riscv instructions are fixed we can just or them together + public static int r_type(int opcode, int rd, int func3, int rs1, int rs2, int func7) { + assert 0 <= rs1 && rs1 < 32; + assert 0 <= rs2 && rs2 < 32; + assert 0 <= rd && rd < 32; + return (func7 << 25) | (rs2 << 20) | (rs1 << 15) | (func3 << 12) | (rd << 7) | opcode; + } + public static void r_type(Encoding enc, Node n, int func3, int func7) { + short dst = enc.reg(n); + short src1 = enc.reg(n.in(1)); + short src2 = enc.reg(n.in(2)); + int body = r_type(OP,dst,func3,src1,src2,func7); + enc.add4(body); + } + + public static void rf_type(Encoding enc, Node n, RM func3, int func7) { + short dst = (short)(enc.reg(n )-F_OFFSET); + short src1 = (short)(enc.reg(n.in(1))-F_OFFSET); + short src2 = (short)(enc.reg(n.in(2))-F_OFFSET); + int body = r_type(OP_FP,dst,func3.ordinal(),src1,src2,func7); + enc.add4(body); + } + + + public static int u_type(int opcode, int rd, int imm20) { + assert 0 <= rd && rd < 32; + return (imm20 << 12) | (rd << 7) | opcode; + } + + public static int j_type(int opcode, int rd, int delta) { + assert -(1L<<20) <= delta && delta < (1L<<20); + assert 0 <= rd && rd < 32; + // Messy branch offset encoding + // 31 30-21 20 19-12 11-7 6-0 + // 20 10- 1 11 19-12 rpc JAL + assert (delta&1)==0; // Low bit is always zero, not encoded + int imm10_01 = (delta>> 1) & 0x3FF; + int imm11 = (delta>>11) & 1; + int imm12_19 = (delta>>12) & 0xFF; + int imm20 = (delta>>19) & 1; + int bits = imm20<<19 | imm10_01 << 9 | imm11 << 8 | imm12_19; + return bits << 12 | rd << 7 | opcode; + } + + + public static int i_type(int opcode, int rd, int func3, int rs1, int imm12) { + assert 0 <= rd && rd < 32; + assert 0 <= rs1 && rs1 < 32; + assert opcode >= 0 && func3 >=0 && imm12 >= 0; // Zero-extend by caller + return (imm12 << 20) | (rs1 << 15) | (func3 << 12) | (rd << 7) | opcode; + } + + + // S-type instructions(store) + public static int s_type(int opcode, int func3, int rs1, int rs2, int imm12) { + assert 0 <= rs1 && rs1 < 32; + assert 0 <= rs2 && rs2 < 32; + assert 0 <= func3; + + assert imm12 >= 0; // Masked to high zero bits by caller + int imm_lo = imm12 & 0x1F; + int imm_hi = imm12 >> 5; + return (imm_hi << 25) | (rs2 << 20) | (rs1 << 15) | (func3 << 12) | (imm_lo << 7) | opcode; + } + + // BRANCH + public static int b_type(int opcode, int func3, short rs1, short rs2, int delta) { + assert 0 <= rs1 && rs1 < 32; + assert 0 <= rs2 && rs2 < 32; + assert -4*1024 <= delta && delta < 4*1024; + assert (delta&1)==0; // Low bit is always zero, not encoded + // Messy branch offset encoding + // 31 30 29 28 27 26 25 24-20 19-15 14-12 11 10 9 8 7 6-0 + // 12 10 9 8 7 6 5 SRC2 SRC1 FUNC3 4 3 2 1 11 OP + int imm4_1 = (delta>> 1) & 0xF; + int imm10_5= (delta>> 5) &0x3F; + int imm11 = (delta>>11) & 1; + int imm12 = (delta>>12) & 1; + int imm5 = imm4_1<<1 | imm11; + int imm7 = imm12<<6 | imm10_5; + return (imm7 << 25 ) | (rs2 << 20) | (rs1 << 15) | (func3 << 12) | (imm5 << 7) | opcode; + } + + public enum RM { + RNE, // Round to Nearest, ties to Even + RTZ, // Round towards Zero + RDN, // Round Down + RUP, // Round Up + DIRECT, // Round to Nearest, ties to Max Magnitude + RESERVED1, // Reserved for futue use + RESERVED2, // Reserved for future use + DYN, // In instruction’s rm field, selects dynamic rounding mode; In Rounding Mode register, reserved + } + + // Since opcode is the same just return back func3 + static public int jumpop(String op) { + return switch(op) { + case "==" -> 0x0; + case "!=" -> 0x1; + case "<" -> 0x4; + case ">=" -> 0x5; + case "u<" -> 0x6; + case "u<=" -> 0x7; + default -> throw Utils.TODO(); + }; + } + + // Since opcode is the same just return back func3 + static public int fsetop(String op) { + return switch(op) { + case "<" -> 1; + case "<=" -> 0; + case "==" -> 2; + default -> throw Utils.TODO(); + }; + } + + @Override public RegMask callArgMask( TypeFunPtr tfp, int idx, int maxArgSlot ) { return callInMask(tfp,idx,maxArgSlot); } + static RegMask callInMask( TypeFunPtr tfp, int idx, int maxArgSlot ) { if( idx==0 ) return RPC_MASK; if( idx==1 ) return null; // Count floats in signature up to index @@ -117,109 +271,94 @@ public class riscv extends Machine { if( idx-2-fcnt < cargs.length ) return cargs[idx-2-fcnt]; } - throw Utils.TODO(); // Pass on stack slot - } - - // callee saved(riscv) - static final long CALLEE_SAVE = - (1L<< S0) | (1L<< S1) | (1L<< S2 ) | (1L<< S3 ) | - (1L<< S4) | (1L<< S5) | (1L<< S6 ) | (1L<< S7 ) | - (1L<< S8) | (1L<< S9) | (1L<< S10) | (1L<< S11) | - (1L<<FS0) | (1L<<FS1) | (1L<<FS2 ) | (1L<<FS3 ) | - (1L<<FS4) | (1L<<FS5) | (1L<<FS6 ) | (1L<<FS7 ) | - (1L<<FS8) | (1L<<FS9) | (1L<<FS10) | (1L<<FS11); - - static final RegMask CALLER_SAVE_MASK; - static { - long caller = ~CALLEE_SAVE; - caller &= ~(1L<<SP); - CALLER_SAVE_MASK = new RegMask(caller,0); - } - static RegMask riscCallerSave() { return CALLER_SAVE_MASK; } - @Override public RegMask callerSave() { return riscCallerSave(); } - - static final RegMask CALLEE_SAVE_MASK = new RegMask(CALLEE_SAVE); - static RegMask riscCalleeSave() { return CALLEE_SAVE_MASK; } - @Override public RegMask calleeSave() { return riscCalleeSave(); } - - // Return single int/ptr register. Used by CallEnd output and Return input. - static final RegMask[] RET_MASKS; - static { - int nSaves = CALLEE_SAVE_MASK.size(); - RET_MASKS = new RegMask[4 + nSaves]; - RET_MASKS[0] = null; - RET_MASKS[1] = null; // Memory - RET_MASKS[2] = null; // Varies, either XMM0 or RAX - RET_MASKS[3] = RMASK; // Expected R1 but could be any register really - short reg = CALLEE_SAVE_MASK.firstReg(); - for( int i=0; i<nSaves; i++ ) { - RET_MASKS[i+4] = new RegMask(reg); - reg = CALLEE_SAVE_MASK.nextReg(reg); + // Pass on stack slot(8 and higher) + if( maxArgSlot>0 ) throw Utils.TODO(); + return new RegMask(MAX_REG + 1 + (idx - 2)); + } + + @Override public short maxArgSlot( TypeFunPtr tfp ) { + int icnt=0, fcnt=0; // Count of ints, floats + for( int i=0; i<tfp.nargs(); i++ ) { + if( tfp.arg(i) instanceof TypeFloat ) fcnt++; + else icnt++; } + int nstk = Math.max(icnt-8,0)+Math.max(fcnt-8,0); + return (short)nstk; } - static RegMask retMask( TypeFunPtr tfp, int i ) { - return i==2 - ? (tfp.ret() instanceof TypeFloat ? FA0_MASK : A0_MASK) - : RET_MASKS[i]; + + static final long CALLER_SAVE = + (1L<<RPC) | + (1L<<A0 ) | (1L<<A1 ) | (1L<<A2 ) | (1L<<A3 ) | (1L<<A4 ) | (1L<<A5 ) | (1L<<A6 ) | (1L<<A7 ) | + (1L<<T0 ) | (1L<<T1 ) | (1L<<T2 ) | (1L<<T3 ) | (1L<<T4 ) | (1L<<T5 ) | (1L<<T6 ) | + (1L<<FA0) | (1L<<FA1) | (1L<<FA2) | (1L<<FA3) | (1L<<FA4) | (1L<<FA5) | (1L<<FA6) | (1L<<FA7) | + (1L<<F0 ) | (1L<<F1 ) | (1L<<F2 ) | (1L<<F3 ) | (1L<<F4 ) | (1L<<F5 ) | (1L<<F6 ) | (1L<<F7 ) | + (1L<<FT8) | (1L<<FT9) | (1L<<FT10)| (1L<<FT11); + + @Override public long callerSave() { return CALLER_SAVE; } + @Override public long neverSave() { return (1L<<RSP) | (1L<<ZERO); } + @Override public RegMask retMask( TypeFunPtr tfp ) { + return tfp.ret() instanceof TypeFloat ? FA0_MASK : A0_MASK; } + @Override public int rpc() { return RPC; } // Return a MachNode unconditional branch - @Override public CFGNode jump() { - throw Utils.TODO(); - } + @Override public CFGNode jump() { return new UJmpRISC(); } // Create a split op; any register to any register, including stack slots @Override public SplitNode split(String kind, byte round, LRG lrg) { return new SplitRISC(kind,round); } - // Break an infinite loop - @Override public IfNode never( CFGNode ctrl ) { - throw Utils.TODO(); - } - // True if signed 12-bit immediate - private static boolean imm12(TypeInteger ti) { - // 64-12==52 + public static boolean imm12(TypeInteger ti) { + // 52 = 64-12 return ti.isConstant() && ((ti.value()<<52)>>52) == ti.value(); } - + // True if HIGH 20-bit signed immediate, with all zeros low. + public static boolean imm20Exact(TypeInteger ti) { + // shift left 32 to clear out the upper 32 bits. + // shift right SIGNED to sign-extend upper 32 bits; then shift 12 more to clear out lower 12 bits. + // shift left 12 to re-center the bits. + return ti.isConstant() && (((ti.value()<<32)>>>44)<<12) == ti.value(); + } @Override public Node instSelect( Node n ) { return switch (n) { - case AddFNode addf -> addf(addf); - case AddNode add -> add(add); - case AndNode and -> and(and); - case BoolNode bool -> cmp(bool); - case CallNode call -> call(call); - case CastNode cast -> new CastRISC(cast); + case AddFNode addf -> addf(addf); + case AddNode add -> add(add); + case AndNode and -> and(and); + case BoolNode bool -> cmp(bool); + case CallNode call -> call(call); + case CastNode cast -> new CastRISC(cast); case CallEndNode cend -> new CallEndRISC(cend); - case CProjNode c -> new CProjNode(c); + case CProjNode c -> new CProjNode(c); case ConstantNode con -> con(con); - case DivFNode divf -> new DivFRISC(divf); - case DivNode div -> new DivRISC(div); - case FunNode fun -> new FunRISC(fun); - case IfNode iff -> jmp(iff); - case LoadNode ld -> ld(ld); + case DivFNode divf -> new DivFRISC(divf); + case DivNode div -> new DivRISC(div); + case FunNode fun -> new FunRISC(fun); + case IfNode iff -> jmp(iff); + case LoadNode ld -> ld(ld); case MemMergeNode mem -> new MemMergeNode(mem); - case MulFNode mulf -> new MulFRISC(mulf); - case MulNode mul -> new MulRISC(mul); - case NewNode nnn -> new NewRISC(nnn); - case OrNode or -> or(or); - case ParmNode parm -> new ParmRISC(parm); - case PhiNode phi -> new PhiNode(phi); - case ProjNode prj -> prj(prj); - case ReadOnlyNode read -> new ReadOnlyNode(read); - case ReturnNode ret -> new RetRISC(ret, ret.fun()); - case SarNode sar -> sra(sar); - case ShlNode shl -> sll(shl); - case ShrNode shr -> srl(shr); - case StartNode start -> new StartNode(start); - case StopNode stop -> new StopNode(stop); - case StoreNode st -> st(st); - case SubFNode subf -> new SubFRISC(subf); - case SubNode sub -> sub(sub); - case ToFloatNode tfn -> i2f8(tfn); - case XorNode xor -> xor(xor); + case MinusNode neg -> new NegRISC(neg); + case MulFNode mulf -> new MulFRISC(mulf); + case MulNode mul -> new MulRISC(mul); + case NewNode nnn -> nnn(nnn); + case NotNode not -> new NotRISC(not); + case OrNode or -> or(or); + case ParmNode parm -> new ParmRISC(parm); + case PhiNode phi -> new PhiNode(phi); + case ProjNode prj -> prj(prj); + case ReadOnlyNode read-> new ReadOnlyNode(read); + case ReturnNode ret -> new RetRISC(ret, ret.fun()); + case SarNode sar -> sra(sar); + case ShlNode shl -> sll(shl); + case ShrNode shr -> srl(shr); + case StartNode start -> new StartNode(start); + case StopNode stop -> new StopNode(stop); + case StoreNode st -> st(st); + case SubFNode subf -> new SubFRISC(subf); + case SubNode sub -> sub(sub); + case ToFloatNode tfn -> i2f8(tfn); + case XorNode xor -> xor(xor); case LoopNode loop -> new LoopNode(loop); case RegionNode region -> new RegionNode(region); @@ -232,36 +371,81 @@ public class riscv extends Machine { } private Node add(AddNode add) { - if( add.in(2) instanceof ConstantNode off && off._con instanceof TypeInteger ti && imm12(ti) ) - return new AddIRISC(add, (int)ti.value()); + if( add.in(2) instanceof ConstantNode off2 && off2._con instanceof TypeInteger ti && imm12(ti) ) + return new AddIRISC(add, (int)ti.value(),true); return new AddRISC(add); } private Node and(AndNode and) { - if( and.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti) ) - return new AndIRISC(and, (int)ti.value()); + if( and.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti ) { + if( imm12(ti) ) + return new AndIRISC(and, (int)ti.value()); + // Could be any size low bit mask + if( ti.value() == 0xFFFFFFFFL ) + return new SrlIRISC(new SllIRISC(and,32),32,false); + } return new AndRISC(and); } private Node call(CallNode call) { - if( call.fptr() instanceof ConstantNode con && con._con instanceof TypeFunPtr tfp ) - return new CallRISC(call, tfp); - return new CallRRISC(call); + return call.fptr() instanceof ConstantNode con && con._con instanceof TypeFunPtr tfp + ? new CallRISC(call, tfp) + : new CallRRISC(call); + } + private Node nnn(NewNode nnn) { + // TODO: pass in the TFP for alloc + return new NewRISC(nnn); } private Node cmp(BoolNode bool) { - if( bool.in(2) instanceof ConstantNode off && off._con instanceof TypeInteger toff ) - return new SetIRISC(bool, (int)toff.value()); - // Float variant + // Float variant directly implemented in hardware if( bool.isFloat() ) return new SetFRISC(bool); - return new SetRISC(bool); + + // Only < and <u are implemented in hardware. + // x < y - as-is + // x <= y - flip and negate; !(y < x); `slt tmp=y,x; xori dst=tmp,#1` + // x == y - sub and vs0 == `sub tmp=x-y; sltu dst=tmp,#1` + + // x > y - swap; y < x + // x >= y - swap and negate; !(x < y); `slt tmp=y,x;` then NOT. + // x != y - sub and vs0 == `sub tmp=x-y; sltu dst=tmp,#1` then NOT. + + // The ">", ">=" and "!=" in Simple include a NotNode, which can be + // implemented with a XOR. If one of the above is followed by a NOT + // we can remove the double XOR in the encodings. + + return switch( bool.op() ) { + case "<" -> bool.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti) + ? new SetIRISC(bool, (int)ti.value(),false) + : new SetRISC(bool, false); + // x <= y - flip and negate; !(y < x); `slt tmp=y,x; xori dst=tmp,#1` + case "<=" -> new XorIRISC(new SetRISC(bool.swap12(), false),1); + // x == y - sub and vs0 == `sub tmp=x-y; sltu dst=tmp,#1` + case "==" -> new SetIRISC(new SubRISC(bool),1,true); + default -> throw Utils.TODO(); + }; } private Node con( ConstantNode con ) { if( !con._con.isConstant() ) return new ConstantNode( con ); // Default unknown caller inputs return switch( con._con ) { - case TypeInteger ti -> new IntRISC(con); + case TypeInteger ti -> { + if( imm12(ti) ) yield new IntRISC(con); + long x = ti.value(); + if( imm20Exact(ti) ) yield new LUI((int)x); + if( (x<<32)>>32 == x ) { // Signed lower 32-bit immediate + // Here, the low 12 bits get sign-extended, which means if + // bit11 is set, the value is negative and lowers the LUI + // value. Add a bit 12 to compensate + if( ((x>>11)&1)==1 ) x += 0x1000; + yield new AddIRISC(new LUI((int)(x & ~0xFFF)), (int)(x & 0xFFF),false); + } + // Need more complex sequence for larger constants... or a load + // from a constant pool, which does not need an extra register + throw Utils.TODO(); + } + // Load from constant pool case TypeFloat tf -> new FltRISC(con); case TypeFunPtr tfp -> new TFPRISC(con); case TypeMemPtr tmp -> throw Utils.TODO(); @@ -272,45 +456,50 @@ public class riscv extends Machine { } private Node jmp( IfNode iff ) { - if( iff.in(1) instanceof BoolNode bool && !bool.isFloat() ) - return new BranchRISC(iff, bool.op(), bool.in(1), bool.in(2)); + if( iff.in(1) instanceof BoolNode bool && !bool.isFloat() ) { + // if less than or equal switch inputs + String bop = bool.op(); + if( bop.equals("<=") || bop.equals(">") ) + return new BranchRISC(iff, IfNode.swap(bop), bool.in(2), bool.in(1)); + return new BranchRISC(iff, bop, bool.in(1), bool.in(2)); + } // Vs zero - return new BranchRISC(iff, "==", iff.in(1), null); + return new BranchRISC(iff, iff.in(1)==null ? "==" : "!=", iff.in(1), null); } private Node or(OrNode or) { - if( or.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti) ) + if( or.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti)) return new OrIRISC(or, (int)ti.value()); return new OrRISC(or); } private Node xor(XorNode xor) { - if( xor.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti) ) + if( xor.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti)) return new XorIRISC(xor, (int)ti.value()); return new XorRISC(xor); } private Node sra(SarNode sar) { - if( sar.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti) ) + if( sar.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti)) return new SraIRISC(sar, (int)ti.value()); return new SraRISC(sar); } private Node srl(ShrNode shr) { - if( shr.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti) ) - return new SrlIRISC(shr, (int)ti.value()); + if( shr.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti)) + return new SrlIRISC(shr, (int)ti.value(),true); return new SrlRISC(shr); } private Node sll(ShlNode sll) { - if( sll.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti) ) + if( sll.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti)) return new SllIRISC(sll, (int)ti.value()); return new SllRISC(sll); } private Node sub(SubNode sub) { return sub.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti) - ? new AddIRISC(sub, (int)(-ti.value())) + ? new AddIRISC(sub, (int)(-ti.value()),true) : new SubRISC(sub); } @@ -324,28 +513,20 @@ public class riscv extends Machine { } private Node ld(LoadNode ld) { - return new LoadRISC(address(ld),ld.ptr(),idx,off); + return new LoadRISC(ld,address(ld),off); } private Node st(StoreNode st) { - int imm=0; - Node xval = st.val(); - if( xval instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti) ) { - xval = null; - imm = (int)ti.value(); - assert imm == ti.value(); // In 32-bit range - } - return new StoreRISC(address(st),st.ptr(),idx,off,imm,xval); + Node xval = st.val() instanceof ConstantNode con && con._con == TypeInteger.ZERO ? null : st.val(); + return new StoreRISC(st,address(st), off, xval); } // Gather addressing mode bits prior to constructing. This is a builder // pattern, but saving the bits in a *local* *global* here to keep mess // contained. private static int off; - private static Node idx; - private <N extends MemOpNode> N address( N mop ) { + private Node address( MemOpNode mop ) { off = 0; // Reset - idx = null; Node base = mop.ptr(); // Skip/throw-away a ReadOnly, only used to typecheck if( base instanceof ReadOnlyNode read ) base = read.in(1); @@ -353,9 +534,10 @@ public class riscv extends Machine { if( mop.off() instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti) ) { off = (int)ti.value(); } else { - idx = mop.off(); + base = new AddRISC(base,mop.off()); + base._type = mop.ptr()._type; } - return mop; + return base; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFMemX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFMemX86.java index ffc6966e..d54c7c31 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFMemX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFMemX86.java @@ -2,15 +2,14 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; import com.seaofnodes.simple.SB; import com.seaofnodes.simple.Utils; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; public class AddFMemX86 extends MemOpX86 { AddFMemX86( AddFNode add, LoadNode ld , Node base, Node idx, int off, int scale, Node val ) { super(add,ld, base, idx, off, scale, 0, val ); } - + @Override public String op() { return "addf"+_sz; } @Override public RegMask regmap(int i) { if( i==1 ) return null; // Memory if( i==2 ) return x86_64_v2.RMASK; // base @@ -18,10 +17,23 @@ public class AddFMemX86 extends MemOpX86 { if( i==4 ) return x86_64_v2.XMASK; // value throw Utils.TODO(); } - - // Register mask allowed as a result. 0 for no register. @Override public RegMask outregmap() { return x86_64_v2.XMASK; } + @Override public int twoAddress() { return 4; } + @Override public void encoding( Encoding enc ) { + // addsd xmm0, DWORD PTR [rdi+0xc] + short dst = enc.reg(this ); + short ptr = enc.reg(ptr()); + short idx = enc.reg(idx()); + // F opcode + enc.add1(0xF2); + // rex prefix must come next (REX.W is not set) + x86_64_v2.rexF(dst, ptr, idx, false, enc); + // FP ADD + enc.add1(0x0F).add1(0x58); + + x86_64_v2.indirectAdr(_scale, idx, ptr, _off, dst, enc); + } // General form: "add dst = src + [base + idx<<2 + 12]" @Override public void asm(CodeGen code, SB sb) { @@ -29,5 +41,4 @@ public class AddFMemX86 extends MemOpX86 { sb.p(val()==null ? "#"+_imm : code.reg(val())).p(" + "); asm_address(code,sb); } - @Override public String op() { return "addf"+_sz; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFX86.java index d4ea9162..a4166bc7 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFX86.java @@ -1,33 +1,33 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.TypeInteger; -import java.io.ByteArrayOutputStream; public class AddFX86 extends MachConcreteNode implements MachNode { AddFX86( Node addf ) { super(addf); } - - // Register mask allowed on input i. + @Override public String op() { return "addf"; } @Override public RegMask regmap(int i) { assert i==1 || i==2; return x86_64_v2.XMASK; } - // Register mask allowed as a result. 0 for no register. @Override public RegMask outregmap() { return x86_64_v2.XMASK; } - // Output is same register as input#1 @Override public int twoAddress() { return 1; } - // Ok to swap arguments @Override public boolean commutes() { return true; } - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); + @Override public void encoding( Encoding enc ) { + // F2 0F 58 /r ADDSD xmm1, xmm2/m64 + short dst = (short)(enc.reg(this ) - x86_64_v2.XMM_OFFSET); + short src = (short)(enc.reg(in(2)) - x86_64_v2.XMM_OFFSET); + // Fopcode + enc.add1(0xF2); + // rex prefix must come next (REX.W is not set) + x86_64_v2.rexF(dst, src, 0, false, enc); + + enc.add1(0x0F).add1(0x58); + + enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, src)); } // General form: "addf dst += src" @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" + ").p(code.reg(in(2))); + sb.p(code.reg(this)).p(" += ").p(code.reg(in(2))); } - - @Override public String op() { return "addf"; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddIX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddIX86.java index c7f92ff1..569be754 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddIX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddIX86.java @@ -1,41 +1,13 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; -import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.TypeInteger; -import java.io.ByteArrayOutputStream; - -public class AddIX86 extends MachConcreteNode implements MachNode { - final TypeInteger _ti; - AddIX86( Node add, TypeInteger ti ) { - super(add); - _inputs.pop(); - _ti = ti; - } - - // Register mask allowed on input i. - @Override public RegMask regmap(int i) { - // assert i== i; - return x86_64_v2.WMASK; } - // Register mask allowed as a result. 0 for no register. - @Override public RegMask outregmap() { return x86_64_v2.WMASK; } - // Output is same register as input#1 - @Override public int twoAddress() { return 1; } - - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); - } - - // General form: "addi dst += #imm" - @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" + #"); - _ti.print(sb); - } +import com.seaofnodes.simple.node.Node; +public class AddIX86 extends ImmX86 { + AddIX86( Node add, int imm ) { super(add,imm); } @Override public String op() { - return _ti.value() == 1 ? "inc" : (_ti.value() == -1 ? "dec" : "addi"); + return _imm == 1 ? "inc" : (_imm == -1 ? "dec" : "addi"); } + @Override public String glabel() { return "+"; } + @Override int opcode() { return 0x81; } + @Override int mod() { return 0; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddMemX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddMemX86.java index 3d4fac41..f97a7cb8 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddMemX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddMemX86.java @@ -1,26 +1,34 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; import com.seaofnodes.simple.SB; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; public class AddMemX86 extends MemOpX86 { AddMemX86( AddNode add, LoadNode ld , Node base, Node idx, int off, int scale, int imm, Node val ) { super(add,ld, base, idx, off, scale, imm, val ); } - @Override public String op() { return "add"+_sz; } - - // Register mask allowed as a result. 0 for no register. @Override public RegMask outregmap() { return x86_64_v2.WMASK; } - // Output is same register as val() @Override public int twoAddress() { return 4; } + @Override public void encoding( Encoding enc ) { + // add something to register from memory + // add eax,DWORD PTR [rdi+0xc] + // REX.W + 03 /r ADD r64, r/m64 + short dst = enc.reg(this ); + short ptr = enc.reg(ptr()); + short idx = enc.reg(idx()); + + enc.add1(x86_64_v2.rex(dst, ptr, idx)); + // opcode + enc.add1(0x03); + + x86_64_v2.indirectAdr(_scale, idx, ptr, _off, dst, enc); + } - // General form: "add dst = src + [base + idx<<2 + 12]" + // General form: "add dst += [base + idx<<2 + 12]" @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(" = "); - sb.p(val()==null ? "#"+_imm : code.reg(val())).p(" + "); + sb.p(code.reg(this)).p(" += "); asm_address(code,sb); } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddX86.java index 9553d499..84543e8f 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddX86.java @@ -1,33 +1,11 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; -import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.TypeInteger; -import java.io.ByteArrayOutputStream; +import com.seaofnodes.simple.node.Node; -public class AddX86 extends MachConcreteNode { +public class AddX86 extends RegX86 { AddX86( Node add ) { super(add); } - - // Register mask allowed on input i. - @Override public RegMask regmap(int i) { assert i==1 || i==2; return x86_64_v2.RMASK; } - // Register mask allowed as a result. 0 for no register. - @Override public RegMask outregmap() { return x86_64_v2.WMASK; } - // Output is same register as input#1 - @Override public int twoAddress() { return 1; } - // Ok to swap arguments - @Override public boolean commutes() { return true; } - - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); - } - - // General form: "add dst += src" - @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" + ").p(code.reg(in(2))); - } - @Override public String op() { return "add"; } + @Override public String glabel() { return "+"; } + @Override int opcode() { return 0x03; } + @Override public boolean commutes() { return true; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AndIX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AndIX86.java index a7f1801e..3c1b567c 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AndIX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AndIX86.java @@ -1,44 +1,11 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; -import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.Type; -import com.seaofnodes.simple.type.TypeInteger; -import java.io.ByteArrayOutputStream; -import java.util.BitSet; -import java.lang.StringBuilder; - -public class AndIX86 extends MachConcreteNode implements MachNode { - final TypeInteger _ti; - AndIX86(Node and, TypeInteger ti) { - super(and); - _inputs.pop(); - _ti = ti; - } - - // Register mask allowed on input i. - // This is the normal calling convention - @Override public RegMask regmap(int i) { assert i==1; return x86_64_v2.WMASK; } - // Register mask allowed as a result. 0 for no register. - @Override public RegMask outregmap() { return x86_64_v2.WMASK; } - - - // Output is same register as input#1 - @Override public int twoAddress() { return 1; } - - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); - } - - // General form - // General form: "andi dst & #imm" - @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" & #"); - _ti.print(sb); - } +import com.seaofnodes.simple.node.Node; +public class AndIX86 extends ImmX86 { + AndIX86( Node add, int imm ) { super(add,imm); } @Override public String op() { return "andi"; } + @Override public String glabel() { return "&"; } + @Override int opcode() { return 0x81; } + @Override int mod() { return 4; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AndX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AndX86.java index d7fb3613..dedbce45 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AndX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AndX86.java @@ -1,34 +1,11 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; -import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; -import com.seaofnodes.simple.node.*; -import java.io.ByteArrayOutputStream; - -public class AndX86 extends MachConcreteNode implements MachNode{ - AndX86(Node and) { - super(and); - } - // Register mask allowed on input i. - // This is the normal calling convention - @Override public RegMask regmap(int i) { assert i==1; return x86_64_v2.WMASK; } - - @Override public RegMask outregmap() { return x86_64_v2.WMASK; } - - // Output is same register as input#1 - @Override public int twoAddress() { return 1; } - - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); - } - - // General form - // General form: "and dst & src" - @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" & ").p(code.reg(in(2)));; - } +import com.seaofnodes.simple.node.Node; +public class AndX86 extends RegX86 { + AndX86( Node add ) { super(add); } @Override public String op() { return "and"; } + @Override public String glabel() { return "&"; } + @Override int opcode() { return 0x23; } + @Override public boolean commutes() { return true; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallEndX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallEndX86.java index b9cc5659..ce8b993f 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallEndX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallEndX86.java @@ -1,35 +1,8 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; -import com.seaofnodes.simple.SB; -import com.seaofnodes.simple.Utils; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; import com.seaofnodes.simple.node.CallEndNode; import com.seaofnodes.simple.node.MachNode; -import com.seaofnodes.simple.type.TypeFloat; -import com.seaofnodes.simple.type.TypeFunPtr; -import java.io.ByteArrayOutputStream; public class CallEndX86 extends CallEndNode implements MachNode { - final TypeFunPtr _tfp; - CallEndX86( CallEndNode cend ) { - super(cend); - _tfp = (TypeFunPtr)(cend.call().fptr()._type); - } - - @Override public String label() { return op(); } - @Override public RegMask regmap(int i) { return null; } - @Override public RegMask outregmap() { return null; } - @Override public RegMask outregmap(int idx) { return idx == 2 ? x86_64_v2.retMask(_tfp,2) : null; } - @Override public RegMask killmap() { return x86_64_v2.x86CallerSave(); } - - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); - } - - @Override public void asm(CodeGen code, SB sb) { } - - @Override public String op() { return "cend"; } - + CallEndX86( CallEndNode cend ) { super(cend); } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallRX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallRX86.java index ac35d17b..1a74f817 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallRX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallRX86.java @@ -1,32 +1,26 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; import com.seaofnodes.simple.SB; -import com.seaofnodes.simple.Utils; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.CallNode; import com.seaofnodes.simple.node.MachNode; -import java.io.ByteArrayOutputStream; public class CallRX86 extends CallNode implements MachNode { CallRX86( CallNode call ) { super(call); } - + @Override public String op() { return "callr"; } @Override public String label() { return op(); } @Override public RegMask regmap(int i) { return i==_inputs._len ? x86_64_v2.WMASK // Function call target - : x86_64_v2.callInMask(tfp(),i); // Normal argument + : x86_64_v2.callInMask(tfp(),i,fun()._maxArgSlot); // Normal argument } @Override public RegMask outregmap() { return null; } - - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - bytes.write(0xe8); - int beforeSize = bytes.size(); - // address - //TODO: relocs - bytes.write(0x00); - return bytes.size() - beforeSize; + @Override public void encoding( Encoding enc ) { + // FF /2 CALL r/m64 + // calls the function in the register + short src = enc.reg(fptr()); + enc.add1(0xFF); + enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.INDIRECT,2,src)); } @Override public void asm(CodeGen code, SB sb) { @@ -36,5 +30,4 @@ public class CallRX86 extends CallNode implements MachNode { sb.unchar(2); } - @Override public String op() { return "callr"; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallX86.java index e2f68c1f..c97037e2 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallX86.java @@ -1,15 +1,12 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; import com.seaofnodes.simple.SB; -import com.seaofnodes.simple.Utils; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.CallNode; import com.seaofnodes.simple.node.MachNode; import com.seaofnodes.simple.type.TypeFunPtr; -import java.io.ByteArrayOutputStream; -public class CallX86 extends CallNode implements MachNode { +public class CallX86 extends CallNode implements MachNode, RIPRelSize { final TypeFunPtr _tfp; final String _name; CallX86( CallNode call, TypeFunPtr tfp ) { @@ -19,26 +16,24 @@ public class CallX86 extends CallNode implements MachNode { _tfp = tfp; _name = CodeGen.CODE.link(tfp)._name; } - + @Override public String op() { return "call"; } @Override public String label() { return op(); } - @Override public RegMask regmap(int i) { - return x86_64_v2.callInMask(_tfp,i); // Normal argument - } + @Override public String name() { return _name; } + @Override public TypeFunPtr tfp() { return _tfp; } + @Override public RegMask regmap(int i) { return x86_64_v2.callInMask(_tfp,i,fun()._maxArgSlot); } @Override public RegMask outregmap() { return null; } @Override public int nargs() { return nIns()-2; } // Minus control, memory, fptr - @Override public String name() { return _name; } - @Override public TypeFunPtr tfp() { return _tfp; } + @Override public void encoding( Encoding enc ) { + enc.relo(this).add1(0xe8).add4(0); + } + + // Delta is from opcode start, but X86 measures from the end of the 5-byte encoding + @Override public byte encSize(int delta) { return 5; } - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - // linker will fix this up - bytes.write(0xe8); - int beforeSize = bytes.size(); - // TODO: relocs - // address - bytes.write(0x00); - return bytes.size() - beforeSize; + // Delta is from opcode start + @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { + enc.patch4(opStart+1, delta-5); } @Override public void asm(CodeGen code, SB sb) { @@ -47,7 +42,4 @@ public class CallX86 extends CallNode implements MachNode { sb.p(code.reg(in(i+2))).p(" "); sb.unchar(2); } - - @Override public String op() { return "call"; } - } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CastX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CastX86.java index b620b89c..9537cfb3 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CastX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CastX86.java @@ -1,18 +1,16 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; import com.seaofnodes.simple.SB; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.CastNode; import com.seaofnodes.simple.node.MachNode; -import java.io.ByteArrayOutputStream; public class CastX86 extends CastNode implements MachNode { CastX86( CastNode cast ) { super(cast); } + @Override public String op() { return label(); } @Override public RegMask regmap(int i) { assert i==1; return RegMask.FULL; } @Override public RegMask outregmap() { return RegMask.FULL; } @Override public int twoAddress( ) { return 1; } - @Override public int encoding(ByteArrayOutputStream bytes) { return 0; /*no encoding*/ } + @Override public void encoding( Encoding enc ) { } @Override public void asm(CodeGen code, SB sb) { _t.print(sb.p(code.reg(in(1))).p(" isa ")); } - @Override public String op() { return label(); } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpFX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpFX86.java index e9b0dacd..35b92170 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpFX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpFX86.java @@ -1,21 +1,26 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.TypeInteger; -import java.io.ByteArrayOutputStream; public class CmpFX86 extends MachConcreteNode implements MachNode { CmpFX86( Node cmp ) { super(cmp); } - - @Override public RegMask regmap(int i) { assert i==1 || i==2; return x86_64_v2.XMASK; } + @Override public String op() { return "cmpf"; } + @Override public RegMask regmap(int i) { return x86_64_v2.XMASK; } @Override public RegMask outregmap() { return x86_64_v2.FLAGS_MASK; } - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); + @Override public void encoding( Encoding enc ) { + // UCOMISD + short src1 = (short)(enc.reg(in(1)) - x86_64_v2.XMM_OFFSET); + short src2 = (short)(enc.reg(in(2)) - x86_64_v2.XMM_OFFSET); + // float opcode + enc.add1(0x66); + // rex prefix must come next (REX.W is not set) + x86_64_v2.rexF(src1, src2, 0, false, enc); + + enc.add1(0x0F).add1(0x2E); + enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, src1, src2)); } // General form: "cmp src1,src2" @@ -24,6 +29,4 @@ public class CmpFX86 extends MachConcreteNode implements MachNode { if( dst!="flags" ) sb.p(dst).p(" = "); sb.p(code.reg(in(1))).p(", ").p(code.reg(in(2))); } - - @Override public String op() { return "cmpf"; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpIX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpIX86.java index fcba397d..91b6d43c 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpIX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpIX86.java @@ -1,53 +1,44 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; -import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.SB; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.Type; -import com.seaofnodes.simple.type.TypeInteger; -import java.io.ByteArrayOutputStream; // Compare immediate. Sets flags. public class CmpIX86 extends MachConcreteNode implements MachNode { final int _imm; - final String _bop; - CmpIX86( BoolNode bool, TypeInteger ti ) { - super(bool); - _inputs.pop(); // Toss ideal ConstantNode away, embedded into machine op - _bop = bool.op(); // One of <,<=,== - _imm = (int)ti.value(); - assert _imm == ti.value(); + CmpIX86( Node cmp, int imm, boolean swap ) { + super(cmp); + _inputs.del(swap ? 1 : 2); + _imm = imm; } - // Copy constructor does not set reverse edges in, because the basic block - // is changing from the original node - and it must be properly placed in a - // new block. CmpIX86( CmpIX86 cmp ) { - super(cmp); - _bop = cmp._bop; + super((Node)null); + addDef(null); + addDef(cmp.in(1)); _imm = cmp._imm; - // While in(0) will be handled by the caller, all other edges must be - // handled in the constructor. - cmp.in(1)._outputs.push(this); } - @Override public RegMask regmap(int i) { assert i==1; return x86_64_v2.RMASK; } + @Override public String op() { return _imm==0 ? "test" : "cmp"; } + @Override public RegMask regmap(int i) { return x86_64_v2.RMASK; } @Override public RegMask outregmap() { return x86_64_v2.FLAGS_MASK; } @Override public boolean isClone() { return true; } @Override public Node copy() { return new CmpIX86(this); } - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); + @Override public final void encoding( Encoding enc ) { + short dst = enc.reg(in(1)); // Also src1 + enc.add1(x86_64_v2.rex(0, dst, 0)); + // opcode; 0x81 or 0x83; 0x69 or 0x6B + enc.add1( 0x81 + (x86_64_v2.imm8(_imm) ? 2 : 0) ); + enc.add1( x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, 7, dst) ); + // immediate(4 bytes) 32 bits or (1 byte)8 bits + if( x86_64_v2.imm8(_imm) ) enc.add1(_imm); + else enc.add4(_imm); } - @Override public void asm(CodeGen code, SB sb) { String dst = code.reg(this); if( dst!="flags" ) sb.p(dst).p(" = "); sb.p(code.reg(in(1))); if( _imm != 0 ) sb.p(", #").p(_imm); } - - @Override public String op() { return _imm==0 ? "test" : "cmp"; } - } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpMemX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpMemX86.java index f9ace702..489853eb 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpMemX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpMemX86.java @@ -1,33 +1,78 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; import com.seaofnodes.simple.SB; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeFloat; public class CmpMemX86 extends MemOpX86 { - final boolean _invert; // Op switched LHS, RHS - CmpMemX86( BoolNode bool, LoadNode ld , Node base, Node idx, int off, int scale, int imm, Node val, boolean invert ) { + final boolean _swap; // Op switched LHS, RHS + CmpMemX86( BoolNode bool, LoadNode ld, Node base, Node idx, int off, int scale, int imm, Node val, boolean swap ) { super(bool,ld, base, idx, off, scale, imm, val ); - _invert = invert; + _swap = swap; + assert !_swap || val() == null || bool.op()=="==" || bool.op()=="!="; // Cannot swap with immediate } - - // Register mask allowed as a result. 0 for no register. + @Override public String op() { return ((val()==null && _imm==0) ? "test" : "cmp") + _sz; } @Override public RegMask outregmap() { return x86_64_v2.FLAGS_MASK; } + @Override public void encoding( Encoding enc ) { + // REX.W + 81 /7 id CMP r/m64, imm32 | REX.W + 39 /r CMP r/m64,r64 + // CMP [mem], imm32 + + short ptr = enc.reg(ptr()); + short idx = enc.reg(idx()); + short src = enc.reg(val()); + + if(src == -1) { + assert !_swap; + int log = _declaredType.log_size(); + x86_64_v2.rexF(-1, ptr, idx, log==3, enc); + switch(log) { + case 0: enc.add1(0x80); break; + case 1: enc.add1(0x66).add1(0x81); break; + case 2: enc.add1(0x81); break; + case 3: enc.add1(0x81); break; + } + x86_64_v2.indirectAdr(_scale, idx, ptr, _off, 7, enc); + switch(log) { + case 0: enc.add1(_imm); break; + case 1: enc.add2(_imm); break; + case 2: enc.add4(_imm); break; + case 3: enc.add8(_imm); break; + } + } else { + encVal(enc, _declaredType, ptr, idx, src, _off, _scale, _swap); + } + } + + static void encVal(Encoding enc, Type decl, short ptr, short idx, short src, int off, int scale, boolean _swap) { + if(decl instanceof TypeFloat) { + src -= (short)x86_64_v2.XMM_OFFSET; + enc.add1(decl==TypeFloat.F32 ? 0xF3 : 0xF2).add1(0x0F).add1(0xC2); + } else { + int log = decl.log_size(); + x86_64_v2.rexF(src, ptr, idx, log == 3, enc); + int swap = _swap ? 2 : 0; + switch(log) { + case 0: enc.add1(0x38+swap); break; + case 1: enc.add1(0x66).add1(0x39+swap); break; + case 2: enc.add1(0x39+swap); break; + case 3: enc.add1(0x39+swap); break; + } + } + x86_64_v2.indirectAdr(scale, idx, ptr, off, src, enc); + } // General form: "cmp dst = src, [base + idx<<2 + 12]" @Override public void asm(CodeGen code, SB sb) { String dst = code.reg(this); - if( dst!="FLAGS" ) sb.p(dst).p(" = "); - if( _invert ) asm_address(code,sb).p(","); + if( dst!="flags" ) sb.p(dst).p(" = "); + if( _swap ) asm_address(code,sb).p(","); if( val()==null ) { if( _imm!=0 ) sb.p("#").p(_imm); } else { sb.p(code.reg(val())); } - if( !_invert ) asm_address(code,sb.p(", ")); + if( !_swap ) asm_address(code,sb.p(", ")); } - - @Override public String op() { return ((val()==null && _imm==0) ? "test" : "cmp") + _sz; } - } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpX86.java index 41ce33d2..06a36d99 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpX86.java @@ -1,29 +1,26 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.TypeInteger; -import java.io.ByteArrayOutputStream; public class CmpX86 extends MachConcreteNode implements MachNode { - CmpX86( Node cmp ) { super(cmp); } - - @Override public RegMask regmap(int i) { assert i==1 || i==2; return x86_64_v2.RMASK; } + CmpX86( Node add ) { super(add); } + @Override public String op() { return "cmp"; } + @Override public RegMask regmap(int i) { return x86_64_v2.RMASK; } @Override public RegMask outregmap() { return x86_64_v2.FLAGS_MASK; } - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); - } + @Override public void encoding( Encoding enc ) { + short dst = enc.reg(in(1)); + short src = enc.reg(in(2)); - // General form: "add dst += src" + enc.add1(x86_64_v2.rex(dst, src, 0)); + enc.add1(0x3B); + enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, src)); + } @Override public void asm(CodeGen code, SB sb) { String dst = code.reg(this); - if( dst!="FLAGS" ) sb.p(dst).p(" = "); + if( dst!="flags" ) sb.p(dst).p(" = "); sb.p(code.reg(in(1))).p(", ").p(code.reg(in(2))); } - - @Override public String op() { return "cmp"; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivFX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivFX86.java index f74b7348..9509a1f5 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivFX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivFX86.java @@ -1,35 +1,34 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.TypeInteger; -import java.io.ByteArrayOutputStream; public class DivFX86 extends MachConcreteNode implements MachNode { DivFX86( Node divf) { super(divf); } - - // Register mask allowed on input i. - @Override public RegMask regmap(int i) { assert i==1 || i==2; return x86_64_v2.XMASK; } - // Register mask allowed as a result. 0 for no register. - @Override public RegMask outregmap() { return x86_64_v2.XMASK; } - // Output is same register as input#1 + @Override public String op() { return "divf"; } + @Override public RegMask regmap(int i) { return x86_64_v2.XMASK; } + @Override public RegMask outregmap() { return x86_64_v2.XMASK; } @Override public int twoAddress() { return 1; } - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); + @Override public void encoding( Encoding enc ) { + // F2 0F 5E /r DIVSD xmm1, xmm2/m64 + short dst = (short)(enc.reg(this ) - x86_64_v2.XMM_OFFSET); + short src = (short)(enc.reg(in(2)) - x86_64_v2.XMM_OFFSET); + + // Fopcode + enc.add1(0xF2); + // rex prefix must come next (REX.W is not set) + x86_64_v2.rexF(dst, src, 0, false, enc); + + enc.add1(0x0F).add1(0x5E); + + enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, src)); } // General form: "divf dst /= src" @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" / ").p(code.reg(in(2))); - } - - @Override public String op() { - // 8 bytes = 64 bits - return "divf"; + sb.p(code.reg(this)).p(" /= ").p(code.reg(in(2))); } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivX86.java index 54372629..87b8b0c3 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivX86.java @@ -1,35 +1,33 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.TypeInteger; -import java.io.ByteArrayOutputStream; public class DivX86 extends MachConcreteNode implements MachNode { DivX86( Node div ) { super(div); } - - // Register mask allowed on input i. + @Override public String op() { return "div"; } @Override public RegMask regmap(int i) { return (i==1) ? x86_64_v2.RAX_MASK : x86_64_v2.RMASK; } - // Register mask allowed as a result. 0 for no register. @Override public RegMask outregmap() { return x86_64_v2.RAX_MASK; } - - // DIV encodes a CQO which sign extends RAX into RDX, killing RDX. @Override public RegMask killmap() { return x86_64_v2.RDX_MASK; } - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); + @Override public void encoding( Encoding enc ) { + // REX.W + F7 /7 IDIV r/m64 + short src = enc.reg(in(2)); + // sign extend rax before 128/64 bits division + // sign extend rax to rdx:rax + enc.add1(x86_64_v2.REX_W); + enc.add1(0x99); + enc.add1(x86_64_v2.rex(0, src, 0)); + // divide + enc.add1(0xF7); // opcode + enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, 0x07, src)); } - // General form: "div dst /= src" @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" / ").p(code.reg(in(2))); } - - @Override public String op() { return "div"; } @Override public String comment() { return "kill rdx"; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FltX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FltX86.java index 47f7149d..e3b20003 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FltX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FltX86.java @@ -1,35 +1,47 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; import com.seaofnodes.simple.SB; -import com.seaofnodes.simple.Utils; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; -import com.seaofnodes.simple.node.ConstantNode; -import com.seaofnodes.simple.node.MachNode; -import java.io.ByteArrayOutputStream; +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; -public class FltX86 extends ConstantNode implements MachNode { +public class FltX86 extends ConstantNode implements MachNode, RIPRelSize{ + private byte _encSize; FltX86(ConstantNode con ) { super(con); } - @Override public String op() { return "fld"; } - - // Register mask allowed on input i. 0 for no register. + @Override public String op() { return "ld8"; } @Override public RegMask regmap(int i) { return null; } - // General int registers @Override public RegMask outregmap() { return x86_64_v2.XMASK; } @Override public boolean isClone() { return true; } - @Override public FltX86 copy() { return new FltX86(this); } + @Override public Node copy() { return new FltX86(this); } - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); + @Override public void encoding( Encoding enc ) { + // Load the constant into a FPR + // movsd xmm, [rip + 0] + // F2 0F 10 /r MOVSD xmm1, m64 + _encSize = 1/*Fop*/+0/*REX*/+2/*op*/+1/*MODRM*/+4/*offset*/; + short dst = (short)(enc.reg(this ) - x86_64_v2.XMM_OFFSET); + // Fopcode + enc.add1(0xF2); + // rex prefix must come next (REX.W is not set) + _encSize += x86_64_v2.rexF(dst, 0, 0, false, enc); + enc.add1(0x0F).add1(0x10); + // hard-code rip here + enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.INDIRECT, dst, 0x05)); + enc.add4(0); + enc.largeConstant(this,_con,_encSize-4,2/*ELF encoding PC32*/); } - // Human-readable form appended to the SB. Things like the encoding, - // indentation, leading address or block labels not printed here. - // Just something like "ld4\tR17=[R18+12] // Load array base". - // General form: "op\tdst=src+src" - @Override public void asm(CodeGen code, SB sb) { - _con.print(sb.p(code.reg(this)).p(" #")); + // Delta is from opcode start + @Override public byte encSize(int delta) { + // TODO: 1-byte RIP offset? + return (byte)_encSize; + } + + // Delta is from opcode start + @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { + enc.patch4(opStart+opLen-4,delta-opLen); } + @Override public void asm(CodeGen code, SB sb) { + _con.print(sb.p(code.reg(this)).p(" = #")); + } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FunX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FunX86.java index 7346ae05..07418eb0 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FunX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FunX86.java @@ -1,22 +1,34 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; +import com.seaofnodes.simple.SB; import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; -import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.codegen.Encoding; import com.seaofnodes.simple.node.FunNode; import com.seaofnodes.simple.node.MachNode; -import java.io.ByteArrayOutputStream; public class FunX86 extends FunNode implements MachNode { FunX86( FunNode fun ) { super(fun); } + @Override public void postSelect(CodeGen code) { + super.postSelect(code); + // One more for RPC pre-pushed; X86 special + _maxArgSlot++; + } - // Register mask allowed on input i. 0 for no register. - @Override public RegMask regmap(int i) { return null; } - // Register mask allowed as a result. 0 for no register. - @Override public RegMask outregmap() { return null; } + @Override public void computeFrameAdjust(CodeGen code, int maxReg) { + super.computeFrameAdjust(code,maxReg); + // Alignment, but off by RPC + if( _hasCalls ) // If non-leaf, pad to 16b + _frameAdjust = ((_frameAdjust+8+8) & -16)-8; + } - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - return 0; + @Override public void encoding( Encoding enc ) { + int sz = _frameAdjust; + if( sz == 0 ) return; // Skip if no frame adjust + // opcode: 0x83, addi rsp with immediate 8 + enc.add1( x86_64_v2.REX_W ).add1( x86_64_v2.imm8(sz) ? 0x83 : 0x81 ); + enc.add1( x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, 0b101, x86_64_v2.RSP) ); + if( x86_64_v2.imm8(sz) ) enc.add1(sz); + else enc.add4(sz); } + } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/I2f8X86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/I2f8X86.java index d99c92a8..d3213ae4 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/I2f8X86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/I2f8X86.java @@ -1,30 +1,31 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.TypeInteger; -import java.io.ByteArrayOutputStream; public class I2f8X86 extends MachConcreteNode implements MachNode { I2f8X86(Node i2f8 ) { super(i2f8); } - - // Register mask allowed on input i. + @Override public String op() { return "cvtf"; } @Override public RegMask regmap(int i) { assert i==1; return x86_64_v2.WMASK; } - // Register mask allowed as a result. 0 for no register. @Override public RegMask outregmap() { return x86_64_v2.XMASK; } - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); - } + @Override public void encoding( Encoding enc ) { + // F2 0F 2A /r CVTSI2SD xmm1, r32/m32 + short dst = (short)(enc.reg(this ) - x86_64_v2.XMM_OFFSET); + short src = enc.reg(in(1)); + + // Fopcode + enc.add1(0xF2); + // rex prefix must come next (REX.W is not set) + x86_64_v2.rexF(dst, src, 0, true, enc); - // General form: "i2f8 (flt)int_value" + enc.add1(0x0F).add1(0x2A); + + enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, src)); + } @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(this)).p(" = ").p("(flt)").p(code.reg(in(1))); } - - @Override public String op() { return "i2f8"; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ImmX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ImmX86.java new file mode 100644 index 00000000..760d6805 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ImmX86.java @@ -0,0 +1,37 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.TypeInteger; + +public abstract class ImmX86 extends MachConcreteNode implements MachNode { + final int _imm; + ImmX86( Node add, int imm ) { + super(add); + _inputs.pop(); // Pop constant input off + _imm = imm; + } + @Override public RegMask regmap(int i) { return x86_64_v2.RMASK; } + @Override public RegMask outregmap() { return x86_64_v2.WMASK; } + @Override public int twoAddress() { return 1; } + + abstract int opcode(); + abstract int mod(); + + @Override public final void encoding( Encoding enc ) { + short dst = enc.reg(this); // Also src1 + enc.add1(x86_64_v2.rex(0, dst, 0)); + // opcode; 0x81 or 0x83; 0x69 or 0x6B + enc.add1( opcode() + (x86_64_v2.imm8(_imm) ? 2 : 0) ); + enc.add1( x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, mod(), dst) ); + // immediate(4 bytes) 32 bits or (1 byte)8 bits + if( x86_64_v2.imm8(_imm) ) enc.add1(_imm); + else enc.add4(_imm); + } + + // General form: "addi dst += #imm" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" ").p(glabel()).p("= #").p(_imm); + } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/IntX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/IntX86.java index fcd39e3f..6d9110f2 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/IntX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/IntX86.java @@ -1,40 +1,65 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; -import com.seaofnodes.simple.node.ConstantNode; -import com.seaofnodes.simple.node.MachNode; -import com.seaofnodes.simple.node.Node; +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; import com.seaofnodes.simple.type.Type; import com.seaofnodes.simple.type.TypeInteger; -import java.io.ByteArrayOutputStream; - // Integer constants public class IntX86 extends ConstantNode implements MachNode { IntX86( ConstantNode con ) { super(con); } - @Override public String op() { return _con == Type.NIL || _con == TypeInteger.ZERO ? "xor" : "ldi"; } - - // Register mask allowed on input i. 0 for no register. + @Override public boolean isClone() { return true; } + @Override public Node copy() { return new IntX86(this); } @Override public RegMask regmap(int i) { return null; } - // General int registers @Override public RegMask outregmap() { return x86_64_v2.WMASK; } - // Zero-set uses XOR kills flags @Override public RegMask killmap() { return _con == Type.NIL || _con == TypeInteger.ZERO ? x86_64_v2.FLAGS_MASK : null; } - @Override public boolean isClone() { return true; } - @Override public Node copy() { return new IntX86(this); } + @Override public void encoding( Encoding enc ) { + short dst = enc.reg(this); + // Short form for zero + if( _con==Type.NIL || _con==TypeInteger.ZERO ) { + // XOR dst,dst. Can skip REX is dst is low 8, makes this a 32b + // xor, which will also zero the high bits. + if( dst >= 8 ) enc.add1(x86_64_v2.rex(dst, dst, 0)); + enc.add1(0x33); // opcode + enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, dst)); + return; + } + + // Simply move the constant into a GPR + // Conditional encoding based on 64 or 32 bits + //REX.W + C7 /0 id MOV r/m64, imm32 - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); + long imm = ((TypeInteger)_con).value(); + if (Integer.MIN_VALUE <= imm && imm < 0) { + // We need sign extension, so use imm32 into 64 bit register + // REX.W + C7 /0 id MOV r/m64, imm32 + enc.add1(x86_64_v2.rex(0, dst, 0)); + enc.add1(0xC7); // 32 bits encoding + enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, 0x00, dst)); + enc.add4((int)imm); + } else if (0 <= imm && imm <= 0xFFFFFFFFL) { + // We want zero extension into 64 bit register + // so move 32 bit into 32 bit register which zeros + // the upper bits in the 64 bit register. + // B8+ rd id MOV r32, imm32 + if (dst >= 8) enc.add1(0x41); + enc.add1(0xB8 + (dst & 0x07)); + enc.add4((int)imm); + } else { + // Just write the full 64 bit constant + // REX.W + B8+ rd io MOV r64, imm64 + enc.add1(x86_64_v2.rex(0, dst, 0)); + enc.add1(0xB8 + (dst & 0x07)); + enc.add8(imm); // 64 bits encoding + } } // Human-readable form appended to the SB. Things like the encoding, diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/JmpX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/JmpX86.java index 173f9ee7..d7acf8b8 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/JmpX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/JmpX86.java @@ -1,48 +1,80 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.Type; -import com.seaofnodes.simple.type.TypeInteger; -import java.io.ByteArrayOutputStream; // Jump on flags, uses flags -public class JmpX86 extends IfNode implements MachNode { - final String _bop; +public class JmpX86 extends IfNode implements MachNode, RIPRelSize { + String _bop; JmpX86( IfNode iff, String bop ) { super(iff); _bop = bop; } - + @Override public String op() { return "j"+_bop; } @Override public String label() { return op(); } - - @Override public void postSelect() { + @Override public void postSelect(CodeGen code) { Node set = in(1); + if( set==null ) return; // Never-node cutout Node cmp = set.in(1); // Bypass an expected Set and just reference the cmp directly - if( set instanceof SetX86 && (cmp instanceof CmpX86 || cmp instanceof CmpIX86 || cmp instanceof CmpMemX86 || cmp instanceof CmpFX86) ) - _inputs.set(1,cmp); - else + if( set instanceof SetX86 setx && (cmp instanceof CmpX86 || cmp instanceof CmpIX86 || cmp instanceof CmpMemX86 || cmp instanceof CmpFX86) ) { + _inputs.set( 1, cmp ); + _bop = setx._bop; + } else throw Utils.TODO(); } @Override public RegMask regmap(int i) { assert i==1; return x86_64_v2.FLAGS_MASK; } @Override public RegMask outregmap() { return null; } + @Override public void negate() { _bop = negate(_bop); } - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); + @Override public void encoding( Encoding enc ) { + if( in(1)!=null ) { + int op = x86_64_v2.jumpop(_bop); + enc.add1(op-16).add1(0); // Short form jump + } else { + if( _bop=="!=" ) return; // Inverted, no code + enc.add1(0xEB).add1(0); // Never-node + } + enc.jump(this,cproj(0)); } - @Override public void asm(CodeGen code, SB sb) { - String src = code.reg(in(1)); - if( src!="flags" ) sb.p(src); + // Delta is from opcode start, but X86 measures from the end of the 2-byte encoding + @Override public byte encSize(int delta) { + if( in(1)==null && _bop=="!=" ) return 0; // Inverted never-node, no code + return (byte)(x86_64_v2.imm8(delta-2) ? 2 : 6); } - @Override public String op() { return "j"+_bop; } + // Delta is from opcode start + @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { + assert !( in(1)==null && _bop=="!=" ); // Inverted never-node, no code no patch + byte[] bits = enc.bits(); + if( opLen==2 ) { + assert in(1)==null || bits[opStart] == x86_64_v2.jumpop(_bop)-16; + delta -= 2; // Offset from opcode END + assert (byte)delta==delta; + bits[opStart+1] = (byte)delta; + } else { + assert in(1)==null || bits[opStart] == x86_64_v2.jumpop(_bop)-16; + delta -= 6; // Offset from opcode END + bits[opStart] = 0x0F; + bits[opStart+1] = (byte)(in(1)==null ? 0xE9 : x86_64_v2.jumpop(_bop)); + enc.patch4(opStart+2,delta); + } + } - @Override public String comment() { - return "L"+cproj(1)._nid+", L"+cproj(0)._nid; + @Override public void asm(CodeGen code, SB sb) { + if( in(1)!=null ) { // Never-node + String src = code.reg(in(1)); + if( src!="flags" ) sb.p(src).p(" "); + } else if( _bop=="!=" ) { + sb.p("never"); + return; + } + CFGNode prj = cproj(0).uctrlSkipEmpty(); // 0 is True is jump target + if( !prj.blockHead() ) prj = prj.cfg0(); + sb.p(label(prj)); } + + @Override public String comment() { return "L"+cproj(1)._nid; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LeaX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LeaX86.java index 229c0151..dce8a49a 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LeaX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LeaX86.java @@ -1,18 +1,15 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.TypeInteger; -import java.io.ByteArrayOutputStream; public class LeaX86 extends MachConcreteNode implements MachNode { final int _scale; - final long _offset; - LeaX86( Node add, Node base, Node idx, int scale, long offset ) { + final int _offset; + LeaX86( Node add, Node base, Node idx, int scale, int offset ) { super(add); - assert scale==1 || scale==2 || scale==4 || scale==8; + assert scale==0 || scale==1 || scale==2 || scale==3; _inputs.pop(); _inputs.pop(); _inputs.push(base); @@ -21,14 +18,30 @@ public class LeaX86 extends MachConcreteNode implements MachNode { _offset = offset; } - // Register mask allowed on input i. + @Override public String op() { return "lea"; } @Override public RegMask regmap(int i) { assert i==1 || i==2; return x86_64_v2.RMASK; } - // Register mask allowed as a result. 0 for no register. @Override public RegMask outregmap() { return x86_64_v2.WMASK; } - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); + @Override public void encoding( Encoding enc ) { + // REX.W + 8D /r LEA r64,m + short dst = enc.reg(this); + short ptr = enc.reg(in(1)); + short idx = enc.reg(in(2)); + // ptr is null + // just do: [(index * s) + disp32] + if( ptr == -1 ) { + enc.add1(x86_64_v2.rex(dst, 0, idx)); + enc.add1(0x8D); // opcode + enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.INDIRECT, dst, 0x04)); + enc.add1(x86_64_v2.sib(_scale, idx, x86_64_v2.RBP)); + enc.add4(_offset); + return; + } + + enc.add1(x86_64_v2.rex(dst, ptr, idx)); + enc.add1(0x8D); // opcode + // rsp is hard-coded here(0x04) + x86_64_v2.indirectAdr(_scale, idx, ptr, _offset, dst, enc); } // General form: "lea dst = base + 4*idx + 12" @@ -36,9 +49,7 @@ public class LeaX86 extends MachConcreteNode implements MachNode { sb.p(code.reg(this)).p(" = "); if( in(1) != null ) sb.p(code.reg(in(1))).p(" + "); - sb.p(code.reg(in(2))).p("*").p(_scale); + sb.p(code.reg(in(2))).p("<<").p(_scale); if( _offset!=0 ) sb.p(" + #").p(_offset); } - - @Override public String op() { return "lea"; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LoadX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LoadX86.java index 893772a6..6e72a540 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LoadX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LoadX86.java @@ -1,20 +1,55 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; import com.seaofnodes.simple.SB; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.LoadNode; import com.seaofnodes.simple.node.Node; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeFloat; +import com.seaofnodes.simple.type.TypeInteger; +import com.seaofnodes.simple.type.TypeMemPtr; public class LoadX86 extends MemOpX86 { LoadX86( LoadNode ld, Node base, Node idx, int off, int scale ) { super(ld,ld, base, idx, off, scale, 0); } - @Override public String op() { return "ld"+_sz; } - - // Register mask allowed as a result. 0 for no register. @Override public RegMask outregmap() { return x86_64_v2.MEM_MASK; } + @Override public void encoding( Encoding enc ) { + // REX.W + 8B /r MOV r64, r/m64 + // Zero extension for u8, u16 and u32 but sign extension i8, i16, i32 + // Use movsx and movzx + short dst = enc.reg(this ); + short ptr = enc.reg(ptr()); + short idx = enc.reg(idx()); + enc(enc, _declaredType, dst, ptr, idx, _off, _scale); + } + + static void enc( Encoding enc, Type decl, short dst, short ptr, short idx, int off, int scale ) { + if( decl == TypeFloat.F32) enc.add1(0xF3); + if( decl == TypeFloat.F64) enc.add1(0xF2); + + if( decl.isa(TypeFloat.F64) ) + dst -= (short)x86_64_v2.XMM_OFFSET; + + x86_64_v2.rexF(dst, ptr, idx, decl != TypeInteger.U32 && decl != TypeFloat.F32 && decl != TypeFloat.F64, enc); + + if( false ) ; + else if( decl == TypeFloat.F32 ) enc.add1(0x0F).add1(0x10); // F3 0F 10 /r MOVSS xmm1, m32 + else if( decl == TypeFloat.F64 ) enc.add1(0x0F).add1(0x10); // F2 0F 10 /r MOVSD xmm1, m64 + else if( decl.isa(TypeInteger.I8)) enc.add1(0x0F).add1(0xBE); // sign extend: REX.W + 0F BE /r MOVSX r64, r/m8 + else if( decl == TypeInteger.I16 ) enc.add1(0x0F).add1(0xBF); // sign extend: REX.W + 0F BF /r MOVSX r64, r/m16 + else if( decl == TypeInteger.I32 ) enc.add1(0x63); // sign extend: REX.W + 63 /r MOVSXD r64, r/m32 + else if( decl == TypeInteger.U8 ) enc.add1(0x0F).add1(0xB6); // zero extend: REX.W + 0F B6 /r MOVZX r64, r/m8 + else if( decl == TypeInteger.U16 ) enc.add1(0x0F).add1(0xB7); // zero extend: REX.W + 0F B7 /r MOVZX r64, r/m16 + // Covers U32, I64/BOT, TMP + else if( decl.log_size()>=2 ) enc.add1(0x8B); // zero extend: 8B /r MOV r32, r/m32 + else throw Utils.TODO(); + + // includes modrm internally + x86_64_v2.indirectAdr(scale, idx, ptr, off, dst, enc); + } // General form: "ldN dst,[base + idx<<2 + 12]" diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemAddX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemAddX86.java index 6d39a2b4..a97f3d9f 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemAddX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemAddX86.java @@ -1,19 +1,34 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; import com.seaofnodes.simple.SB; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; public class MemAddX86 extends MemOpX86 { MemAddX86( StoreNode st, Node base, Node idx, int off, int scale, int imm, Node val ) { super(st, st, base, idx, off, scale, imm, val ); } - + @Override public String op() { + return (_imm == 1 ? "inc" : (_imm == -1 ? "dec" : "add")) + _sz; + } // Register mask allowed as a result. 0 for no register. @Override public RegMask outregmap() { return null; } + @Override public void encoding( Encoding enc ) { + // add something to memory + // REX.W + 01 /r | REX.W + 81 /0 id + // ADD [mem], imm32/reg + short ptr = enc.reg(ptr()); + short idx = enc.reg(idx()); + short src = enc.reg(val()); + enc.add1(x86_64_v2.rex(src, ptr, idx)); + // opcode + enc.add1( src == -1 ? 0x81: 0x01); + // includes modrm + x86_64_v2.indirectAdr(_scale, idx, ptr, _off, src == -1 ? 0 : src, enc); + if( src == -1 ) enc.add4(_imm); + } // General form: "add [base + idx<<2 + 12] += src" @Override public void asm(CodeGen code, SB sb) { asm_address(code,sb); @@ -21,8 +36,4 @@ public class MemAddX86 extends MemOpX86 { if( _imm != 1 && _imm != -1 ) sb.p(" += #").p(_imm); } else sb.p(" += ").p(code.reg(val())); } - - @Override public String op() { - return (_imm == 1 ? "inc" : (_imm == -1 ? "dec" : "add")) + _sz; - } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemOpX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemOpX86.java index dee4afd4..97586d87 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemOpX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemOpX86.java @@ -1,11 +1,10 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; import com.seaofnodes.simple.type.*; -import java.io.ByteArrayOutputStream; + import java.lang.StringBuilder; import java.util.BitSet; @@ -15,7 +14,7 @@ import java.util.BitSet; // mem - memory dependence edge // base - Basic object pointer // idx/null - Scaled index offset, or null if none -// val/null - Value to store, as part of a op-to-mem or op-from-mem. Null for loads, or if an immediate is being used +// val/null - Value to store, as part of an op-to-mem or op-from-mem. Null for loads, or if an immediate is being used // Constants: // offset - offset added to base. Can be zero. // scale - scale on index; only 0,1,2,4,8 allowed, 0 is only when index is null @@ -24,13 +23,13 @@ public abstract class MemOpX86 extends MemOpNode implements MachNode { final int _off; // Limit 32 bits final int _scale; // Limit 0,1,2,3 final int _imm; // Limit 32 bits - final char _sz = (char)('0'+(1<<_declaredType.log_size())); + final char _sz; // Handy print name MemOpX86( Node op, MemOpNode mop, Node base, Node idx, int off, int scale, int imm ) { super(op,mop); assert base._type instanceof TypeMemPtr && !(base instanceof AddNode); assert (idx==null && scale==0) || (idx!=null && 0<= scale && scale<=3); - // Copy memory parts from eg the LoadNode over the opcode, e.g. an Add + // Copy memory parts from e.g. the LoadNode over the opcode, e.g. an Add if( op != mop ) { _inputs.set(0,mop.in(0)); // Control from mem op _inputs.set(1,mop.in(1)); // Memory from mem op @@ -42,6 +41,7 @@ public abstract class MemOpX86 extends MemOpNode implements MachNode { _off = off; _scale = scale; _imm = imm; + _sz = (char)('0'+(1<<_declaredType.log_size())); } // Store-based flavors have a value edge @@ -66,20 +66,19 @@ public abstract class MemOpX86 extends MemOpNode implements MachNode { if( i==1 ) return null; // Memory if( i==2 ) return x86_64_v2.RMASK; // base in GPR if( i==3 ) return x86_64_v2.RMASK; // index in GPR - if( i==4 ) return x86_64_v2.MEM_MASK; // value in GPR or XMM - throw Utils.TODO(); + if( i==4 ) return _sz >= 2 + ? x86_64_v2.MEM_MASK // value in GPR or XMM + : x86_64_v2.RMASK; // Bytes and shorts in GPR only + return null; // Anti-dependence } - @Override public int encoding(ByteArrayOutputStream bytes) { throw Utils.TODO(); } - - // "[base + idx<<2 + 12]" SB asm_address(CodeGen code, SB sb) { sb.p("[").p(code.reg(ptr())); if( idx() != null ) { sb.p("+").p(code.reg(idx())); if( _scale != 0 ) - sb.p("<<").p(_scale); + sb.p("*").p((1<<_scale)); } if( _off != 0 ) sb.p("+").p(_off); diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulFX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulFX86.java index 9e996130..c1edb082 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulFX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulFX86.java @@ -1,34 +1,32 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.TypeFloat; -import com.seaofnodes.simple.type.TypeInteger; -import java.io.ByteArrayOutputStream; public class MulFX86 extends MachConcreteNode implements MachNode { MulFX86( Node mulf) { super(mulf); } - - // Register mask allowed on input i. + @Override public String op() { return "mulf"; } @Override public RegMask regmap(int i) { assert i==1 || i==2; return x86_64_v2.XMASK; } - // Register mask allowed as a result. 0 for no register. @Override public RegMask outregmap() { return x86_64_v2.XMASK; } - // Output is same register as input#1 @Override public int twoAddress() { return 1; } - // Ok to swap arguments @Override public boolean commutes() { return true; } - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); + @Override public void encoding( Encoding enc ) { + // F2 0F 59 /r MULSD xmm1,xmm2/m64 + short dst = (short)(enc.reg(this ) - x86_64_v2.XMM_OFFSET); + short src = (short)(enc.reg(in(2)) - x86_64_v2.XMM_OFFSET); + + // Fopcode + enc.add1(0xF2); + // rex prefix must come next (REX.W is not set) + x86_64_v2.rexF(dst, src, 0, false, enc); + + enc.add1(0x0F).add1(0x59).add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, src)); } // General form: "mulf dst *= src" @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" * ").p(code.reg(in(2))); + sb.p(code.reg(this)).p(" *= ").p(code.reg(in(2))); } - - @Override public String op() { return "mulf"; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulIX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulIX86.java index b41a5720..a9a83fb7 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulIX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulIX86.java @@ -1,41 +1,11 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; -import com.seaofnodes.simple.SB; -import com.seaofnodes.simple.Utils; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; -import com.seaofnodes.simple.node.MachConcreteNode; -import com.seaofnodes.simple.node.MachNode; import com.seaofnodes.simple.node.Node; -import com.seaofnodes.simple.type.TypeInteger; -import java.io.ByteArrayOutputStream; - -public class MulIX86 extends MachConcreteNode implements MachNode { - final TypeInteger _ti; - MulIX86(Node mul, TypeInteger ti) {super(mul); _inputs.pop(); _ti = ti;} - - // Register mask allowed on input i. - // This is the normal calling convention - @Override public RegMask regmap(int i) { - // assert i==1; - return x86_64_v2.WMASK; } - // Register mask allowed as a result. 0 for no register. - @Override public RegMask outregmap() { return x86_64_v2.WMASK; } - - - // Output is same register as input#1 - @Override public int twoAddress() { return 1; } - - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); - } - // General form - // General form: "muli dst * #imm" - @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" * #"); - _ti.print(sb); - } +public class MulIX86 extends ImmX86 { + MulIX86( Node add, int imm ) { super(add,imm); } @Override public String op() { return "muli"; } + @Override public String glabel() { return "*"; } + @Override int opcode() { return 0x69; } + @Override int mod() { return 0; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulX86.java index d880208e..3cd1f795 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulX86.java @@ -1,33 +1,29 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.TypeInteger; -import java.io.ByteArrayOutputStream; public class MulX86 extends MachConcreteNode implements MachNode { MulX86( Node mul ) { super(mul); } - - // Register mask allowed on input i. + @Override public String op() { return "mul"; } @Override public RegMask regmap(int i) { assert i==1 || i==2; return x86_64_v2.RMASK; } - // Register mask allowed as a result. 0 for no register. @Override public RegMask outregmap() { return x86_64_v2.WMASK; } - // Output is same register as input#1 @Override public int twoAddress() { return 1; } - // Ok to swap arguments @Override public boolean commutes() { return true; } - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); + @Override public void encoding( Encoding enc ) { + // REX.W + 0F AF /r IMUL r64, r/m64 + short dst = enc.reg(this ); // src1 + short src = enc.reg(in(2)); // src2 + enc.add1(x86_64_v2.rex(dst, src, 0)); + enc.add1(0x0F); // opcode + enc.add1(0xAF); // opcode + enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, src)); } // General form: "mul dst *= src" @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" * ").p(code.reg(in(2))); } - - @Override public String op() { return "mul"; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NegX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NegX86.java new file mode 100644 index 00000000..b8b83cb0 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NegX86.java @@ -0,0 +1,21 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.SB; +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; + +public class NegX86 extends MachConcreteNode implements MachNode { + NegX86(MinusNode not) { super(not); } + @Override public String op() { return "neg"; } + @Override public RegMask regmap(int i) { return x86_64_v2.RMASK; } + @Override public RegMask outregmap() { return x86_64_v2.RMASK; } + @Override public RegMask killmap() { return x86_64_v2.FLAGS_MASK; } + + @Override public void encoding( Encoding enc ) { + short dst = enc.reg(this ); + enc.add1(x86_64_v2.rex(0, dst, 0)); + enc.add1(0xF7); // opcode + enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, 3, dst)); + } + @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(this)); } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NewX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NewX86.java index cd47c64b..0439adfd 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NewX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NewX86.java @@ -1,29 +1,25 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; -import java.io.ByteArrayOutputStream; +import com.seaofnodes.simple.type.TypeFunPtr; public class NewX86 extends NewNode implements MachNode { // A pre-zeroed chunk of memory. NewX86( NewNode nnn ) { super(nnn); } - // Size and pointer result in standard calling convention; null for all the - // memory aliases edges - @Override public RegMask regmap(int i) { return i == 1 ? x86_64_v2.RDI_MASK : null; } - @Override public RegMask outregmap(int i) { return i == 1 ? x86_64_v2.RAX_MASK : null; } - @Override public RegMask outregmap() { return null; } - - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); + @Override public void encoding( Encoding enc ) { + enc.external(this,"calloc"); + // ldi rcx,#1 // number of elements to calloc + enc.add1(0xB8 + _arg2Reg).add4(1); + // E8 cd CALL rel32; + enc.add1(0xE8); + enc.add4(0); // offset } - - // General form: "alloc #bytes" + // General form: "alloc #bytes PC" @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(size())); + sb.p("ldi rcx = #1\n"); + sb.p("call #calloc"); } - @Override public String op() { return "alloc"; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NotX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NotX86.java index a4b82de8..b23249b8 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NotX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NotX86.java @@ -1,26 +1,39 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; import com.seaofnodes.simple.SB; -import com.seaofnodes.simple.Utils; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.LRG; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; -import java.io.ByteArrayOutputStream; public class NotX86 extends MachConcreteNode implements MachNode { - NotX86(NotNode not) {super(not);} + NotX86(NotNode not) { super(not); } + @Override public String op() { return "not"; } @Override public RegMask regmap(int i) { return x86_64_v2.RMASK; } @Override public RegMask outregmap() { return x86_64_v2.RMASK; } + @Override public RegMask killmap() { return x86_64_v2.FLAGS_MASK; } + + @Override public void encoding( Encoding enc ) { + assert !(in(1) instanceof NotNode); // Cleared out by peeps + assert !(in(1) instanceof BoolNode); // Cleared out by peeps + short dst = enc.reg(this ); + short src = enc.reg(in(1)); - @Override public int twoAddress( ) { return 0; } + // Pre-zero using XOR dst,dst; since zero'd will not have a + // byte-dependency from the setz. Can skip REX is dst is low 8, makes + // this a 32b xor, which will also zero the high bits. + if( dst >= 8 ) enc.add1(x86_64_v2.rex(dst, dst, 0)); + enc.add1(0x33); // opcode + enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, dst)); - // NotNode(BoolNode) = Inverted BoolNode(1) - // NotNode(NotNode(BoolNode)) = BoolNode(2) - // anything else to test & setz(3) - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); + // test rdi,rdi + enc.add1(x86_64_v2.rex(src, src, 0)); + enc.add1(0x85); + enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, src, src)); + + // setz (sete dil) + enc.add1(x86_64_v2.rex(dst, 0, 0)); + enc.add1(0x0F); + enc.add1(0x94); + enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, 0)); } @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(this)); } - @Override public String op() { return "not"; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/OrIX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/OrIX86.java index 949ac6c2..15f4824d 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/OrIX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/OrIX86.java @@ -1,43 +1,11 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; -import com.seaofnodes.simple.SB; -import com.seaofnodes.simple.Utils; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; -import com.seaofnodes.simple.node.MachConcreteNode; -import com.seaofnodes.simple.node.MachNode; import com.seaofnodes.simple.node.Node; -import com.seaofnodes.simple.type.TypeInteger; -import java.io.ByteArrayOutputStream; - -public class OrIX86 extends MachConcreteNode implements MachNode { - - final TypeInteger _ti; - OrIX86(Node or, TypeInteger ti) {super(or); _inputs.pop(); _ti = ti;} - - // Register mask allowed on input i. - // This is the normal calling convention - @Override public RegMask regmap(int i) { - //assert i==1; - return x86_64_v2.WMASK; } - // Register mask allowed as a result. 0 for no register. - @Override public RegMask outregmap() { return x86_64_v2.WMASK; } - - - // Output is same register as input#1 - @Override public int twoAddress() { return 1; } - - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); - } - - // General form - // General form: "ori ori | #imm" - @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" | #"); - _ti.print(sb); - } +public class OrIX86 extends ImmX86 { + OrIX86( Node add, int imm ) { super(add,imm); } @Override public String op() { return "ori"; } + @Override public String glabel() { return "|"; } + @Override int opcode() { return 0x81; } + @Override int mod() { return 1; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/OrX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/OrX86.java index 6d99fe3b..bc8f0ea8 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/OrX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/OrX86.java @@ -1,35 +1,11 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; -import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; -import com.seaofnodes.simple.node.*; -import java.io.ByteArrayOutputStream; - -public class OrX86 extends MachConcreteNode implements MachNode{ - OrX86(Node or) { - super(or); - } - - // Register mask allowed on input i. - // This is the normal calling convention - @Override public RegMask regmap(int i) { assert i==1; return x86_64_v2.WMASK; } - - @Override public RegMask outregmap() { return x86_64_v2.WMASK; } - - // Output is same register as input#1 - @Override public int twoAddress() { return 1; } - - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); - } - - // General form - // General form: "or dst | src" - @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))); - } +import com.seaofnodes.simple.node.Node; +public class OrX86 extends RegX86 { + OrX86( Node add ) { super(add); } @Override public String op() { return "or"; } + @Override public String glabel() { return "|"; } + @Override int opcode() { return 0x0B; } + @Override public boolean commutes() { return true; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ParmX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ParmX86.java index 69ebb74e..5cfdd804 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ParmX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ParmX86.java @@ -1,27 +1,17 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; -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.RegMask; import com.seaofnodes.simple.node.MachNode; import com.seaofnodes.simple.node.ParmNode; -import java.io.ByteArrayOutputStream; public class ParmX86 extends ParmNode implements MachNode { final RegMask _rmask; ParmX86( ParmNode parm ) { super(parm); - _rmask = x86_64_v2.callInMask(fun().sig(),_idx); + _rmask = x86_64_v2.callInMask(fun().sig(),_idx,1/*RPC*/); } - - // Register mask allowed on input i. 0 for no register. - @Override public RegMask regmap(int i) { return _rmask; } - // Register mask allowed as a result. Calling convention register + @Override public RegMask regmap(int i) { return null; } @Override public RegMask outregmap() { return _rmask; } - - // Encoding is appended into the byte array. Returns size - @Override public int encoding(ByteArrayOutputStream bytes) { - return 0; - } + @Override public void encoding( Encoding enc ) { } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ProjX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ProjX86.java index 7ac014e6..046c7b4c 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ProjX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ProjX86.java @@ -1,23 +1,15 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; -import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.codegen.Encoding; import com.seaofnodes.simple.codegen.RegMask; import com.seaofnodes.simple.node.MachNode; import com.seaofnodes.simple.node.ProjNode; -import java.io.ByteArrayOutputStream; public class ProjX86 extends ProjNode implements MachNode { ProjX86( ProjNode p ) { super(p); } - - // Register mask allowed on input i. 0 for no register. @Override public RegMask regmap(int i) { return null; } - // Register mask allowed as a result. 0 for no register. @Override public RegMask outregmap() { return ((MachNode)in(0)).outregmap(_idx); } - - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - return 0; - } + @Override public void encoding( Encoding enc ) {} } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RegX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RegX86.java new file mode 100644 index 00000000..0997644d --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RegX86.java @@ -0,0 +1,26 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; + +public abstract class RegX86 extends MachConcreteNode { + RegX86( Node add ) { super(add); } + @Override public RegMask regmap(int i) { return x86_64_v2.RMASK; } + @Override public RegMask outregmap() { return x86_64_v2.WMASK; } + @Override public int twoAddress() { return 1; } + abstract int opcode(); + @Override public void encoding( Encoding enc ) { + // REX.W + 01 /r + short dst = enc.reg(in(1)); // src1 + short src = enc.reg(in(2)); // src2 + + enc.add1(x86_64_v2.rex(dst, src, 0)); + enc.add1(opcode()); // opcode + enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, src)); + } + // General form: "add dst += src" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" ").p(glabel()).p("= ").p(code.reg(in(2))); + } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RetX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RetX86.java index 433f64cc..a35d43a2 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RetX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RetX86.java @@ -1,49 +1,19 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; -import com.seaofnodes.simple.node.FunNode; -import com.seaofnodes.simple.node.ReturnNode; -import com.seaofnodes.simple.node.MachNode; -import com.seaofnodes.simple.type.TypeFloat; -import java.io.ByteArrayOutputStream; +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; -// Return public class RetX86 extends ReturnNode implements MachNode { RetX86( ReturnNode ret, FunNode fun ) { super(ret, fun); fun.setRet(this); } - - // Correct Nodes outside the normal edges - @Override public void postSelect() { - FunNode fun = (FunNode)rpc().in(0); - _fun = fun; - fun.setRet(this); - } - - // Register mask allowed on input i. - // This is the normal calling convention - @Override public RegMask regmap(int i) { return x86_64_v2.retMask(_fun.sig(),i); } - // Register mask allowed as a result. 0 for no register. - @Override public RegMask outregmap() { return null; } - - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - // Without imm form - // Near return - int beforeSize = bytes.size(); - bytes.write(0xC3); // opcode - - return bytes.size() - beforeSize; - } - - @Override public void asm(CodeGen code, SB sb) { - // Post code-gen, just print the "ret" - if( code._phase.ordinal() <= CodeGen.Phase.RegAlloc.ordinal() ) - // Prints return reg (either RAX or XMM0), RPC (always [rsp-4]) and - // then the callee-save registers. - for( int i=2; i<nIns(); i++ ) - sb.p(code.reg(in(i))).p(" "); + @Override public void encoding( Encoding enc ) { + int sz = fun()._frameAdjust; + if( sz != 0 ) { + enc.add1( x86_64_v2.REX_W ).add1( x86_64_v2.imm8(sz) ? 0x83 : 0x81 ); + enc.add1( x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, 0b000, x86_64_v2.RSP) ); + if( x86_64_v2.imm8(sz) ) enc.add1(sz); + else enc.add4(sz); + } + enc.add1(0xC3); } - - @Override public String op() { return "ret"; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SarIX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SarIX86.java index 9f942cc6..07256604 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SarIX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SarIX86.java @@ -1,39 +1,40 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; -import com.seaofnodes.simple.*; +import com.seaofnodes.simple.SB; import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.Encoding; import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.node.Node; + import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.TypeInteger; -import java.io.ByteArrayOutputStream; // Arithmetic Right Shift public class SarIX86 extends MachConcreteNode implements MachNode { - final TypeInteger _ti; - SarIX86(Node sar, TypeInteger ti) {super(sar); _inputs.pop(); _ti = ti;} - - // Register mask allowed on input i. - // This is the normal calling convention - @Override public RegMask regmap(int i) { - // assert i==1; - return x86_64_v2.RMASK; } - // Register mask allowed as a result. 0 for no register. - @Override public RegMask outregmap() { return x86_64_v2.WMASK; } + final int _imm; + SarIX86( Node sari, int imm ) { super(sari); assert x86_64_v2.imm8(imm); _inputs.pop(); _imm = imm; } + @Override public String op() { return "sari"; } + @Override public String glabel() { return ">>"; } + int opcode() { return 0xC1; } + int mod() { return 7; } - // Output is same register as input#1 + @Override public RegMask regmap(int i) { return x86_64_v2.RMASK; } + @Override public RegMask outregmap() { return x86_64_v2.WMASK; } @Override public int twoAddress() { return 1; } - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); + @Override public void encoding(Encoding enc) { + short dst = enc.reg(this); // Also src1 + enc.add1(x86_64_v2.rex(0, dst, 0)); + enc.add1( opcode()); + + enc.add1( x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, mod(), dst) ); + + // immediate(4 bytes) 32 bits or (1 byte)8 bits + if( x86_64_v2.imm8(_imm) ) enc.add1(_imm); + else enc.add4(_imm); } - // General form - // General form: "sari dst << #imm" @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" >> #"); - _ti.print(sb); + sb.p(code.reg(this)).p(" ").p(glabel()).p("= #").p(_imm); } - - @Override public String op() { return "sari"; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SarX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SarX86.java index ee7a2f56..b1be643c 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SarX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SarX86.java @@ -1,41 +1,24 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; -import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.Type; -import com.seaofnodes.simple.type.TypeInteger; -import java.io.ByteArrayOutputStream; -import java.util.BitSet; -import java.lang.StringBuilder; +import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.Node; - -public class SarX86 extends MachConcreteNode implements MachNode { - SarX86(Node sar) { super(sar); } - - // Register mask allowed on input i. - // This is the normal calling convention +public class SarX86 extends RegX86 { + SarX86( Node add ) { super(add); } + @Override public String op() { return "sar"; } + @Override public String glabel() { return ">>"; } @Override public RegMask regmap(int i) { - if(i == 1) return x86_64_v2.WMASK; - if(i == 2) return x86_64_v2.RCX_MASK; - throw Utils.TODO(); - } - // Register mask allowed as a result. 0 for no register. - @Override public RegMask outregmap() { return x86_64_v2.WMASK; } - - // Output is same register as input#1 - @Override public int twoAddress() { return 1; } - - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { + if (i == 1) return x86_64_v2.WMASK; + if (i == 2) return x86_64_v2.RCX_MASK; throw Utils.TODO(); } - - // General form - @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(" >> ").p(code.reg(in(1))); + @Override int opcode() { return 0xD3; } + @Override public void encoding( Encoding enc ) { + short dst = enc.reg(this ); // src1 + short src = enc.reg(in(2)); // src2 + enc.add1(x86_64_v2.rex(0, dst, 0)); + enc.add1(opcode()); // opcode + enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, 7, dst)); } - - @Override public String op() { return "sar"; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SetX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SetX86.java index 53237538..b2902086 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SetX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SetX86.java @@ -1,33 +1,48 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.Type; -import com.seaofnodes.simple.type.TypeInteger; -import java.io.ByteArrayOutputStream; -// Compare immediate. Sets flags. // Corresponds to the x86 instruction "sete && setne". // Use result of comparison without jump. public class SetX86 extends MachConcreteNode implements MachNode { final String _bop; // One of <,<=,== + final boolean _unsigned; // Constructor expects input is an X86 and not an Ideal node. - SetX86( Node cmp, String bop ) { + SetX86( Node cmp, String bop, boolean unsigned ) { super(cmp); _inputs.setLen(1); // Pop the cmp inputs // Replace with the matched cmp _inputs.push(cmp); _bop = bop; + _unsigned = unsigned; } - + @Override public String op() { return "set"+_bop; } @Override public RegMask regmap(int i) { assert i==1; return x86_64_v2.FLAGS_MASK; } @Override public RegMask outregmap() { return x86_64_v2.WMASK; } - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); + @Override public void encoding( Encoding enc ) { + // REX + 0F 94 + short dst = enc.reg(this ); + + // Optional rex, for dst + if( dst >= 8 ) enc.add1(x86_64_v2.rex(0, dst, 0)); + enc.add1(0x0F); // opcode + enc.add1(switch (_bop) { + case "==" -> 0x94; // SETE + case "<" -> _unsigned ? 0x92 : 0x9C; // SETB /SETL + case "<=" -> _unsigned ? 0x96 : 0x9E; // SETBE/SETLE + case ">=" -> _unsigned ? 0x93 : 0x9D; // SETAE/SETGE + default -> throw Utils.TODO(); + }); + enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, 0, dst)); + + // low 8 bites are set, now zero extend for next instruction + if( dst >= 8 ) enc.add1(x86_64_v2.rex(0, dst, 0)); + enc.add1(0x0F); // opcode + enc.add1(0xB6); // opcode + enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, dst)); } @Override public void asm(CodeGen code, SB sb) { @@ -35,7 +50,4 @@ public class SetX86 extends MachConcreteNode implements MachNode { String src = code.reg(in(1)); if( src!="flags" ) sb.p(" = ").p(src); } - - @Override public String op() { return "set"+_bop; } - } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlIX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlIX86.java index c18007cd..0b75b456 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlIX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlIX86.java @@ -1,40 +1,40 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; +import com.seaofnodes.simple.node.Node; + import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.Type; -import com.seaofnodes.simple.type.TypeInteger; -import java.io.ByteArrayOutputStream; -import java.util.BitSet; -public class ShlIX86 extends MachConcreteNode implements MachNode{ - final TypeInteger _ti; - ShlIX86(Node shl, TypeInteger ti) {super(shl); _inputs.pop(); _ti = ti;} +public class ShlIX86 extends MachConcreteNode implements MachNode { + final int _imm; + ShlIX86( Node shli, int imm ) { + super(shli); + assert x86_64_v2.imm8(imm); + _inputs.pop(); // Pop constant input off + _imm = imm; + } + public int opcode() { return 0xC1; } + public int mod() { return 4; } - // Register mask allowed on input i. - // This is the normal calling convention - @Override public RegMask regmap(int i) { - //assert i==1; - return x86_64_v2.RMASK; } - // Register mask allowed as a result. 0 for no register. + @Override public RegMask regmap(int i) { return x86_64_v2.RMASK; } @Override public RegMask outregmap() { return x86_64_v2.WMASK; } - - // Output is same register as input#1 @Override public int twoAddress() { return 1; } - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); - } + @Override public String op() { return "shli"; } + @Override public String glabel() { return "<<"; } + @Override public void encoding(Encoding enc) { + short dst = enc.reg(this); // Also src1 + enc.add1(x86_64_v2.rex(0, dst, 0)); + enc.add1( opcode()); - // General form - // General form: "shli dst << #imm" + enc.add1( x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, mod(), dst) ); + + // immediate(4 bytes) 32 bits or (1 byte)8 bits + if( x86_64_v2.imm8(_imm) ) enc.add1(_imm); + else enc.add4(_imm); + } @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" << #"); - _ti.print(sb); + sb.p(code.reg(this)).p(" ").p(glabel()).p("= #").p(_imm); } - - @Override public String op() { return "shli"; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlX86.java index a3de5347..9f358575 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlX86.java @@ -1,39 +1,24 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; -import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; -import com.seaofnodes.simple.node.*; -import java.io.ByteArrayOutputStream; +import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.Node; -public class ShlX86 extends MachConcreteNode implements MachNode { - ShlX86(Node shl) { super(shl); }; - - // Register mask allowed on input i. - // This is the normal calling convention - - // CL register must be used for input(2) +public class ShlX86 extends RegX86 { + ShlX86( Node add ) { super(add); } + @Override public String op() { return "shl"; } + @Override public String glabel() { return "<<"; } @Override public RegMask regmap(int i) { - if(i == 1) return x86_64_v2.WMASK; - if(i == 2) return x86_64_v2.RCX_MASK; + if (i == 1) return x86_64_v2.WMASK; + if (i == 2) return x86_64_v2.RCX_MASK; throw Utils.TODO(); } - - @Override public RegMask outregmap() { return x86_64_v2.WMASK; } - - // Output is same register as input#1 - @Override public int twoAddress() { return 1; } - - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); - } - - // General form - // General form: "shl dst << src" - @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(" << ").p(code.reg(in(1))); + @Override int opcode() { return 0xD3; } + @Override public final void encoding( Encoding enc ) { + short dst = enc.reg(this ); // src1 + short src = enc.reg(in(2)); // src2 + enc.add1(x86_64_v2.rex(0, dst, 0)); + enc.add1(opcode()); // opcode + enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, 4, dst)); } - - @Override public String op() { return "shl"; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrIX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrIX86.java index d17ee734..893eb23d 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrIX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrIX86.java @@ -1,41 +1,35 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; +import com.seaofnodes.simple.node.Node; + import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.Type; -import com.seaofnodes.simple.type.TypeInteger; -import java.io.ByteArrayOutputStream; -import java.util.BitSet; -import java.lang.StringBuilder; -public class ShrIX86 extends MachConcreteNode implements MachNode{ - final TypeInteger _ti; - ShrIX86(Node shr, TypeInteger ti) {super(shr); _inputs.pop(); _ti = ti;} +public class ShrIX86 extends MachConcreteNode implements MachNode { + final int _imm; + ShrIX86( Node shri, int imm ) { super(shri); assert x86_64_v2.imm8(imm); _inputs.pop(); _imm = imm; } + @Override public String op() { return "shri"; } + @Override public String glabel() { return ">>>"; } + int opcode() { return 0xC1; } + int mod() { return 5; } - // Register mask allowed on input i. - // This is the normal calling convention - @Override public RegMask regmap(int i) { - //assert i==1; - return x86_64_v2.RMASK; } - // Register mask allowed as a result. 0 for no register. + @Override public RegMask regmap(int i) { return x86_64_v2.RMASK; } @Override public RegMask outregmap() { return x86_64_v2.WMASK; } - - // Output is same register as input#1 @Override public int twoAddress() { return 1; } - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); - } + @Override public void encoding(Encoding enc) { + short dst = enc.reg(this); // Also src1 + enc.add1(x86_64_v2.rex(0, dst, 0)); + enc.add1( opcode()); - // General form - // General form: "shri dst >>> #imm" + enc.add1( x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, mod(), dst) ); + + // immediate(4 bytes) 32 bits or (1 byte)8 bits + if( x86_64_v2.imm8(_imm) ) enc.add1(_imm); + else enc.add4(_imm); + } @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" >>> #"); - _ti.print(sb); + sb.p(code.reg(this)).p(" ").p(glabel()).p("= #").p(_imm); } - - @Override public String op() { return "shri"; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrX86.java index 757d9b8e..d869260f 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrX86.java @@ -1,40 +1,24 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; -import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; -import com.seaofnodes.simple.node.*; -import java.io.ByteArrayOutputStream; +import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.Node; -public class ShrX86 extends MachConcreteNode implements MachNode { - ShrX86(Node shr) { - super(shr); - }; - // Register mask allowed on input i. - // This is the normal calling convention - - // CL register must be used for input(2) +public class ShrX86 extends RegX86 { + ShrX86( Node add ) { super(add); } + @Override public String op() { return "shr"; } + @Override public String glabel() { return ">>>"; } @Override public RegMask regmap(int i) { - if(i == 1) return x86_64_v2.WMASK; - if(i == 2) return x86_64_v2.RCX_MASK; - throw Utils.TODO(); - } - - @Override public RegMask outregmap() { return x86_64_v2.WMASK; } - - // Output is same register as input#1 - @Override public int twoAddress() { return 1; } - - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { + if (i == 1) return x86_64_v2.WMASK; + if (i == 2) return x86_64_v2.RCX_MASK; throw Utils.TODO(); } - - // General form - // General form: "shr dst >>> src" - @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(" >>> ").p(code.reg(in(1))); + @Override int opcode() { return 0xD3; } + @Override public final void encoding( Encoding enc ) { + short dst = enc.reg(this ); // src1 + short src = enc.reg(in(2)); // src2 + enc.add1(x86_64_v2.rex(0, dst, 0)); + enc.add1(opcode()); // opcode + enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, 5, dst)); } - - @Override public String op() { return "shr"; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitX86.java index ea283a87..960ea6f8 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitX86.java @@ -1,21 +1,107 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; +import com.seaofnodes.simple.SB; import com.seaofnodes.simple.Utils; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.FunNode; import com.seaofnodes.simple.node.Node; import com.seaofnodes.simple.node.SplitNode; -import java.io.ByteArrayOutputStream; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeFloat; +import com.seaofnodes.simple.type.TypeInteger; public class SplitX86 extends SplitNode { SplitX86( String kind, byte round ) { super(kind,round, new Node[2]); } - - // Register mask allowed on input i. + @Override public String op() { return "mov"; } @Override public RegMask regmap(int i) { return x86_64_v2.SPLIT_MASK; } - // Register mask allowed as a result. 0 for no register. @Override public RegMask outregmap() { return x86_64_v2.SPLIT_MASK; } - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); + // Need to handle 8 cases: . reg->reg, reg->xmm, reg->flags, xmm->reg, xmm->xmm, xmm->flags, flags->reg, flags->xmm, + // flags->flags. + @Override public void encoding( Encoding enc ) { + // REX.W + 8B /r MOV r64, r/m64 + short dst = enc.reg(this ); + short src = enc.reg(in(1)); + if( dst == x86_64_v2.FLAGS ) { + // mov reg, flags + // push rcx + // popf (Pop the top of the stack into the FLAGS register) + // 50+rd PUSH r64 + x86_64_v2.rexF(0,src,0,false,enc); + enc.add1(0x50 + src); + // popf + enc.add1(0x9D); + return; + } + if( src == x86_64_v2.FLAGS ) { + // mov flags, reg + // pushf; pop reg + enc.add1(0x9C); + // 58+ rd POP r64 + x86_64_v2.rexF(0,dst,0,false, enc); + enc.add1(0x58 + dst); + return; + } + + // Flag for being either XMM or stack + boolean dstX = dst >= x86_64_v2.XMM_OFFSET; + boolean srcX = src >= x86_64_v2.XMM_OFFSET; + + // Stack spills + if( dst >= x86_64_v2.MAX_REG ) { + int off = enc._fun.computeStackOffset(enc._code,dst); + if( src >= x86_64_v2.MAX_REG ) { + // Rare stack-stack move. push [RSP+soff]; pop [RSP+doff] + int soff = enc._fun.computeStackOffset(enc._code,src); + enc.add1(0xFF); + x86_64_v2.indirectAdr(0, (short)-1/*index*/, (short)x86_64_v2.RSP, soff, 6, enc); + enc.add1(0x8F); + x86_64_v2.indirectAdr(0, (short)-1/*index*/, (short)x86_64_v2.RSP, off, 0, enc); + return; + } + StoreX86.encVal(enc, srcX ? TypeFloat.F64 : TypeInteger.BOT, (short)x86_64_v2.RSP, (short)-1/*index*/, src, off, 0); + return; + } + if( src >= x86_64_v2.MAX_REG ) { + int off = enc._fun.computeStackOffset(enc._code,src); + LoadX86.enc(enc, dstX ? TypeFloat.F64 : TypeInteger.BOT, dst, (short)x86_64_v2.RSP, (short)-1, off, 0); + return; + } + + // reg-reg move. Adjust numbering for GPR vs FPR reg set + if( dstX ) dst -= (short) x86_64_v2.XMM_OFFSET; + if( srcX ) src -= (short) x86_64_v2.XMM_OFFSET; + + // 0x66 if moving between register classes + if( dstX ^ srcX ) enc.add1(0x66); + if( !dstX && srcX ) + { short tmp=src; src=dst; dst=tmp; } + enc.add1(x86_64_v2.rex(dst, src, 0)); + + // pick opcode based on regs + if( !dstX && !srcX ) { + // reg->reg (MOV r64, r/m64) + enc.add1(0x8B); + } else if( dstX && srcX ) { + // xmm->xmm (NP 0F 28 /r MOVAPS xmm1, xmm2/m128) + enc.add1(0x0F); + enc.add1(0x28); + } else if( dstX && !srcX ) { + // reg->xmm (66 REX.W 0F 6E /r MOVQ xmm, r/m64) + enc.add1(0x0F); + enc.add1(0x6E); + } else if( !dstX && srcX ) { + // xmm->reg(66 0F 7E /r MOVQ r/m64, xmm) + enc.add1(0x0F); + enc.add1(0x7E); + } + + enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, src)); + } + + // General form: "mov dst = src" + @Override public void asm(CodeGen code, SB sb) { + FunNode fun = code._encoding==null ? null : code._encoding._fun; + sb.p(code.reg(this,fun)).p(" = ").p(code.reg(in(1),fun)); } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/StoreX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/StoreX86.java index 024ef904..76fb4e8d 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/StoreX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/StoreX86.java @@ -1,19 +1,19 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; import com.seaofnodes.simple.SB; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.ConstantNode; import com.seaofnodes.simple.node.Node; import com.seaofnodes.simple.node.StoreNode; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeFloat; import java.util.BitSet; public class StoreX86 extends MemOpX86 { StoreX86( StoreNode st, Node base, Node idx, int off, int scale, int imm, Node val ) { super(st,st, base, idx, off, scale, imm, val); } - @Override public String op() { return "st"+_sz; } - @Override public StringBuilder _printMach(StringBuilder sb, BitSet visited) { Node val = val(); sb.append(".").append(_name).append("="); @@ -21,9 +21,60 @@ public class StoreX86 extends MemOpX86 { else val._print0(sb,visited); return sb.append(";"); } - // Register mask allowed as a result. 0 for no register. @Override public RegMask outregmap() { return null; } + @Override public void encoding( Encoding enc ) { + // REX.W + C7 /0 id MOV r/m64, imm32 | + // REX.W + 89 /r MOV r/m64, r64 + short ptr = enc.reg(ptr()); + short idx = enc.reg(idx()); + short src = enc.reg(val()); + + if( src == -1 ) { + int log = _declaredType.log_size(); + x86_64_v2.rexF(-1, ptr, idx, log==3, enc); + switch( log ) { + case 0: enc .add1(0xC6); break; + case 1: enc.add1(0x66).add1(0xC7); break; + case 2: enc .add1(0xC7); break; + case 3: enc .add1(0xC7); break; + } + x86_64_v2.indirectAdr(_scale, idx, ptr, _off, src, enc); + switch( log ) { + case 0: enc.add1(_imm); break; + case 1: enc.add2(_imm); break; + case 2: enc.add4(_imm); break; + case 3: enc.add4(_imm); break; // Limit of a 4 byte immediate + } + } else { + encVal(enc,_declaredType,ptr,idx,src,_off,_scale); + } + } + + // Non-immediate encoding + static void encVal( Encoding enc, Type decl, short ptr, short idx, short src, int off, int scale ) { + int log = decl.log_size(); + // Float reg being stored + if( src >= x86_64_v2.XMM_OFFSET ) { + src -= (short)x86_64_v2.XMM_OFFSET; + assert log == 2 || log == 3; + enc.add1( log==2 ? 0xF3 : 0xF2 ); + x86_64_v2.rexF(src,ptr,idx,false,enc); + enc.add1(0x0F).add1(0x11); + } else { + // byte stores from sil, dil, bpl, spl need a rex + if( log == 0 && src >= x86_64_v2.RSP ) enc.add1(x86_64_v2.rex(src,ptr,idx,false)); + else x86_64_v2.rexF(src,ptr,idx,log==3,enc); + switch( log ) { + case 0: enc .add1(0x88); break; + case 1: enc.add1(0x66).add1(0x89); break; + case 2: enc .add1(0x89); break; + case 3: enc .add1(0x89); break; + } + } + x86_64_v2.indirectAdr(scale, idx, ptr, off, src, enc); + } + // General form: "stN [base + idx<<2 + 12],val" @Override public void asm(CodeGen code, SB sb) { diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubFX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubFX86.java index ceaf0636..0a169a22 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubFX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubFX86.java @@ -1,31 +1,31 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; -import java.io.ByteArrayOutputStream; public class SubFX86 extends MachConcreteNode implements MachNode { SubFX86( Node subf) { super(subf); } - - - // Register mask allowed on input i. + @Override public String op() { return "subf"; } @Override public RegMask regmap(int i) { assert i==1 || i==2; return x86_64_v2.XMASK; } - // Register mask allowed as a result. 0 for no register. @Override public RegMask outregmap() { return x86_64_v2.XMASK; } - // Output is same register as input#1 @Override public int twoAddress() { return 1; } - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); + @Override public void encoding( Encoding enc ) { + // F2 0F 5C /r SUBSD xmm1, xmm2/m64 + short dst = (short)(enc.reg(this ) - x86_64_v2.XMM_OFFSET); + short src = (short)(enc.reg(in(2)) - x86_64_v2.XMM_OFFSET); + // Fopcode + enc.add1(0xF2); + // rex prefix must come next (REX.W is not set) + x86_64_v2.rexF(dst, src, 0, false, enc); + + enc.add1(0x0F).add1(0x5C); + enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, src)); } // General form: "subf dst -= src" @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" - ").p(code.reg(in(2))); + sb.p(code.reg(this)).p(" -= ").p(code.reg(in(2))); } - - @Override public String op() { return "subf"; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubX86.java index da44a838..8da34bf5 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubX86.java @@ -1,31 +1,10 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; -import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.TypeInteger; -import java.io.ByteArrayOutputStream; - -public class SubX86 extends MachConcreteNode implements MachNode { - SubX86( Node sub ) { super(sub); } - - // Register mask allowed on input i. - @Override public RegMask regmap(int i) { assert i==1 || i==2; return x86_64_v2.RMASK; } - // Register mask allowed as a result. 0 for no register. - @Override public RegMask outregmap() { return x86_64_v2.WMASK; } - // Output is same register as input#1 - @Override public int twoAddress() { return 1; } - - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); - } - - // General form: "sub dst -= src" - @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" - ").p(code.reg(in(2))); - } +import com.seaofnodes.simple.node.Node; +public class SubX86 extends RegX86 { + SubX86( Node add ) { super(add); } @Override public String op() { return "sub"; } + @Override public String glabel() { return "-"; } + @Override int opcode() { return 0x2B; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TFPX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TFPX86.java index 2b4de2ca..be4d1a83 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TFPX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TFPX86.java @@ -1,29 +1,55 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; -import com.seaofnodes.simple.node.ConstantNode; -import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.node.cpus.riscv.riscv; import com.seaofnodes.simple.type.Type; import com.seaofnodes.simple.type.TypeFunPtr; -import java.io.ByteArrayOutputStream; - // Function constants -public class TFPX86 extends ConstantNode implements MachNode { +public class TFPX86 extends ConstantNode implements MachNode, RIPRelSize { TFPX86( ConstantNode con ) { super(con); } - @Override public String op() { return _con == Type.NIL ? "xor" : "ldx"; } - // Register mask allowed on input i. 0 for no register. + @Override public String op() { + return _con == Type.NIL ? "xor" : "ldx"; + } + @Override public boolean isClone() { return true; } + @Override public Node copy() { return new TFPX86(this); } @Override public RegMask regmap(int i) { return null; } - // General int registers @Override public RegMask outregmap() { return x86_64_v2.WMASK; } - @Override public boolean isClone() { return true; } - @Override public TFPX86 copy() { return new TFPX86(this); } + // Zero-set uses XOR kills flags + @Override public RegMask killmap() { + return _con == Type.NIL ? x86_64_v2.FLAGS_MASK : null; + } + + @Override public void encoding( Encoding enc ) { + short dst = enc.reg(this); + // Short form for zero + if( _con==Type.NIL ) { + // XOR dst,dst. Can skip REX is dst is low 8, makes this a 32b + // xor, which will also zero the high bits. + if( dst >= 8 ) enc.add1(x86_64_v2.rex(dst, dst, 0)); + enc.add1(0x33); // opcode + enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, dst)); + return; + } + + // lea to load function pointer address + // lea rax, [rip+disp32] + enc.relo(this); + enc.add1(x86_64_v2.rex(dst, 0, 0)); + enc.add1(0x8D); // opcode + enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.INDIRECT, dst, 0b101)); + enc.add4(0); + } + + @Override public byte encSize(int delta) { + return 7; + } - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); + // Delta is from opcode start + @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { + enc.patch4(opStart + 3, delta - 7); } // Human-readable form appended to the SB. Things like the encoding, @@ -35,6 +61,6 @@ public class TFPX86 extends ConstantNode implements MachNode { if( _con == Type.NIL ) sb.p(reg).p(",").p(reg); else - _con.print(sb.p(reg).p(" #")); + _con.print(sb.p(reg).p(" = #")); } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/UJmpX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/UJmpX86.java new file mode 100644 index 00000000..ec4eeb3b --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/UJmpX86.java @@ -0,0 +1,53 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.Type; +import java.util.BitSet; + +// unconditional jump +public class UJmpX86 extends CFGNode implements MachNode, RIPRelSize { + UJmpX86() { } + @Override public String op() { return "jmp"; } + @Override public String label() { return op(); } + @Override public StringBuilder _print1( StringBuilder sb, BitSet visited ) { + return sb.append("jmp "); + } + @Override public RegMask regmap(int i) {return null; } + @Override public RegMask outregmap() { return null; } + @Override public Type compute() { throw Utils.TODO(); } + @Override public Node idealize() { throw Utils.TODO(); } + @Override public void encoding( Encoding enc ) { + enc.jump(this,uctrl()); + // Short-form jump + enc.add1(0xEB).add1(0); + //// E9 cd JMP rel32 + //enc.add1(0xE9).add4(0); + } + + // Delta is from opcode start, but X86 measures from the end of the 2-byte encoding + @Override public byte encSize(int delta) { return (byte)(x86_64_v2.imm8(delta-2) ? 2 : 5); } + + // Delta is from opcode start + @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { + byte[] bits = enc.bits(); + if( opLen==2 ) { + assert bits[opStart] == (byte)0xEB; + delta -= 2; // Offset from opcode END + assert (byte)delta==delta; + bits[opStart+1] = (byte)delta; + } else { + assert bits[opStart] == (byte)0xEB; + bits[opStart] = (byte)0xE9; // Long form + delta -= 5; // Offset from opcode END + enc.patch4(opStart+1,delta); + } + } + + @Override public void asm(CodeGen code, SB sb) { + CFGNode target = uctrl(); + //assert target.nOuts() > 1; // Should optimize jmp to empty targets + sb.p(label(target)); + } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/XorIX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/XorIX86.java index 47c6be73..2d989a50 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/XorIX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/XorIX86.java @@ -1,41 +1,11 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; -import com.seaofnodes.simple.SB; -import com.seaofnodes.simple.Utils; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.RegMask; -import com.seaofnodes.simple.node.MachConcreteNode; -import com.seaofnodes.simple.node.MachNode; import com.seaofnodes.simple.node.Node; -import com.seaofnodes.simple.type.TypeInteger; -import java.io.ByteArrayOutputStream; - -public class XorIX86 extends MachConcreteNode implements MachNode { - - final TypeInteger _ti; - XorIX86(Node xor, TypeInteger ti) {super(xor); _inputs.pop(); _ti = ti;} - - // Register mask allowed on input i. - // This is the normal calling convention - @Override public RegMask regmap(int i) { assert i==1; return x86_64_v2.WMASK; } - // Register mask allowed as a result. 0 for no register. - @Override public RegMask outregmap() { return x86_64_v2.WMASK; } - - - // Output is same register as input#1 - @Override public int twoAddress() { return 1; } - - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); - } - - // General form - // General form: "ori xori ^ #imm" - @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" ^ #"); - _ti.print(sb); - } +public class XorIX86 extends ImmX86 { + XorIX86( Node add, int imm ) { super(add,imm); } @Override public String op() { return "xori"; } + @Override public String glabel() { return "^"; } + @Override int opcode() { return 0x81; } + @Override int mod() { return 6; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/XorX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/XorX86.java index 3322d951..86703903 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/XorX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/XorX86.java @@ -1,36 +1,11 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; -import com.seaofnodes.simple.SB; -import com.seaofnodes.simple.Utils; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.LRG; -import com.seaofnodes.simple.codegen.RegMask; -import com.seaofnodes.simple.node.MachConcreteNode; -import com.seaofnodes.simple.node.MachNode; import com.seaofnodes.simple.node.Node; -import com.seaofnodes.simple.type.TypeInteger; -import java.io.ByteArrayOutputStream; - -public class XorX86 extends MachConcreteNode implements MachNode{ - XorX86(Node xor) {super(xor);} - - @Override public RegMask regmap(int i) { return x86_64_v2.WMASK; } - @Override public RegMask outregmap() { return x86_64_v2.WMASK; } - - // Output is same register as input#1 - @Override public int twoAddress() { return 1; } - - // Encoding is appended into the byte array; size is returned - @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); - } - - // General form - // General form: "xor ^ reg - @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" ^ ").p(code.reg(in(2))); - } +public class XorX86 extends RegX86 { + XorX86( Node add ) { super(add); } @Override public String op() { return "xor"; } - + @Override public String glabel() { return "^"; } + @Override int opcode() { return 0x33; } + @Override public boolean commutes() { return true; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java index aacd2352..2b7fddb1 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java @@ -1,26 +1,37 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; import com.seaofnodes.simple.Utils; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.LRG; -import com.seaofnodes.simple.codegen.Machine; -import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; import com.seaofnodes.simple.type.*; public class x86_64_v2 extends Machine { + public x86_64_v2( CodeGen code ) {} // X86-64 V2. Includes e.g. SSE4.2 and POPCNT. @Override public String name() { return "x86_64_v2"; } + @Override public int defaultOpSize() { return 5; } - static final int RAX = 0, RCX = 1, RDX = 2, RBX = 3, RSP = 4, RBP = 5, RSI = 6, RDI = 7; - static final int R08 = 8, R09 = 9, R10 = 10, R11 = 11, R12 = 12, R13 = 13, R14 = 14, R15 = 15; + public static final int RAX = 0, RCX = 1, RDX = 2, RBX = 3, RSP = 4, RBP = 5, RSI = 6, RDI = 7; + public static final int R08 = 8, R09 = 9, R10 = 10, R11 = 11, R12 = 12, R13 = 13, R14 = 14, R15 = 15; - static final int XMM0 = 16, XMM1 = 17, XMM2 = 18, XMM3 = 19, XMM4 = 20, XMM5 = 21, XMM6 = 22, XMM7 = 23; - static final int XMM8 = 24, XMM9 = 25, XMM10 = 26, XMM11 = 27, XMM12 = 28, XMM13 = 29, XMM14 = 30, XMM15 = 31; - static final int FLAGS = 32; - static final int MAX_REG = 33; - static final int RPC = 33; + public static final int XMM0 = 16, XMM1 = 17, XMM2 = 18, XMM3 = 19, XMM4 = 20, XMM5 = 21, XMM6 = 22, XMM7 = 23; + public static final int XMM8 = 24, XMM9 = 25, XMM10 = 26, XMM11 = 27, XMM12 = 28, XMM13 = 29, XMM14 = 30, XMM15 = 31; + public static final int FLAGS = 32; + public static final int MAX_REG = 33; + public static final int RPC = 33; + // Human-readable name for a register number, e.g. "RAX". + public static final String[] REGS = new String[]{ + "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi", + "r8" , "r9" , "r10", "r11", "r12", "r13", "r14", "r15", + "xmm0", "xmm1", "xmm2" , "xmm3" , "xmm4" , "xmm5" , "xmm6" , "xmm7" , + "xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15", + "flags", + }; + // Hard crash for bad register number, fix yer bugs! + @Override public String[] regs() { return REGS; } + + public static int XMM_OFFSET = 16; // General purpose register mask: pointers and ints, not floats static final long RD_BITS = 0b1111111111111111L; // All the GPRs static RegMask RMASK = new RegMask(RD_BITS); @@ -29,49 +40,229 @@ public class x86_64_v2 extends Machine { static RegMask WMASK = new RegMask(WR_BITS); // Xmm register mask static final long FP_BITS = 0b1111111111111111L << XMM0; // All the XMMs - static final RegMask XMASK = new RegMask(FP_BITS); - static final RegMask FLAGS_MASK = new RegMask(FLAGS); - static final RegMask RPC_MASK = new RegMask(RPC); + static RegMask XMASK = new RegMask(FP_BITS); + static RegMask FLAGS_MASK = new RegMask(FLAGS); + static RegMask RPC_MASK = new RegMask(RPC); static final long SPILLS = -(1L << MAX_REG); - static final RegMask SPLIT_MASK = new RegMask(WR_BITS | FP_BITS | (1L<<FLAGS) | SPILLS, -1L ); + static final RegMask SPLIT_MASK = new RegMask(WR_BITS | FP_BITS /* | (1L<<FLAGS)*/ | SPILLS, -1L ); // Load/store mask; both GPR and FPR static RegMask MEM_MASK = new RegMask(WR_BITS | FP_BITS); - static final RegMask RAX_MASK = new RegMask(RAX); - static final RegMask RCX_MASK = new RegMask(RCX); - static final RegMask RDX_MASK = new RegMask(RDX); - static final RegMask RDI_MASK = new RegMask(RDI); - static final RegMask R08_MASK = new RegMask(R08); - static final RegMask R09_MASK = new RegMask(R09); - static final RegMask RSI_MASK = new RegMask(RSI); + public static RegMask RAX_MASK = new RegMask(RAX); + public static RegMask RCX_MASK = new RegMask(RCX); + public static RegMask RDX_MASK = new RegMask(RDX); + public static RegMask RDI_MASK = new RegMask(RDI); + public static RegMask R08_MASK = new RegMask(R08); + public static RegMask R09_MASK = new RegMask(R09); + public static RegMask RSI_MASK = new RegMask(RSI); + + public static RegMask XMM0_MASK = new RegMask(XMM0); + + // Encoding + public static int REX = 0x40; + public static int REX_W = 0x48; + public static int REX_WR = 0x4C; + public static int REX_WRB= 0x4D; + public static int REX_WB = 0x49; + + public enum MOD { + INDIRECT, // [mem] + INDIRECT_disp8, // [mem + 0x12] + INDIRECT_disp32, // [mem + 0x12345678] + DIRECT, // mem + }; - static final RegMask XMM0_MASK = new RegMask(XMM0); + // opcode included here + // 0F 84 cd JE rel32 + // 0F 85 cd JNE rel32 + // 0F 8F cd JG rel32 + // 0F 8C cd JL rel32 + // 0F 8E cd JLE rel32 + // 0F 8D cd JGE rel32 + static public int jumpop(String op) { + return switch (op) { + case "==" -> 0x84; + case "!=" -> 0x85; + case ">" -> 0x8F; + case "<" -> 0x8C; + case "<=" -> 0x8E; + case ">=" -> 0x8D; + default -> throw new IllegalArgumentException("Too many arguments"); + }; + } - // Human-readable name for a register number, e.g. "RAX". - // Hard crash for bad register number, fix yer bugs! - static final String[] REGS = new String[] { - "rax" , "rcx" , "rdx" , "rbx" , "rsp" , "rbp" , "rsi" , "rdi" , - "r8" , "r9" , "r10" , "r11" , "r12" , "r13" , "r14" , "r15" , - "xmm0", "xmm1", "xmm2" , "xmm3" , "xmm4" , "xmm5" , "xmm6" , "xmm7" , - "xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15", - "flags","[rsp-4]", + public static int modrm(MOD mod, int reg, int m_r) { + if( reg == -1 ) reg=0; // Missing reg in this flavor + // combine all the bits + assert 0 <= reg && reg < 16; + assert 0 <= m_r && m_r < 16; + return (mod.ordinal() << 6) | ((reg & 0x07) << 3) | m_r & 0x07; + } + + // 00 000 000 + // same bit-layout as modrm + public static int sib(int scale, int index, int base) { + assert 0 <= base && base < 16; + assert 0 <= index && index < 16; + return (scale << 6) | ((index & 0x07) << 3) | base & 0x07; + } + + // reg1 is reg(R) + // reg2 is r/mem(B) + // reg3 is X(index) + // reg4 is X(base) + + // 0 denotes no direct register + public static int rex(int reg, int ptr, int idx, boolean wide) { + // assuming 64 bit by default so: 0100 1000 + assert -1 <= reg && reg < 16; + assert -1 <= ptr && ptr < 16; + assert -1 <= idx && idx < 16; + + int rex = wide ? REX_W : REX; + if( 8 <= reg ) rex |= 0b00000100; // REX.R + if( 8 <= ptr ) rex |= 0b00000001; // REX.B + if( 8 <= idx ) rex |= 0b00000010; // REX.X + return rex; + } + + public static int rex(int reg, int ptr, int idx) { + return rex(reg, ptr, idx, true); + } + + // rex for floats. Return size (0 or 1) + // don't need to use REX if 0x40. + public static byte rexF(int reg, int ptr, int idx, boolean wide, Encoding enc) { + int rex = rex(reg, ptr, idx, wide); + if (rex == REX) return 0; + enc.add1(rex); + return 1; + } + // Function used for encoding indirect memory addresses + // Does not always generate SIB byte e.g index == -1. + // -1 denotes empty value, not set - note 0 is different from -1 as it can represent rax. + // Looks for best mod locally + public static void indirectAdr( int scale, short index, short base, int offset, int reg, Encoding enc ) { + // Assume indirect + assert 0 <= base && base < 16; + assert -1 <= index && index < 16 && index != RSP; + + MOD mod = MOD.INDIRECT; + // is 1 byte enough or need more? + if( offset != 0 ) + mod = imm8(offset) + ? MOD.INDIRECT_disp8 + : MOD.INDIRECT_disp32; + + if( mod == MOD.INDIRECT && (base == RBP || base == R13) ) { + mod = MOD.INDIRECT_disp8; + } else if( index == -1 && (base == RSP || base == R12) ) { + index = RSP; + } + + if( index == -1 ) { + enc.add1(modrm(mod, reg, base)); + } else { + enc.add1(modrm(mod, reg, x86_64_v2.RSP)); + enc.add1(sib(scale, index, base)); + } + + if( mod == MOD.INDIRECT_disp8 ) { + enc.add1(offset); + } else if( mod == MOD.INDIRECT_disp32 ) { + enc.add4(offset); + } + } + + + // Limit of float args passed in registers + static RegMask[] XMMS8 = new RegMask[]{ + new RegMask(XMM0), new RegMask(XMM1), new RegMask(XMM2), new RegMask(XMM3), + new RegMask(XMM4), new RegMask(XMM5), new RegMask(XMM6), new RegMask(XMM7), }; - @Override public String reg( int reg ) { - return reg < REGS.length ? REGS[reg] : "[rsp+"+(reg-REGS.length)*4+"]"; + + // Map from function signature and argument index to register. + // Used to set input registers to CallNodes, and ParmNode outputs. + @Override public RegMask callArgMask( TypeFunPtr tfp, int idx, int maxArgSlot ) { return callInMask(tfp,idx,maxArgSlot); } + static RegMask callInMask( TypeFunPtr tfp, int idx, int maxArgSlot ) { + if( idx==0 ) return RPC_MASK; + if( idx==1 ) return null; + return switch( CodeGen.CODE._callingConv ) { + case "SystemV" -> callSys5 (tfp,idx,maxArgSlot); + case "Win64" -> callWin64(tfp,idx,maxArgSlot); + default -> throw Utils.TODO(); + }; + } + + // Maximum stack args used by this signature + @Override public short maxArgSlot( TypeFunPtr tfp ) { + return switch( CodeGen.CODE._callingConv ) { + case "SystemV" -> maxArgSlotSys5 (tfp); + case "Win64" -> maxArgSlotWin64(tfp); + default -> throw Utils.TODO(); + }; } - // WIN64(param passing) - static RegMask[] CALLINMASK_WIN64 = new RegMask[] { + // Win64 - max *4* args in registers of all kinds; after that on stack. + // Stack args land in increasing memory order with empty/mirror slots for + // every register. + // + // foo( int i0, flt f1, int i2, flt f3, int i4, flt f5, int i6, ... ) + // + // -- Prior Frame -- + // i6 + // f5 + // i4 + // empty mirror, XMM3= f3 + // empty mirror, R08 = i2 + // empty mirror, XMM1= f1 + // empty mirror, RCX = i0 + // -- Caller Frame; 16b align -- + // RPC + // PAD/ALIGN + // -- Callee Frame; 16b align -- + static RegMask[] WIN64_CALL = new RegMask[] { RCX_MASK, RDX_MASK, R08_MASK, R09_MASK, }; + static RegMask callWin64(TypeFunPtr tfp, int idx, int maxArgSlot ) { + // idx 2,3,4,5 passed in registers, with stack slot mirrors. + // idx >= 6 passed on stack, starting at slot#1 (#0 reserved for RPC). + if( idx >= 6 ) + return new RegMask(MAX_REG+maxArgSlot+(idx-2)); + return tfp.arg(idx-2) instanceof TypeFloat + ? XMMS8 [idx-2] + : WIN64_CALL[idx-2]; + } + static short maxArgSlotWin64(TypeFunPtr tfp) { + return (short)tfp.nargs(); + } + + // Sys5: max 6 GPRs and 8 FPRS filled first. Extra args land in increasing + // memory order as needed - no mirror space. + // + // foo( int i0, flt f1, int i2, flt f3, int i4, flt f5, int i6, ... ) + // RDI =i0, RSI =i2, RDX =i4, RCX =i6, R08 =i8, R09 =i10 + // XMM0=f1, XMM1=f3, XMM2=f5, XMM3=f7, XMM4=f9, XMM5=f11, XMM6=f13, XMM7=f15 + // + // -- Prior Frame -- + // f18 + // i18 + // f17 + // i16 + // i14 + // i12 + // -- Caller Frame; 16b align -- + // RPC + // PAD/ALIGN + // -- Callee Frame; 16b align -- // SystemV(param passing) - static RegMask[] CALLINMASK_SYSTEMV = new RegMask[] { + static RegMask[] SYS5_CALL = new RegMask[] { RDI_MASK, RSI_MASK, RDX_MASK, @@ -80,285 +271,243 @@ public class x86_64_v2 extends Machine { R09_MASK, }; - // Limit of float args passed in registers - static RegMask[] XMMS4 = new RegMask[]{ - new RegMask(XMM0), new RegMask(XMM1), new RegMask(XMM2), new RegMask(XMM3), - }; - - // Map from function signature and argument index to register. - // Used to set input registers to CallNodes, and ParmNode outputs. - static RegMask callInMask( TypeFunPtr tfp, int idx ) { - if( idx==0 ) return RPC_MASK; - if( idx==1 ) return null; - // Count floats in signature up to index - int fcnt=0; - for( int i=2; i<idx; i++ ) - if( tfp.arg(i-2) instanceof TypeFloat ) - fcnt++; - // Floats up to XMMS in XMM registers - if( tfp.arg(idx-2) instanceof TypeFloat ) { - if( fcnt < XMMS4.length ) - return XMMS4[fcnt]; - } else { - RegMask[] cargs = switch( CodeGen.CODE._callingConv ) { - case "SystemV" -> CALLINMASK_SYSTEMV; - case "Win64" -> CALLINMASK_WIN64; - default -> throw new IllegalArgumentException("Unknown calling convention: "+CodeGen.CODE._callingConv); - }; - if( idx-2-fcnt < cargs.length ) - return cargs[idx-2-fcnt]; + static RegMask callSys5(TypeFunPtr tfp, int idx, int maxArgSlot ) { + // First 6 integers passed in registers: rdi,rsi,rdx,rcx,r08,r09 + // First 8 floats passed in registers: xmm0-xmm7 + int icnt=0, fcnt=0; // Count of ints, floats + for( int i=2; i<idx; i++ ) { + if( tfp.arg(i-2) instanceof TypeFloat ) fcnt++; + else icnt++; } - throw Utils.TODO(); // Pass on stack slot + int nstk = Math.max(icnt-6,0)+Math.max(fcnt-8,0); + return tfp.arg(idx-2) instanceof TypeFloat + ? fcnt<8 ? XMMS8 [fcnt] : new RegMask(MAX_REG+maxArgSlot+nstk) + : icnt<6 ? SYS5_CALL[icnt] : new RegMask(MAX_REG+maxArgSlot+nstk); + } + static short maxArgSlotSys5(TypeFunPtr tfp) { + int icnt=0, fcnt=0; // Count of ints, floats + for( int i=0; i<tfp.nargs(); i++ ) { + if( tfp.arg(i) instanceof TypeFloat ) fcnt++; + else icnt++; + } + int nstk = Math.max(icnt-6,0)+Math.max(fcnt-8,0); + return (short)nstk; } - // caller saved(systemv) static final long SYSTEM5_CALLER_SAVE = - (1L<< RAX) | (1L<< RCX) | (1L<< RDX) | - (1L<< RDI) | (1L<< RSI) | - (1L<< R08) | (1L<< R09) | (1L<< R10) | (1L<< R11) | - (1L<<FLAGS)| // Flags are killed + (1L << RAX) | (1L << RCX) | (1L << RDX) | + (1L << RDI) | (1L << RSI) | + (1L << R08) | (1L << R09) | (1L << R10) | (1L << R11) | + (1L << FLAGS) | // Flags are killed // All FP regs are killed FP_BITS; - static final RegMask SYSTEM5_CALLER_SAVE_MASK = new RegMask(SYSTEM5_CALLER_SAVE); - // caller saved(win64) static final long WIN64_CALLER_SAVE = (1L<< RAX) | (1L<< RCX) | (1L<< RDX) | (1L<< R08) | (1L<< R09) | (1L<< R10) | (1L<< R11) | (1L<<FLAGS)| // Flags are killed // Only XMM0-XMM5 are killed; XMM6-XMM15 are preserved (1L<<XMM0) | (1L<<XMM1) | (1L<<XMM2) | (1L<<XMM3) | - (1L<<XMM4) | (1L<<XMM5); - static final RegMask WIN64_CALLER_SAVE_MASK = new RegMask(WIN64_CALLER_SAVE); + (1L<<XMM4) | (1L<<XMM5) ; - static RegMask x86CallerSave() { - return switch( CodeGen.CODE._callingConv ) { - case "SystemV" -> SYSTEM5_CALLER_SAVE_MASK; - case "Win64" -> WIN64_CALLER_SAVE_MASK; - default -> throw new IllegalArgumentException("Unknown calling convention: "+CodeGen.CODE._callingConv); - }; - } - @Override public RegMask callerSave() { return x86CallerSave(); } - - static final RegMask SYSTEM5_CALLEE_SAVE_MASK; - static final RegMask WIN64_CALLEE_SAVE_MASK; - static { - long callee = ~SYSTEM5_CALLER_SAVE; - // Remove the spills - callee &= (1L<<MAX_REG)-1; - callee &= ~(1L<<FLAGS); - callee &= ~(1L<<RSP); - SYSTEM5_CALLEE_SAVE_MASK = new RegMask(callee); - - callee = ~WIN64_CALLER_SAVE; - // Remove the spills - callee &= (1L<<MAX_REG)-1; - callee &= ~(1L<<FLAGS); - callee &= ~(1L<<RSP); - WIN64_CALLEE_SAVE_MASK = new RegMask(callee); - } - static RegMask x86CalleeSave() { - return switch( CodeGen.CODE._callingConv ) { - case "SystemV" -> SYSTEM5_CALLEE_SAVE_MASK; - case "Win64" -> WIN64_CALLEE_SAVE_MASK; - default -> throw new IllegalArgumentException("Unknown calling convention: "+CodeGen.CODE._callingConv); + @Override public long callerSave() { + return switch (CodeGen.CODE._callingConv) { + case "SystemV" -> SYSTEM5_CALLER_SAVE; + case "Win64" -> WIN64_CALLER_SAVE; + default -> throw new IllegalArgumentException("Unknown calling convention: " + CodeGen.CODE._callingConv); }; } - @Override public RegMask calleeSave() { return x86CalleeSave(); } - - - static final RegMask[] WIN64_RET_MASKS, SYS5_RET_MASKS; - static { - WIN64_RET_MASKS = makeRetMasks( WIN64_CALLEE_SAVE_MASK); - SYS5_RET_MASKS = makeRetMasks(SYSTEM5_CALLEE_SAVE_MASK); - } - private static RegMask[] makeRetMasks(RegMask mask) { - int nSaves = mask.size(); - RegMask[] masks = new RegMask[4 + nSaves]; - masks[0] = null; - masks[1] = null; // Memory - masks[2] = null; // Varies, either XMM0 or RAX - masks[3] = RPC_MASK; - short reg = mask.firstReg(); - for( int i=0; i<nSaves; i++ ) { - masks[i+4] = new RegMask(reg); - reg = mask.nextReg(reg); - } - return masks; + @Override public long neverSave() { return 1L<<RSP; } + @Override public RegMask retMask( TypeFunPtr tfp ) { + return tfp.ret() instanceof TypeFloat ? XMM0_MASK : RAX_MASK; } + @Override public int rpc() { return RPC; } - // Return single int/ptr register. Used by CallEnd output and Return input. - static RegMask retMask( TypeFunPtr tfp, int i ) { - if( i==2 ) - return tfp.ret() instanceof TypeFloat ? XMM0_MASK : RAX_MASK; - RegMask[] masks = switch( CodeGen.CODE._callingConv ) { - case "SystemV" -> SYS5_RET_MASKS; - case "Win64" -> WIN64_RET_MASKS; - default -> throw new IllegalArgumentException("Unknown calling convention: "+CodeGen.CODE._callingConv); - }; - return masks[i]; - } // Create a split op; any register to any register, including stack slots - @Override public SplitNode split(String kind, byte round, LRG lrg) { return new SplitX86(kind,round); } + @Override public SplitNode split(String kind, byte round, LRG lrg) { + return new SplitX86(kind, round); + } // Return a MachNode unconditional branch @Override public CFGNode jump() { - throw Utils.TODO(); - } - - // Break an infinite loop - @Override public IfNode never( CFGNode ctrl ) { - throw Utils.TODO(); + return new UJmpX86(); } // Instruction selection - @Override public Node instSelect( Node n ) { - return switch( n ) { - case AddFNode addf -> addf(addf); - case AddNode add -> add(add); - case AndNode and -> and(and); - case BoolNode bool -> cmp(bool); - case CallEndNode cend -> new CallEndX86(cend); - case CallNode call -> call(call); - case CastNode cast -> new CastX86(cast); - case CProjNode c -> new CProjNode(c); - case ConstantNode con -> con(con); - case DivFNode divf -> new DivFX86(divf); - case DivNode div -> new DivX86(div); - case FunNode fun -> new FunX86(fun); - case IfNode iff -> jmp(iff); - case LoadNode ld -> ld(ld); - case MemMergeNode mem -> new MemMergeNode(mem); - case MulFNode mulf -> new MulFX86(mulf); - case MulNode mul -> mul(mul); - case NewNode nnn -> new NewX86(nnn); - case OrNode or -> or(or); - case ParmNode parm -> new ParmX86(parm); - case PhiNode phi -> new PhiNode(phi); - case ProjNode prj -> prj(prj); - case ReadOnlyNode read -> new ReadOnlyNode(read); - case ReturnNode ret -> new RetX86(ret,ret.fun()); - case SarNode sar -> sar(sar); - case ShlNode shl -> shl(shl); - case ShrNode shr -> shr(shr); - case StartNode start -> new StartNode(start); - case StopNode stop -> new StopNode(stop); - case StoreNode st -> st(st); - case SubFNode subf -> new SubFX86(subf); - case SubNode sub -> sub(sub); - case ToFloatNode tfn -> i2f8(tfn); - case XorNode xor -> xor(xor); - - case LoopNode loop -> new LoopNode(loop); - case RegionNode region-> new RegionNode(region); + @Override + public Node instSelect(Node n) { + return switch (n) { + case AddFNode addf -> addf(addf); + case AddNode add -> add(add); + case AndNode and -> and(and); + case BoolNode bool -> cmp(bool); + case CProjNode c -> new CProjNode(c); + case CallEndNode cend -> new CallEndX86(cend); + case CallNode call -> call(call); + case CastNode cast -> new CastX86(cast); + case ConstantNode con -> con(con); + case DivFNode divf -> new DivFX86(divf); + case DivNode div -> new DivX86(div); + case FunNode fun -> new FunX86(fun); + case IfNode iff -> jmp(iff); + case LoadNode ld -> ld(ld); + case MemMergeNode mem -> new MemMergeNode(mem); + case MinusNode neg -> new NegX86(neg); + case MulFNode mulf -> new MulFX86(mulf); + case MulNode mul -> mul(mul); + case NewNode nnn -> new NewX86(nnn); + case NotNode not -> new NotX86(not); + case OrNode or -> or(or); + case ParmNode parm -> new ParmX86(parm); + case PhiNode phi -> new PhiNode(phi); + case ProjNode prj -> prj(prj); + case ReadOnlyNode read-> new ReadOnlyNode(read); + case ReturnNode ret -> new RetX86(ret, ret.fun()); + case SarNode sar -> sar(sar); + case ShlNode shl -> shl(shl); + case ShrNode shr -> shr(shr); + case StartNode start -> new StartNode(start); + case StopNode stop -> new StopNode(stop); + case StoreNode st -> st(st); + case SubFNode subf -> new SubFX86(subf); + case SubNode sub -> sub(sub); + case ToFloatNode tfn -> i2f8(tfn); + case XCtrlNode x -> new ConstantNode(Type.XCONTROL); + case XorNode xor -> xor(xor); + + case LoopNode loop -> new LoopNode(loop); + case RegionNode region -> new RegionNode(region); default -> throw Utils.TODO(); }; } + public static boolean imm8( long imm ) { return -128 <= imm && imm <= 127; } + + public static boolean imm32( long imm ) { return (int)imm==imm; } // Attempt a full LEA-style break down. - private Node add( AddNode add ) { + private Node add(AddNode add) { Node lhs = add.in(1); Node rhs = add.in(2); - if( lhs instanceof LoadNode ld && ld.nOuts()==1 ) - return new AddMemX86(add,address(ld),ld.ptr(),idx,off,scale, imm(rhs),val); + if( lhs instanceof LoadNode ld && ld.nOuts() == 1 && ld._declaredType.log_size() >= 3) + return new AddMemX86(add, address(ld), ld.ptr(), idx, off, scale, 0, rhs); - if( rhs instanceof LoadNode ld && ld.nOuts()==1 ) - throw Utils.TODO(); // Swap load sides +// if(rhs instanceof LoadNode ld && ld.nOuts() == 1 && ld._declaredType.log_size() >= 3) { +// throw Utils.TODO(); // Swap load sides +// } // Attempt a full LEA-style break down. // Returns one of AddX86, AddIX86, LeaX86, or LHS - if( rhs instanceof ConstantNode off && off._con instanceof TypeInteger toff ) { + if( rhs instanceof ConstantNode off2 && off2._con instanceof TypeInteger toff ) { + long imm = toff.value(); + assert imm!=0; // Folded in peeps + if( (int)imm != imm ) // Full 64bit immediate + return new AddX86(add); + // Now imm <= 32bits if( lhs instanceof AddNode ladd ) // ((base + (idx << scale)) + off) - return _lea(add,ladd.in(1),ladd.in(2),toff.value()); + return _lea(add, ladd.in(1), ladd.in(2), (int)imm); if( lhs instanceof ShlNode shift ) // (idx << scale) + off; no base - return _lea(add,null,shift,toff.value()); + return _lea(add, null, shift, (int)imm); // lhs + rhs1 - if( toff.value()==0 ) return add; - return new AddIX86(add, toff); + return new AddIX86(add, (int)imm); } - return _lea(add,lhs,rhs,0); + return _lea(add, lhs, rhs, 0); } - private Node addf( AddFNode addf ) { - if( addf.in(1) instanceof LoadNode ld && ld.nOuts()==1 ) - return new AddFMemX86(addf,address(ld),ld.ptr(),idx,off,scale, addf.in(2)); + private Node addf(AddFNode addf) { + if(addf.in(1) instanceof LoadNode ld && ld.nOuts() == 1) + return new AddFMemX86(addf, address(ld), ld.ptr(), idx, off, scale, addf.in(2)); - if( addf.in(2) instanceof LoadNode ld && ld.nOuts()==1 ) - throw Utils.TODO(); // Swap load sides +// if(addf.in(2) instanceof LoadNode ld && ld.nOuts() == 1) +// throw Utils.TODO(); // Swap load sides return new AddFX86(addf); } - private Node _lea( Node add, Node base, Node idx, long off ) { - int scale = 1; - if( base instanceof ShlNode && !(idx instanceof ShlNode) ) throw Utils.TODO(); // Bug in canonicalization, should on RHS + private Node _lea(Node add, Node base, Node idx, int off) { + int scale = 0; + if( base instanceof ShlNode && !(idx instanceof ShlNode) ) + throw Utils.TODO(); // Bug in canonicalization, should on RHS if( idx instanceof ShlNode shift && shift.in(2) instanceof ConstantNode shfcon && shfcon._con instanceof TypeInteger tscale && 0 <= tscale.value() && tscale.value() <= 3 ) { idx = shift.in(1); - scale = 1 << ((int)tscale.value()); + scale = ((int) tscale.value()); } // (base + idx) + off - return off==0 && scale==1 + return off == 0 && scale == 0 ? new AddX86(add) - : new LeaX86(add,base,idx,scale,off); + : new LeaX86(add, base, idx, scale, off); } private Node and(AndNode and) { - if( and.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti ) - return new AndIX86(and, ti); + if( and.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm32(ti.value()) ) + return new AndIX86(and, (int)ti.value()); return new AndX86(and); } private Node call(CallNode call) { - if( call.fptr() instanceof ConstantNode con && con._con instanceof TypeFunPtr tfp ) - return new CallX86(call, tfp); - return new CallRX86( call ); + return call.fptr() instanceof ConstantNode con && con._con instanceof TypeFunPtr tfp + ? new CallX86(call, tfp) + : new CallRX86(call); } // Because X86 flags, a normal ideal Bool is 2 X86 ops: a "cmp" and at "setz". // Ideal If reading from a setz will skip it and use the "cmp" instead. + private static boolean swap, unsigned; private Node cmp( BoolNode bool ) { + swap = unsigned = false; Node cmp = _cmp(bool); - return new SetX86(cmp,bool.op()); + return new SetX86(cmp, swap ? IfNode.swap(bool.op()) : bool.op(), unsigned); } - private Node _cmp( BoolNode bool ) { + + private Node _cmp(BoolNode bool) { // Float variant - if( bool.isFloat() ) + if( bool.isFloat() ) { + unsigned = true; return new CmpFX86(bool); - + } Node lhs = bool.in(1); Node rhs = bool.in(2); - if( lhs instanceof LoadNode ld && ld.nOuts()==1 ) - return new CmpMemX86(bool,address(ld),ld.ptr(),idx,off,scale, imm(rhs),val,false); - if( rhs instanceof LoadNode ld && ld.nOuts()==1 ) - return new CmpMemX86(bool,address(ld),ld.ptr(),idx,off,scale, imm(lhs),val,true); + // Since cmp does not record a BOP, SetX/Jmp need to know if the CMP is swapped. + + // Vs memory + if( lhs instanceof LoadNode ld && ld.nOuts() == 1 && rhs._type.isa(ld._declaredType) ) + return new CmpMemX86(bool, address(ld), ld.ptr(), idx, off, scale, imm(rhs), val, false); + + // Operands swap in the encoding directly, no need for Set/Jmp to swap `bop` + if( rhs instanceof LoadNode ld && ld.nOuts() == 1 && lhs._type.isa(ld._declaredType)) + return new CmpMemX86(bool, address(ld), ld.ptr(), idx, off, scale, imm(lhs), val, true ); // Vs immediate - if( rhs instanceof ConstantNode con && con._con instanceof TypeInteger ti ) - return new CmpIX86(bool, ti); + if( rhs instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm32(ti.value()) ) + return new CmpIX86(bool, (int)ti.value(), false); + + // Operand swap compare; Set and Jmp need to swap `bop` + if( lhs instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm32(ti.value()) ) + return new CmpIX86(bool, (int)ti.value(), swap=true); + // x vs y return new CmpX86(bool); } private Node con( ConstantNode con ) { - if( !con._con.isConstant() ) return new ConstantNode( con ); // Default unknown caller inputs - return switch( con._con ) { - case TypeInteger ti -> new IntX86(con); - case TypeFloat tf -> new FltX86(con); - case TypeFunPtr tfp -> new TFPX86(con); - case TypeMemPtr tmp -> throw Utils.TODO(); - case TypeNil tn -> throw Utils.TODO(); - // TOP, BOTTOM, XCtrl, Ctrl, etc. Never any executable code. - case Type t -> t==Type.NIL ? new IntX86(con) : new ConstantNode(con); + if(!con._con.isConstant()) return new ConstantNode(con); // Default unknown caller inputs + return switch (con._con) { + case TypeInteger ti -> new IntX86(con); + case TypeFloat tf -> new FltX86(con); + case TypeFunPtr tfp -> new TFPX86(con); + case TypeMemPtr tmp -> throw Utils.TODO(); + case TypeNil tn -> throw Utils.TODO(); + // TOP, BOTTOM, XCtrl, Ctrl, etc. Never any executable code. + case Type t -> t == Type.NIL ? new IntX86(con) : new ConstantNode(con); }; } @@ -367,28 +516,30 @@ public class x86_64_v2 extends Machine { return new I2f8X86(tfn); } - private Node jmp( IfNode iff ) { + private Node jmp(IfNode iff) { // If/Bool combos will match to a Cmp/Set which sets flags. // Most general arith ops will also set flags, which the Jmp needs directly. // Loads do not set the flags, and will need an explicit TEST - if( !(iff.in(1) instanceof BoolNode) ) - iff.setDef(1,new BoolNode.EQ(iff.in(1),new ConstantNode(TypeInteger.ZERO))); - return new JmpX86(iff, ((BoolNode)iff.in(1)).op()); + String op = "!="; + if( iff.in(1) instanceof BoolNode bool ) op = swap ? IfNode.swap(bool.op()) : bool.op(); + else if( iff.in(1)==null ) op = "=="; // Never-node cutout + else iff.setDef(1, new BoolNode.NE(iff.in(1), new ConstantNode(TypeInteger.ZERO))); + return new JmpX86(iff, op); } - private Node ld( LoadNode ld ) { - return new LoadX86(address(ld),ld.ptr(),idx,off,scale); + private Node ld(LoadNode ld) { + return new LoadX86(address(ld), ld.ptr(), idx, off, scale); } private Node mul(MulNode mul) { - return mul.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti - ? new MulIX86(mul, ti) - : new MulX86(mul); + if( mul.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm32(ti.value()) ) + return new MulIX86(mul, (int)ti.value()); + return new MulX86(mul); } private Node or(OrNode or) { - if( or.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti) - return new OrIX86(or, ti); + if( or.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm32(ti.value()) ) + return new OrIX86(or, (int)ti.value()); return new OrX86(or); } @@ -396,21 +547,21 @@ public class x86_64_v2 extends Machine { return new ProjX86(prj); } - private Node sar( SarNode sar ) { - if( sar.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti) - return new SarIX86(sar, ti); + private Node sar(SarNode sar) { + if( sar.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm8(ti.value())) + return new SarIX86(sar, (int)(ti.value() & 0xff) ); return new SarX86(sar); } - private Node shl( ShlNode shl ) { - if( shl.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti ) - return new ShlIX86(shl, ti); + private Node shl(ShlNode shl) { + if( shl.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm8(ti.value())) + return new ShlIX86(shl, (int)(ti.value() & 0xff) ); return new ShlX86(shl); } - private Node shr(ShrNode shr) { - if( shr.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti ) - return new ShrIX86(shr, ti); + private Node shr( ShrNode shr ) { + if( shr.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm8(ti.value()) ) + return new ShrIX86(shr, (int)(ti.value() & 0xff) ); return new ShrX86(shr); } @@ -418,58 +569,58 @@ public class x86_64_v2 extends Machine { // Look for "*ptr op= val" Node op = st.val(); if( op instanceof AddNode ) { - if( op.in(1) instanceof LoadNode ld && - ld.in(0)==st.in(0) && - ld.mem()==st.mem() && - ld.ptr()==st.ptr() && - ld.off()==st.off() ) { - if( op instanceof AddNode ) { - return new MemAddX86(address(st),st.ptr(),idx,off,scale,imm(op.in(2)),val); - } - throw Utils.TODO(); - } + if( op.in(1) instanceof LoadNode ld && stld_match(st,ld) ) + return new MemAddX86(address(st), st.ptr(), idx, off, scale, imm(op.in(2)), val); + if( op.in(2) instanceof LoadNode ld && stld_match(st,ld) ) + return new MemAddX86(address(st), st.ptr(), idx, off, scale, imm(op.in(1)), val); } - - return new StoreX86(address(st),st.ptr(),idx,off,scale,imm(st.val()),val); + return new StoreX86(address(st), st.ptr(), idx, off, scale, imm(st.val()), val); + } + private static boolean stld_match(StoreNode st, LoadNode ld ) { + return + ld.in(0) == st.in(0) && + ld.mem() == st.mem() && + ld.ptr() == st.ptr() && + ld.off() == st.off(); } - private Node sub( SubNode sub ) { + + private Node sub (SubNode sub ){ return sub.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti - ? new AddIX86(sub, TypeInteger.constant(-ti.value())) + ? new AddIX86(sub, (int)-ti.value()) : new SubX86(sub); } - private Node xor(XorNode xor) { - if(xor.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti) - return new XorIX86(xor, ti); - throw Utils.TODO(); + private Node xor (XorNode xor){ + if( xor.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm32(ti.value()) ) + return new XorIX86(xor, (int)ti.value()); + return new XorX86(xor); } - // Gather X86 addressing mode bits prior to constructing. This is a // builder pattern, but saving the bits in a *local* *global* here to keep // mess contained. private static int off, scale, imm; private static Node idx, val; - private <N extends MemOpNode> N address( N mop ) { + private <N extends MemOpNode > N address(N mop) { off = scale = imm = 0; // Reset idx = val = null; Node base = mop.ptr(); // Skip/throw-away a ReadOnly, only used to typecheck - if( base instanceof ReadOnlyNode read ) base = read.in(1); + if(base instanceof ReadOnlyNode read) base = read.in(1); assert !(base instanceof AddNode) && base._type instanceof TypeMemPtr; // Base ptr always, not some derived - if( mop.off() instanceof AddNode add && add.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti ) { - off = (int)ti.value(); + if(mop.off() instanceof AddNode add && add.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti) { + off = (int) ti.value(); assert off == ti.value(); // In 32-bit range idx = add.in(1); - if( idx instanceof ShlNode shift && shift.in(2) instanceof ConstantNode shfcon && - shfcon._con instanceof TypeInteger tscale && 0 <= tscale.value() && tscale.value() <= 3 ) { + if(idx instanceof ShlNode shift && shift.in(2) instanceof ConstantNode shfcon && + shfcon._con instanceof TypeInteger tscale && 0 <= tscale.value() && tscale.value() <= 3) { idx = shift.in(1); - scale = (int)tscale.value(); + scale = (int) tscale.value(); } } else { - if( mop.off() instanceof ConstantNode con && con._con instanceof TypeInteger ti ) { - off = (int)ti.value(); + if(mop.off() instanceof ConstantNode con && con._con instanceof TypeInteger ti) { + off = (int) ti.value(); assert off == ti.value(); // In 32-bit range } else { idx = mop.off(); @@ -479,10 +630,10 @@ public class x86_64_v2 extends Machine { } private int imm( Node xval ) { - assert val==null && imm==0; - if( xval instanceof ConstantNode con && con._con instanceof TypeInteger ti ) { + assert val == null && imm == 0; + if( xval instanceof ConstantNode con && con._con instanceof TypeInteger ti) { val = null; - imm = (int)ti.value(); + imm = (int) ti.value(); assert imm == ti.value(); // In 32-bit range } else { val = xval; diff --git a/src/main/java/com/seaofnodes/simple/print/ASMPrinter.java b/src/main/java/com/seaofnodes/simple/print/ASMPrinter.java index 1a233aa2..da269d31 100644 --- a/src/main/java/com/seaofnodes/simple/print/ASMPrinter.java +++ b/src/main/java/com/seaofnodes/simple/print/ASMPrinter.java @@ -1,11 +1,12 @@ package com.seaofnodes.simple.print; -import com.seaofnodes.simple.codegen.CodeGen; 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.node.*; -import com.seaofnodes.simple.type.TypeFunPtr; -import com.seaofnodes.simple.type.TypeMem; -import com.seaofnodes.simple.type.TypeRPC; +import com.seaofnodes.simple.type.*; +import java.util.HashSet; public abstract class ASMPrinter { @@ -18,17 +19,70 @@ public abstract class ASMPrinter { for( int i=0; i<code._cfg._len; i++ ) if( code._cfg.at(i) instanceof FunNode fun ) iadr = print(iadr,sb,code,fun,i); + + // Skip padding + while( ((iadr+7) & -8) > iadr ) + iadr++; + + // constant pool + Encoding enc = code._encoding; + if( enc!=null && !enc._bigCons.isEmpty() ) { + iadr = (iadr+15)&-16; // pad to 16 + 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); + 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); + } + relo._t.print(sb).nl(); + } + } + } + } + + return sb; } private static int print(int iadr, SB sb, CodeGen code, FunNode fun, int cfgidx) { + FunNode old=null; + if( code._encoding!=null ) { + old = code._encoding._fun; + code._encoding._fun = fun; // Useful printing after RA + } // Function header sb.nl().p("---"); if( fun._name != null ) sb.p(fun._name).p(" "); fun.sig().print(sb); sb.p("---------------------------").nl(); + iadr = (iadr + 15)&-16; // All function entries padded to 16 align - + if( fun._frameAdjust != 0 ) + iadr = doInst(iadr,sb,code,fun,cfgidx,fun,true,true); while( !(code._cfg.at(cfgidx) instanceof ReturnNode) ) iadr = doBlock(iadr,sb,code,fun,cfgidx++); @@ -36,69 +90,74 @@ public abstract class ASMPrinter { sb.p("---"); fun.sig().print(sb); sb.p("---------------------------").nl(); + if( code._encoding != null ) + code._encoding._fun = old; return iadr; } - static private final int encWidth = 8; static private final int opWidth = 5; static private final int argWidth = 30; static int doBlock(int iadr, SB sb, CodeGen code, FunNode fun, int cfgidx) { + final int encWidth = code._mach==null ? 2 : code._mach.defaultOpSize()*2; CFGNode bb = code._cfg.at(cfgidx); if( bb != fun && !(bb instanceof IfNode) && !(bb instanceof CallEndNode) && !(bb instanceof CallNode) && !(bb instanceof CProjNode && bb.in(0) instanceof CallEndNode )) - sb.p(bb instanceof LoopNode ? "LOOP" : "L").p(bb._nid).p(":").nl(); + sb.p(label(bb)).p(":").nl(); if( bb instanceof CallNode ) return iadr; final boolean postAlloc = code._phase.ordinal() > CodeGen.Phase.RegAlloc.ordinal(); + final boolean postEncode= code._phase.ordinal() >=CodeGen.Phase.Encoding.ordinal(); - // Count Phis - int nPhi=0; - for( ; nPhi<bb.nOuts(); nPhi++ ) - if( !(bb.out(nPhi) instanceof PhiNode) ) - break; - - if( nPhi>0 ) { - // Post-alloc phi prints all on one line + boolean once=false; + for( Node n : bb.outs() ) { + if( !(n instanceof PhiNode phi) ) continue; + if( phi._type instanceof TypeMem || phi._type instanceof TypeRPC ) continue; // Nothing for the hidden ones + // Post-RegAlloc phi prints all on one line if( postAlloc ) { - sb.fix(4," ").p(" ").fix(encWidth,"").p(" "); - for( int i=0; i<nPhi; i++ ) { - PhiNode phi = (PhiNode)bb.out(i); - if( !(phi._type instanceof TypeMem || phi._type instanceof TypeRPC) ) // Nothing for the hidden ones - sb.p(phi._label).p(':').p(code.reg(phi)).p(','); - } - sb.unchar().nl(); - + if( !once ) { once=true; sb.fix(4," ").p(" ").fix(encWidth,"").p(" "); } + sb.p(phi._label).p(':').p(code.reg(phi,fun)).p(','); } else { - for( int j=0; j<nPhi; j++ ) { - PhiNode phi = (PhiNode)bb.out(j); - if( phi._type instanceof TypeMem || phi._type instanceof TypeRPC ) continue; // Nothing for the hidden ones - sb.fix(4," ").p(" ").fix(encWidth,"").p(" ").fix(opWidth,phi._label).p(" "); - sb.p(code.reg(phi)); - if( !(phi instanceof ParmNode) ) { - sb.p(" = phi( "); - for( int i=1; i<phi.nIns(); i++ ) - sb.p("N").p(phi.in(i)._nid).p(","); - sb.unchar().p(" )"); - } - sb.nl(); + // Pre-RegAlloc phi prints one line per + sb.fix(4," ").p(" ").fix(encWidth,"").p(" ").fix(opWidth,phi._label).p(" ").p(code.reg(phi,fun)); + if( phi.getClass() == PhiNode.class ) { + sb.p(" = phi( "); + for( int i=1; i<phi.nIns(); i++ ) + sb.p("N").p(phi.in(i)._nid).p(","); + sb.unchar().p(" )"); } + sb.nl(); } } + if( once ) sb.unchar().nl(); // All the non-phis - for( int i=nPhi; i<bb.nOuts(); i++ ) - iadr = doInst(iadr, sb,code,fun,bb,bb.out(i),postAlloc ); + for( int i=0; i<bb.nOuts(); i++ ) + if( !(bb.out(i) instanceof PhiNode) ) + iadr = doInst(iadr, sb,code, fun, cfgidx, bb.out(i),postAlloc, postEncode ); return iadr; } - static int doInst(int iadr, SB sb, CodeGen code, FunNode fun, CFGNode bb, Node n, boolean postAlloc ) { - if( n instanceof CProjNode ) return iadr; - if( n instanceof CalleeSaveNode && postAlloc ) return iadr; + static int doInst( int iadr, SB sb, CodeGen code, FunNode fun, int cfgidx, Node n, boolean postAlloc, boolean postEncode ) { + if( n==null || n instanceof CProjNode ) return iadr; + if( postAlloc && n instanceof CalleeSaveNode ) return iadr; + if( postEncode && n instanceof ProjNode ) return iadr; + if( n instanceof MemMergeNode ) return iadr; + if( n.getClass() == ConstantNode.class ) return iadr; // Default placeholders + final int dopz = code._mach==null ? 2 : code._mach.defaultOpSize(); + final int encWidth = dopz*2; // All blocks ending in a Region will need to either fall into or jump // to this block. Until the post-reg-alloc block layout cleanup, we // need to assume a jump. There's no real hardware op here, yet. - if( n instanceof RegionNode && !(n instanceof FunNode) ) { - sb.hex4(iadr++).p(" ").fix(encWidth,"??").p(" ").fix(opWidth,"JMP").p(" ").fix(argWidth,"L"+n._nid).nl(); + if( n instanceof RegionNode cfg && !(n instanceof FunNode) ) { + if( postEncode ) return iadr; // All jumps inserted already + while( cfgidx < code._cfg._len-1 ) { + CFGNode next = code._cfg.at(++cfgidx); + if( next == n ) return iadr; // Fall-through, no branch + if( next.nOuts()>1 ) + break; // Has code in the block, need to jump around + // No code in the block, can fall through it + } + sb.hex2(iadr++).p(" ").fix(encWidth,"??").p(" ").fix(opWidth,"JMP").p(" ").fix(argWidth,label(cfg)).nl(); return iadr; } @@ -106,30 +165,44 @@ public abstract class ASMPrinter { // get indent slightly and just print their index & node# if( n instanceof ProjNode proj ) { if( proj._type instanceof TypeMem ) return iadr; // Nothing for the hidden ones - sb.fix(4," ").p(" ").fix(encWidth,"").p(" ").fix(opWidth,proj._label==null ? "---" : proj._label).p(" ").p(code.reg(n)).nl(); + sb.fix(4," ").p(" ").fix(encWidth,"").p(" ").fix(opWidth,proj._label==null ? "---" : proj._label).p(" ").p(code.reg(n,fun)).nl(); return iadr; } // ADDR ENCODING Op--- dst = src op src // Comment // 1234 abcdefgh ld4 RAX = [Rbase + off] // Comment - sb.hex4(iadr); - sb.p(" "); + sb.hex2(iadr).p(" "); // Encoding - int size = 1; // TODO: Fake encoding size - iadr += size; - sb.fix(encWidth,"??"); + int fatEncoding = 0; + if( code._encoding != null ) { + int size = code._encoding._opLen[n._nid]; + if( code._asmLittle ) + for( int i=0; i<Math.min(size,dopz); i++ ) + sb.hex1(code._encoding._bits.buf()[iadr++]); + else { + iadr += Math.min(size,dopz); + for( int i=0; i<Math.min(size,dopz); i++ ) + sb.hex1(code._encoding._bits.buf()[iadr-i-1]); + } + for( int i=size*2; i<encWidth; i++ ) + sb.p(" "); + fatEncoding = size - (encWidth>>1); // Not-printed parts of encoding + } else + sb.fix(encWidth,""); sb.p(" "); // Op; generally "ld4" or "call" - sb.fix(opWidth, n instanceof MachNode mach ? mach.op() : n.label()); - sb.p(" "); + sb.fix(opWidth, n instanceof MachNode mach ? mach.op() : n.label()).p(" "); // General asm args + String isMultiOp = null; if( n instanceof MachNode mach ) { int old = sb.len(); mach.asm(code,sb); - sb.fix(argWidth-(sb.len()-old),""); // Pad out + int len = sb.len(); + isMultiOp = isMultiOp(sb,old,len); + sb.fix(argWidth-(len-old),""); // Pad out } else if( !(n._type instanceof TypeMem) ) { // Room for some inputs @@ -150,14 +223,64 @@ public abstract class ASMPrinter { sb.nl(); + // Printing more op bits than fit + if( isMultiOp != null && code._encoding != null ) { + // Multiple ops, template style, no RA, no scheduling. Print out + // one-line-per-newline, with encoding bits up front. + int size = code._encoding._opLen[n._nid]; + int off = Math.min(size,dopz); + while( isMultiOp!=null ) { + sb.hex2(iadr).p(" "); + int len = Math.min(size-off,dopz); + for( int i=0; i<len; i++ ) + sb.hex1(code._encoding._bits.buf()[iadr++]); + off += len; + for( int i=len; i<dopz; i++ ) sb.p(" "); + sb.p(" "); + int x = isMultiOp.indexOf('\n'); + if( x== -1 ) { // Last line + sb.p(isMultiOp).nl(); + isMultiOp = null; + } else { + sb.p(isMultiOp.substring(0,x+1)); // Includes the newline + isMultiOp = isMultiOp.substring(x+1); + } + + } + + } else if( fatEncoding > 0 ) { + // Extra bytes past the default encoding width, all put on a line by + // themselves. X86 special for super long encodings + sb.hex2(iadr).p(" "); + for( int i=0; i<fatEncoding; i++ ) + sb.hex1(code._encoding._bits.buf()[iadr++]); + sb.nl(); + } + + // MultiNodes are immediately followed by projection(s) if( !(n instanceof CFGNode) && n instanceof MultiNode ) { for( Node proj : n._outputs ) { - assert proj instanceof ProjNode; - doInst(iadr,sb,code,fun,bb,proj,postAlloc); + if( proj instanceof ProjNode ) // it could also be an ante-dep + doInst(iadr,sb,code,fun, cfgidx, proj,postAlloc,postEncode); } } return iadr; } + + private static String label( CFGNode bb ) { + return (bb instanceof LoopNode ? "LOOP" : "L")+bb._nid; + } + + private static String isMultiOp(SB sb, int old, int len) { + for( int i=old; i<len; i++ ) + if( sb.at(i)=='\n' ) { + String s = sb.subString(i+1,len); + sb.setLen(i); + return s; + } + return null; + } + } diff --git a/src/main/java/com/seaofnodes/simple/print/IRPrinter.java b/src/main/java/com/seaofnodes/simple/print/IRPrinter.java index c91f844b..c6e478c5 100644 --- a/src/main/java/com/seaofnodes/simple/print/IRPrinter.java +++ b/src/main/java/com/seaofnodes/simple/print/IRPrinter.java @@ -16,6 +16,7 @@ public abstract class IRPrinter { // NNID NNAME DDEF DDEF [[ UUSE UUSE ]] TYPE // 1234 sssss 1234 1234 1234 1234 1234 1234 tttttt public static SB printLine( Node n, SB sb ) { + if( n==null ) return sb; sb.p("%4d %-7.7s ".formatted(n._nid,n.label())); if( n._inputs==null ) return sb.p("DEAD\n"); @@ -211,7 +212,8 @@ public abstract class IRPrinter { printLine(n,sb); if( !(n instanceof CFGNode) && n instanceof MultiNode ) for( Node use : n._outputs ) - printLine(use,sb); + if( use instanceof ProjNode ) + printLine(use,sb); } } @@ -281,7 +283,8 @@ public abstract class IRPrinter { printLine( n, sb, bns, i--, ds,ns ); if( n instanceof MultiNode && !(n instanceof CFGNode) ) { for( Node use : n._outputs ) { - printLine(use,sb,bns,bns.indexOf(use),ds,ns); + if( use instanceof ProjNode ) + printLine(use,sb,bns,bns.indexOf(use),ds,ns); } } } diff --git a/src/main/java/com/seaofnodes/simple/print/JSViewer.java b/src/main/java/com/seaofnodes/simple/print/JSViewer.java index a2b674c9..76c64c71 100644 --- a/src/main/java/com/seaofnodes/simple/print/JSViewer.java +++ b/src/main/java/com/seaofnodes/simple/print/JSViewer.java @@ -72,7 +72,7 @@ public class JSViewer implements AutoCloseable { public static void show() { if( SERVER!=null && SHOW ) _show(); } private static void _show() { // Skip util we at least get the Parse object made - if( CODE._phase == null ) + if( CODE._phase==null || CODE._phase.ordinal() < CodeGen.Phase.Parse.ordinal() ) return; boolean midParse = CODE._phase == CodeGen.Phase.Parse; Stack<ScopeNode> xScopes = midParse ? CODE.P._xScopes : null; diff --git a/src/main/java/com/seaofnodes/simple/type/Type.java b/src/main/java/com/seaofnodes/simple/type/Type.java index 847235dd..6df4e7ab 100644 --- a/src/main/java/com/seaofnodes/simple/type/Type.java +++ b/src/main/java/com/seaofnodes/simple/type/Type.java @@ -103,6 +103,7 @@ public class Type { // checks with every type compare, only during interning. // Factory method which interns "this" + @SuppressWarnings("unchecked") public <T extends Type> T intern() { T nnn = (T)INTERN.get(this); if( nnn==null ) @@ -217,7 +218,7 @@ public class Type { // Sizes are expected to be between 1 and 64 bits. // Size 0 means this either takes no space (such as a known-zero field) // or isn't a scalar to be stored in memory. - public int log_size() { throw Utils.TODO(); } + public int log_size() { return 3; } // ---------------------------------------------------------- // Useful in the debugger, which calls toString everywhere. diff --git a/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java b/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java index 167a931c..bd64bd81 100644 --- a/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java +++ b/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java @@ -44,6 +44,7 @@ public class TypeFunPtr extends TypeNil { public static TypeFunPtr TEST = make((byte)2,TypeTuple.TEST,TypeInteger.BOT,1); public static TypeFunPtr TEST0 = make((byte)3,TypeTuple.TEST,TypeInteger.BOT,3); public static TypeFunPtr MAIN = make((byte)3,TypeTuple.MAIN,TypeInteger.BOT,-1); + public static TypeFunPtr CALLOC= make((byte)3,TypeTuple.CALLOC,TypeMemPtr.BOT,-1); public static void gather(ArrayList<Type> ts) { ts.add(TEST); ts.add(TEST0); ts.add(BOT); ts.add(MAIN); } @Override diff --git a/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java b/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java index 7e607838..284bf581 100644 --- a/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java +++ b/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java @@ -77,7 +77,7 @@ public class TypeMemPtr extends TypeNil { // Is forward-reference @Override public boolean isFRef() { return _obj.isFRef(); } - @Override public int log_size() { return 2; } // (1<<2)==4-byte pointers + @Override public int log_size() { return 3; } // (1<<3)==8-byte pointers @Override int hash() { return _obj.hashCode() ^ super.hash(); } diff --git a/src/main/java/com/seaofnodes/simple/type/TypeStruct.java b/src/main/java/com/seaofnodes/simple/type/TypeStruct.java index e981b5dc..aa6f2dfd 100644 --- a/src/main/java/com/seaofnodes/simple/type/TypeStruct.java +++ b/src/main/java/com/seaofnodes/simple/type/TypeStruct.java @@ -182,9 +182,12 @@ public class TypeStruct extends Type { @Override public SB print(SB sb) { + if( _fields == null ) return sb.p(_name); // Forward reference struct, just print the name + if( isAry() ) { + if( !isFinal() ) sb.p("!"); + return sb.p(_name); // Skip printing generic array fields + } sb.p(_name); - if( _fields == null ) return sb; // Forward reference struct, just print the name - if( isAry() ) return sb; // Skip printing generic array fields sb.p(" {"); for( Field f : _fields ) f._type.print(sb).p(f._final ? " " : " !").p(f._fname).p("; "); diff --git a/src/main/java/com/seaofnodes/simple/type/TypeTuple.java b/src/main/java/com/seaofnodes/simple/type/TypeTuple.java index 95e35e99..d3b19b52 100644 --- a/src/main/java/com/seaofnodes/simple/type/TypeTuple.java +++ b/src/main/java/com/seaofnodes/simple/type/TypeTuple.java @@ -17,6 +17,7 @@ public class TypeTuple extends Type { public static final TypeTuple START= make(Type.CONTROL,TypeMem.TOP,TypeInteger.BOT); public static final TypeTuple MAIN = make(TypeInteger.BOT); public static final TypeTuple RET = make(Type.CONTROL,TypeMem.BOT,Type.BOTTOM); + public static final TypeTuple CALLOC = make(TypeInteger.BOT,TypeInteger.BOT); public static final TypeTuple IF_BOTH = make(new Type[]{Type. CONTROL,Type. CONTROL}); public static final TypeTuple IF_NEITHER = make(new Type[]{Type.XCONTROL,Type.XCONTROL}); @@ -55,6 +56,21 @@ public class TypeTuple extends Type { return make(ts); } + @Override public boolean isConstant() { + for( Type t : _types ) + if( !t.isConstant() ) + return false; + return true; + } + + @Override public int log_size() { + assert isConstant(); + int log_size = 0; + for( Type t : _types ) + log_size = Math.max(log_size,t.log_size()); + return log_size; + } + public Type ret() { assert _types.length==3; return _types[2]; } @Override public String str() { return print(new SB()).toString(); } |
