summaryrefslogtreecommitdiff
path: root/chapter21/src
diff options
context:
space:
mode:
Diffstat (limited to 'chapter21/src')
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/codegen/Encoding.java120
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/codegen/RIPRelSize.java8
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/BranchARM.java20
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/UJmpARM.java18
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java35
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/BranchRISC.java24
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/UJmpRISC.java24
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java36
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/JmpX86.java17
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/UJmpX86.java18
10 files changed, 254 insertions, 66 deletions
diff --git a/chapter21/src/main/java/com/seaofnodes/simple/codegen/Encoding.java b/chapter21/src/main/java/com/seaofnodes/simple/codegen/Encoding.java
index b0fda2e1..5d7266aa 100644
--- a/chapter21/src/main/java/com/seaofnodes/simple/codegen/Encoding.java
+++ b/chapter21/src/main/java/com/seaofnodes/simple/codegen/Encoding.java
@@ -51,7 +51,7 @@ public class Encoding {
_code = code;
_bits = new BAOS();
_bigCons = new HashMap<>();
- _jmps = new HashMap<>();
+ _jmps = new HashMap<>();
}
// Shortcut to the defining register
@@ -73,6 +73,18 @@ public class Encoding {
add4((int)(i64>>32));
}
+ // 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);
+ }
+
// Relocation thinking:
// `encoding()` calls back with info (TFP needed, branch target needed).
@@ -98,14 +110,32 @@ public class Encoding {
// TODO:
}
private final HashMap<CFGNode,CFGNode> _jmps;
- public void jump( CFGNode jmp, CFGNode target ) {
- // TDOO: also support 1-byte offset and short jump and compressing the binary
- // TODO: record and patch
- }
+ public void jump( CFGNode jmp, CFGNode target ) { _jmps.put(jmp,target); }
void encode() {
// Basic block layout: invert branches to keep blocks in-order; insert
- // unconditional jumps. Layout is still RPO but with more restrictions.
+ // unconditional jumps. Attempt to keep backwards branches taken,
+ // forwards not-taken (this is the default prediction on most
+ // hardware). Layout is still RPO 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: invert 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 RPO but with more restrictions.
+ private void basicBlockLayout() {
Ary<CFGNode> rpo = new Ary<>(CFGNode.class);
BitSet visit = _code.visit();
for( Node n : _code._start._outputs )
@@ -118,27 +148,6 @@ public class Encoding {
rpo.swap(i,rpo.size()-1-i);
visit.clear();
_code._cfg = rpo; // Save the new ordering
-
- // Write encoding bits in order
- _opStart= new int [_code.UID()];
- _opLen = new byte[_code.UID()]; // Used during ASM printing
- for( CFGNode bb : _code._cfg ) {
- if( bb instanceof CProjNode ) _opStart[bb._nid] = _bits.size();
- for( Node n : bb._outputs ) {
- if( n instanceof MachNode mach ) {
- _opStart[n._nid] = _bits.size();
- mach.encoding( this );
- _opLen[n._nid] = (byte) (_bits.size() - _opStart[n._nid]);
- }
- }
- }
-
- System.out.println(_code.asm());
-
- compactShortForm();
-
- // Patch RIP-relative encodings now
- throw Utils.TODO();
}
// Basic block layout. Now that RegAlloc is finished, no more spill code
@@ -154,7 +163,7 @@ public class Encoding {
CFGNode next = bb instanceof ReturnNode ? (CFGNode)bb.out(bb.nOuts()-1) : bb.uctrl();
// If the *next* BB has already been visited, we require an
// unconditional backwards jump here
- if( visit.get(next._nid) ) {
+ if( visit.get(next._nid) && !(next instanceof StopNode) && !(bb.in(0) instanceof IfNode )) {
CFGNode jmp = _code._mach.jump();
jmp.setDefX(0,bb);
next.setDef(next._inputs.find(bb),jmp);
@@ -164,9 +173,11 @@ public class Encoding {
} else {
boolean invert = false;
// Pick out the T/F projections
- CProjNode t = (CProjNode)bb.out(bb.nOuts()-1);
- CProjNode f = (CProjNode)bb.out(bb.nOuts()-2);
- if( t._idx==1 ) { CProjNode tmp=f; f=t; t=tmp; }
+ CProjNode t = iff.cproj(0);
+ CProjNode f = iff.cproj(1);
+ //CProjNode t = (CProjNode)bb.out(bb.nOuts()-1);
+ //CProjNode f = (CProjNode)bb.out(bb.nOuts()-2);
+ //if( t._idx==1 ) { CProjNode tmp=f; f=t; t=tmp; }
int tld = t.loopDepth(), fld = f.loopDepth(), bld = bb.loopDepth();
// Decide entering or exiting a loop
if( tld==bld ) {
@@ -203,13 +214,37 @@ public class Encoding {
}
- // Short-form RIP-relative support
- void compactShortForm() {
+ // Write encoding bits in order into a big byte array.
+ // Record opcode start and length.
+ private void writeEncodings() {
+ _opStart= new int [_code.UID()];
+ _opLen = new byte[_code.UID()];
+ for( CFGNode bb : _code._cfg ) {
+ if( !(bb instanceof MachNode) ) _opStart[bb._nid] = _bits.size();
+ for( Node n : bb._outputs ) {
+ if( n instanceof MachNode mach ) {
+ _opStart[n._nid] = _bits.size();
+ mach.encoding( this );
+ _opLen[n._nid] = (byte) (_bits.size() - _opStart[n._nid]);
+ }
+ }
+ }
+ }
+
+
+ // Short-form RIP-relative support: replace long encodings with short
+ // encodings and compact 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;
@@ -221,6 +256,10 @@ public class Encoding {
_opStart[bb._nid] += slide;
if( bb instanceof RIPRelSize riprel ) {
CFGNode target = (CFGNode)bb.out(0);
+ // 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
+ assert _opStart[target._nid] > 0;
int delta = _opStart[target._nid] - _opStart[bb._nid];
byte opLen = riprel.encSize(delta);
if( _opLen[bb._nid] < opLen ) {
@@ -234,7 +273,7 @@ public class Encoding {
// 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( 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-- ) {
@@ -248,4 +287,17 @@ public class Encoding {
_bits.set(bits,bits.length);
}
}
+
+ // Patch local encodings now
+ private void patchLocalRelocations() {
+ // Walk all the jumps. Re-patch them all now with but with the Real Offset
+ for( CFGNode jmp : _jmps.keySet() ) {
+ CFGNode target = jmp instanceof IfNode iff ? iff.cproj(0) : jmp.uctrl();
+ while( target.nOuts() == 1 ) // Skip empty blocks
+ target = target.uctrl();
+ int start = _opStart[jmp._nid];
+ ((RIPRelSize)jmp).patch(this, start, _opLen[jmp._nid], _opStart[target._nid] - start);
+ }
+ }
+
}
diff --git a/chapter21/src/main/java/com/seaofnodes/simple/codegen/RIPRelSize.java b/chapter21/src/main/java/com/seaofnodes/simple/codegen/RIPRelSize.java
index 36f36b5a..777182dc 100644
--- a/chapter21/src/main/java/com/seaofnodes/simple/codegen/RIPRelSize.java
+++ b/chapter21/src/main/java/com/seaofnodes/simple/codegen/RIPRelSize.java
@@ -2,5 +2,13 @@ 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/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/BranchARM.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/BranchARM.java
index b0a517b0..e3c63ccc 100644
--- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/BranchARM.java
+++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/BranchARM.java
@@ -4,9 +4,8 @@ import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
-
// Jump on flags, uses flags
-public class BranchARM extends IfNode implements MachNode{
+public class BranchARM extends IfNode implements MachNode, RIPRelSize {
String _bop;
BranchARM(IfNode iff, String bop ) {
super(iff);
@@ -38,6 +37,23 @@ public class BranchARM extends IfNode implements MachNode{
int body = arm.b_cond(0b01010100, 0, arm.make_condition(_bop));
enc.add4(body);
}
+
+ // Delta is from opcode start
+ @Override public byte encSize(int delta) {
+ if( -(1<<19) <= delta && delta < (1<<19) ) 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_cond(0b01010100, delta, arm.make_condition(_bop)));
+ } else {
+ throw Utils.TODO();
+ }
+ }
+
@Override public void asm(CodeGen code, SB sb) {
String src = code.reg(in(1));
if( src!="flags" ) sb.p(src).p(" ");
diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/UJmpARM.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/UJmpARM.java
index e08010f4..c2345a2e 100644
--- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/UJmpARM.java
+++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/UJmpARM.java
@@ -7,7 +7,7 @@ import com.seaofnodes.simple.type.Type;
import java.util.BitSet;
// unconditional jump
-public class UJmpARM extends CFGNode implements MachNode {
+public class UJmpARM extends CFGNode implements MachNode, RIPRelSize {
UJmpARM() { }
@Override public String op() { return "jmp"; }
@Override public String label() { return op(); }
@@ -24,6 +24,22 @@ public class UJmpARM extends CFGNode implements MachNode {
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(0b01010100, 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
diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java
index bff08458..9d8468a8 100644
--- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java
+++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java
@@ -305,7 +305,7 @@ public class arm extends Machine {
short val = enc.reg(xval) ;
int body = n.off() == null
? load_str_imm(0b1111100101, off, ptr, val)
- : indr_adr(0b11111000011, off, arm.STORE_LOAD_OPTION.SXTX, 0, ptr, val);
+ : indr_adr(0b11111000011, off, STORE_LOAD_OPTION.SXTX, 0, ptr, val);
enc.add4(body);
}
@@ -329,26 +329,29 @@ public class arm extends Machine {
return (opcode << 24) | (ftype << 21) | (rm << 16) | (8 << 10) | (rn << 5) | 8;
}
public static void f_cmp(Encoding enc, Node n) {
- short reg1 = (short)(enc.reg(n.in(1))-arm.D_OFFSET);
- short reg2 = (short)(enc.reg(n.in(2))-arm.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_cmp(0b00011110, 3, reg1, reg2);
enc.add4(body);
}
public static COND make_condition(String bop) {
return switch (bop) {
- case "==" -> arm.COND.EQ;
- case "!=" -> arm.COND.NE;
- case "<" -> arm.COND.LE;
- case "<=" -> arm.COND.LT;
- case ">=" -> arm.COND.GT;
- case ">" -> arm.COND.GE;
- default -> throw Utils.TODO();
+ case "==" -> COND.EQ;
+ case "!=" -> COND.NE;
+ case "<" -> COND.LE;
+ case "<=" -> COND.LT;
+ case ">=" -> COND.GT;
+ case ">" -> COND.GE;
+ default -> throw Utils.TODO();
};
}
- // Todo: maybe missing zero here after label << 5
- public static int b_cond(int opcode, int label, COND cond) {
- return (opcode << 24) | (label << 5) | cond.ordinal();
+ // Todo: maybe missing zero here after delta << 5
+ public static int b_cond(int opcode, int delta, COND cond) {
+ // 24-5 == 19bits offset range
+ assert -(1<<19) <= delta && delta < (1<<19);
+ 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) {
@@ -359,8 +362,10 @@ public class arm extends Machine {
public static int blr(int opcode, int rd) {
return opcode << 10 | rd << 5;
}
- public static int b(int opcode, int imm26) {
- return (opcode << 26) | imm26;
+ public static int b(int opcode, int delta) {
+ assert -(1<<26) <= delta && delta < (1<<26);
+ delta &= (1L<<26)-1; // Zero extend
+ return (opcode << 26) | delta;
}
static RegMask callInMask(TypeFunPtr tfp, int idx ) {
diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/BranchRISC.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/BranchRISC.java
index b0283ae9..a4c15c0c 100644
--- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/BranchRISC.java
+++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/BranchRISC.java
@@ -6,7 +6,7 @@ import com.seaofnodes.simple.node.*;
import java.util.BitSet;
// Conditional branch such as: BEQ
-public class BranchRISC extends IfNode implements MachNode {
+public class BranchRISC extends IfNode implements MachNode, RIPRelSize {
String _bop;
// label is obtained implicitly
BranchRISC( IfNode iff, String bop, Node n1, Node n2 ) {
@@ -40,9 +40,27 @@ public class BranchRISC extends IfNode implements MachNode {
// Todo: relocs (for offset - immf)
short src1 = enc.reg(in(1));
short src2 = in(2)==null ? (short)riscv.ZERO : enc.reg(in(2));
- int body = riscv.b_type(0x63, 0, riscv.jumpop(_bop), src1, src2, 0);
- enc.add4(body);
+ enc.add4(riscv.b_type(0x63, riscv.jumpop(_bop), src1, src2, 0));
}
+
+ // Delta is from opcode start
+ @Override public byte encSize(int delta) {
+ if( -4*1024 <= delta && delta < 4*1024 ) 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 ) {
+ 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(0x63, riscv.jumpop(_bop), src1, src2, delta));
+ } else {
+ throw Utils.TODO();
+ }
+ }
+
@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))).p(" ");
CFGNode prj = cproj(0);
diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/UJmpRISC.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/UJmpRISC.java
index cf330ed5..b34e6c06 100644
--- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/UJmpRISC.java
+++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/UJmpRISC.java
@@ -7,7 +7,7 @@ import com.seaofnodes.simple.type.Type;
import java.util.BitSet;
// unconditional jump
-public class UJmpRISC extends CFGNode implements MachNode {
+public class UJmpRISC extends CFGNode implements MachNode, RIPRelSize {
UJmpRISC() { }
@Override public String op() { return "jmp"; }
@Override public String label() { return op(); }
@@ -19,10 +19,26 @@ public class UJmpRISC extends CFGNode implements MachNode {
@Override public Type compute() { throw Utils.TODO(); }
@Override public Node idealize() { throw Utils.TODO(); }
@Override public void encoding( Encoding enc ) {
- // jal
+ // Short form +/-4K: beq r0,r0,imm12
+ // Long form: auipc rX,imm20/32; jal r0,[rX+imm12/32]
enc.jump(this,uctrl());
- int body = riscv.j_type(0b0110111, 0, 0);
- enc.add4(body);
+ enc.add4(riscv.j_type(riscv.J_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.J_JAL, 0, delta));
+ } else {
+ throw Utils.TODO();
+ }
}
@Override public void asm(CodeGen code, SB sb) {
diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java
index aa5de1aa..fbb57421 100644
--- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java
+++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java
@@ -107,6 +107,8 @@ public class riscv extends Machine {
// 0110 0111
public static int I_JALR = 0x67;
+ public static int J_JAL = 0b1101111;
+
// Since riscv instructions are fixed we can just or them togehter
public static int r_type(int opcode, int rd, int func3, int rs1, int rs2, int func7) {
@@ -131,9 +133,22 @@ public class riscv extends Machine {
public static int u_type(int opcode, int rd, int imm20) {
return (imm20 << 12) | (rd << 7) | opcode;
}
- public static int j_type(int opcode, int rd, int imm20) {
- return imm20 << 12 | rd << 7 | opcode;
+
+ public static int j_type(int opcode, int rd, int delta) {
+ assert -(1L<<20) <= delta && delta < (1L<<20);
+ // 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<<20 | imm12_19 << 12 | imm11 << 11 | imm10_01;
+ return bits << 12 | rd << 7 | opcode;
}
+
+
public static int i_type(int opcode, int rd, int func3, int rs1, int imm12) {
assert opcode >= 0 && rd >=0 && func3 >=0 && rs1 >=0 && imm12 >= 0; // Zero-extend by caller
return (imm12 << 20) | (rs1 << 15) | (func3 << 12) | (rd << 7) | opcode;
@@ -154,11 +169,20 @@ public class riscv extends Machine {
return (imm_hi << 25) | (rs2 << 20) | (rs1 << 15) | (func3 << 12) | (imm_lo << 7) | opcode;
}
- // immf = first imm
- // immd = second imm
// BRANCH
- public static int b_type(int opcode, int immf, int func3, int rs1, int rs2, int immd) {
- return (immd << 25 ) | (rs2 << 20) | (rs1 << 15) | (func3 << 12) | (immf << 7) | opcode;
+ public static int b_type(int opcode, int func3, short rs1, short rs2, int delta) {
+ 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) &0x2F;
+ int imm11 = (delta>>11) & 1;
+ int imm12 = (delta>>12) & 1;
+ int imm5 = imm4_1<<4 | imm11;
+ int imm7 = imm12<<7 | imm10_5;
+ return (imm7 << 25 ) | (rs2 << 20) | (rs1 << 15) | (func3 << 12) | (imm5 << 7) | opcode;
}
public enum RM {
diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/JmpX86.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/JmpX86.java
index b307cbd6..667ab012 100644
--- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/JmpX86.java
+++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/JmpX86.java
@@ -36,8 +36,25 @@ public class JmpX86 extends IfNode implements MachNode, RIPRelSize {
//enc.add4(0); // Offset patched later
}
+ // 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 : 6); }
+ // 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] == x86_64_v2.jumpop(_bop)-16;
+ delta -= 2; // Offset from opcode END
+ assert (byte)delta==delta;
+ bits[opStart+1] = (byte)delta;
+ } else {
+ assert bits[opStart ] == 0x0F;
+ assert bits[opStart+1] == x86_64_v2.jumpop(_bop);
+ delta -= 6; // Offset from opcode END
+ enc.patch4(opStart+2,delta);
+ }
+ }
+
@Override public void asm(CodeGen code, SB sb) {
String src = code.reg(in(1));
if( src!="flags" ) sb.p(src).p(" ");
diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/UJmpX86.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/UJmpX86.java
index e77acfa3..143383a6 100644
--- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/UJmpX86.java
+++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/UJmpX86.java
@@ -26,7 +26,23 @@ public class UJmpX86 extends CFGNode implements MachNode, RIPRelSize {
//enc.add1(0xE9).add4(0);
}
- @Override public byte encSize(int delta) { return (byte)(x86_64_v2.imm8(delta) ? 2 : 6); }
+ // 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)0xE9;
+ delta -= 5; // Offset from opcode END
+ enc.patch4(opStart+1,delta);
+ }
+ }
@Override public void asm(CodeGen code, SB sb) {
CFGNode target = uctrl();