From d5b91a513b53c0e69a82f66f350b4e91a86f615d Mon Sep 17 00:00:00 2001 From: Cliff Click Date: Mon, 17 Mar 2025 12:22:33 -0700 Subject: patch branch offsets --- .../com/seaofnodes/simple/codegen/Encoding.java | 120 +++++++++++++++------ .../com/seaofnodes/simple/codegen/RIPRelSize.java | 8 ++ .../seaofnodes/simple/node/cpus/arm/BranchARM.java | 20 +++- .../seaofnodes/simple/node/cpus/arm/UJmpARM.java | 18 +++- .../com/seaofnodes/simple/node/cpus/arm/arm.java | 35 +++--- .../simple/node/cpus/riscv/BranchRISC.java | 24 ++++- .../simple/node/cpus/riscv/UJmpRISC.java | 24 ++++- .../seaofnodes/simple/node/cpus/riscv/riscv.java | 36 +++++-- .../simple/node/cpus/x86_64_v2/JmpX86.java | 17 +++ .../simple/node/cpus/x86_64_v2/UJmpX86.java | 18 +++- 10 files changed, 254 insertions(+), 66 deletions(-) (limited to 'chapter21/src/main/java/com/seaofnodes') 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 _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 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 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(); -- cgit v1.2.3