summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--chapter21/README.md168
-rw-r--r--chapter21/simple0
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/codegen/Encoding.java4
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/TFPARM.java25
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java7
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/TFPRISC.java23
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LoadX86.java37
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/StoreX86.java23
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TFPX86.java12
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java19
10 files changed, 271 insertions, 47 deletions
diff --git a/chapter21/README.md b/chapter21/README.md
index ee62cadd..769633ec 100644
--- a/chapter21/README.md
+++ b/chapter21/README.md
@@ -44,10 +44,13 @@
This chapter will add instruction encodings.
Instruction encoding refers to the representation of
machine instructions in a specific binary format defined by the
-processor's instruction set architecture (ISA)
+processor's instruction set architecture (ISA).
-After we established instruction selection, we need to encode the instructions before we can write them
-into an ELF object file.
+
+After completing instruction selection, the next step is to encode the
+instructions before writing them into an ELF object file.
+
+To avoid ambiguity, we define the sources of the information we use to encode the instructions:
Currently, we support these architectures:
### AMD64:
@@ -56,10 +59,8 @@ For *x86-64(amd64):*, we use the latest Intel manual for the [encoding rules](ht
### RISC-V:
For *riscv* we currently target [RVA23U64](https://msyksphinz-self.github.io/riscv-isadoc/html/).
-- ARM
-Each of which has its own instruction encoding format.
-To avoid ambiguity, we define the sources of the information we use to encode the instructions:
+
### ARMV8/AArch64 :
For *arm*(aarch64) we use this collection for the [encoding rules](https://docsmirror.github.io/A64/2023-06/index.html).
@@ -93,11 +94,11 @@ As of now the bitstream is a simple BAOS stucture.
enc.add4(0); // adds 4 bytes to the bitstream
}
```
-On *riscv* where the manual contains big endian encodings, this function also converts it to little-endian, for other architectures this stays the same.
+On *RISC-V*, where the documentation provides big-endian encodings,
+this function also converts them to little-endian. For other architectures, no conversion is necessary.
## Endianness
-Both *ARM* *RISC-V* and *X86* are **little-endian** *in practice* architectures, which means that the least significant byte is stored first.
-We ensure that the encoding is done in little-endian format:
+ARM, RISC-V, and x86 are all little-endian architectures in practice, meaning the least significant byte is stored first. We ensure that instruction encodings are generated in little-endian format accordingly.
```java
// Little endian write of a 32b opcode
@@ -108,15 +109,15 @@ public void add4( int op ) {
_bits.write(op>>24);
}
```
-For example if we have this 32 bits encoding for fixed width instructions:
+For example, consider a 32-bit encoding for fixed-width instructions:
*10110101100111101011001001101101*.
We append the least significant byte first, then the next byte, and so on.
```
_bits.write(op );
```
-`_bits.write()` only appeds the least significant byte, by shifting it to the right we can manipulate it
-in a way so that the next byte becomes the least significant byte, and so on.
+
+`_bits.write()` only appends the least significant byte. By shifting the value to the right, we can bring the next byte into the least significant position, allowing us to write each byte in sequence.
## AMD64
As opposed to riscv arhitectures, where the instruction width is fixed, *x86-64* has variable width instructions.
@@ -124,8 +125,7 @@ This is common with *CISC(Complex instruction set computer)* architectures, wher
Since AMD64 supports many indirect addressing modes, the goal with CISC in general is to complete a task in as few lines of assembly as possible.
### REX PREFIX
-Since we target the 64 bit version of x86, we have to deal with this
-prefix, otherwise its negligible in 32 bit mode.
+Since we are targeting the 64-bit version of x86, we need to handle this prefix. In 32-bit mode, however, it is typically unnecessary.
Generally speaking the *REX* prefix must be encoded when:
- using one of the extended registers (R8 to R15, XMM8 to XMM15, YMM8 to YMM15, CR8 to CR15 and DR8 to DR15);
@@ -134,7 +134,8 @@ Generally speaking the *REX* prefix must be encoded when:
A *REX* prefix must not be encoded when:
- using one of the high byte registers AH, CH, BH or DH.
-In all other cases, iti s ignored.
+*Note:* When encoding SSE instruction, the *REX* prefix(0x40) must come after the prefix.
+In all other cases, it is ignored.
In Simple the REX prefix is just appneded to the beginning of the bit stream.
The layout is the following:
@@ -285,7 +286,7 @@ if( x86_64_v2.imm8(_imm) ) enc.add1(_imm);
else enc.add4(_imm);
```
-#### Indirect
+#### INDIRECT(MemOp)
TBD
#### Float
@@ -318,6 +319,7 @@ We see that `XMM0` corresponds to `RAX` and so on.
#### Function constant
To load a function constant we use lea which will load the address into the specified reg
relative to the instruction pointer.
+`lea rax, [rip+disp32]`
#### Conditional flags:
The FLAGS register is the status register that contains the current state of an x86 CPU.
@@ -359,8 +361,7 @@ And then a conditional branch that is relying on the flags set by the comparison
jne 1159 <square(int)+0x29>
```
We'll see how this differs in ARM and RISCV.
----
-
+---
## ARM
Arm instructions are all 32 bits wide, and are always little-endian.
@@ -420,7 +421,7 @@ After subbing in the values we get:
> imms = (64 - _imm) - 1;
-Hence the `imms` value is `(64 - _imm) - 1`.
+Hence, the `imms` value is `(64 - _imm) - 1`.
#### LSR(immediate)
Same as *ASR*.
@@ -487,7 +488,7 @@ int body = arm.load_pc(0b01011100, 0, dst);
#### Large constants
-##### INT:
+##### INT: We can use different combinations of movs(movz, movk, movn) - this allows us to avoid PC relative loading.
##### FLOAT: We use the *LDR (immediate, SIMD&FP)* instruction relative to the
@@ -522,9 +523,18 @@ operands and also sets the flags.
Later on `b.ne` relies on this flag again when it attempts to conditionally execute a branch.
-#### Indirect
-TBD
+#### INDIRECT(MemOp)
+Arm supports the following indirect forms:
+
+[Load](https://docsmirror.github.io/A64/2023-06/ldr_reg_gen.html): `ldr dst, [base + index]` where both, base and index are registers.
+
+[Load](https://docsmirror.github.io/A64/2023-06/ldr_imm_gen.html): `ldr dst, [base + #offset]` where base is a register and the offset is an immediate.
+
+[Store](https://docsmirror.github.io/A64/2023-06/str_reg_gen.html): `str dst, [base + index]`
+[Store](https://docsmirror.github.io/A64/2023-06/strb_imm.html): `str dst, [base + #offset]`
+
+If the offset is not a con
#### Branching
`B.cond` - branch [conditionally](https://developer.arm.com/documentation/dui0802/b/A32-and-T32-Instructions/Condition-codes?lang=en) to a label at a PC-relative offset.
@@ -533,37 +543,123 @@ TBD
---
## RISCV
+RISCV instructions are all 32 bits wide, and are always little-endian.
+RISCV is a RISC architecture, it prioritizes "register-to-register" forms.
-####
#### Instruction formats
-#### FLOAT
+##### R-TYPE
+This encoding format is used for reg-to-reg operations.
+Such as `AddRISC`, `MulRISC` etc.
+```java
+public static int r_type(int opcode, int rd, int func3, int rs1, int rs2, int func7) {
+ return (func7 << 25) | (rs2 << 20) | (rs1 << 15) | (func3 << 12) | (rd << 7) | opcode;
+}
+```
-#### INDIRECT(MemOp)
+##### I-TYPE
+This layout is used for immediate formats. Such as `AddIRISC`
-#### BRANCH
+```java
+ 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;
+ }
+```
+##### S-TYPE
-#### Large constants
+This encoding format is used for `StoreRISC`.
-##### FLOAT: HANDLED ALREADY
-##### INT: HANDLED ALREADY
+```java
+public static int s_type(int opcode, int func3, int rs1, int rs2, int imm12) {
+ 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;
+}
+```
-#### Function Constant
+##### B-TYPE
-#### LUI
-Constant hacking from cpp discord.
+This encoding layout is used by `BranchRISC`.
+```java
+ // 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;
+ }
+```
+##### U-TYPE
+This layout is used for `AUIPC` and `LUI`(upper immediate is 20-bits).
+```java
+ public static int u_type(int opcode, int rd, int imm20) {
+ return (imm20 << 12) | (rd << 7) | opcode;
+ }
+```
+##### J-TYPE
+Used for unconditional jumps. (`UJmpRISC`)
+```java
+ public static int j_type(int opcode, int rd, int imm20) {
+ return imm20 << 12 | rd << 7 | opcode;
+ }
+```
+#### FLOAT
+For loading float constants, we use indirect memory load(PC-relative).
+```
+AUIPC dst,#hi20_constant_pool
+Load dst,[dst+#low12_constant_pool]
+```
-## Relocation
+#### INDIRECT(MemOp)
+
+[Load](https://msyksphinz-self.github.io/riscv-isadoc/html/rvi.html#lw): `lw rd,offset(rs1)`
-### Lots of Bits
+[Store](https://msyksphinz-self.github.io/riscv-isadoc/html/rvi.html#sw): `sw rs2,offset(rs1)`
+#### BRANCH
+RISC-V is unusual because branch instructions include the comparison and branch target in one instruction.
+
+#### Immediates
+We only encode the imm form for the *ALU* operations if they fit into 12 bits.(signed)
+Otherwise we do an extra load.
+(encode it in instruction)
+```
+ addiw a5,a5,123
+```
+vs
+(do it with extra load):
+```
+addw a5,a5,a4
+```
+
+### FLOAT Constants
+- Stored in the **constant pool**.
+- Accessed via **PC-relative loads**.
+### INT Constants
+- **12-bit constants**
+ - Use `ADDI`
+- **20-bit constants**
+ - Use `LUI`
+- **32-bit constants**
+ - Use `LUI` + `ADDI` combo
+ -
+
+#### Function Constant
+In our RISC-V code, when we need to load a function-local constant,
+we store the current PC into a register and use that as a base to access the constant. However, we usually try to avoid loads, since simple arithmetic like addition is faster and more efficient than accessing memory.
+
+```java
+// auipc t0,0
+// addi t1,t0 + #0
+```
+## Relocation
-[^1]: [Intel](smt here)
+## Local paches: TBD
-[^2]: ()
diff --git a/chapter21/simple b/chapter21/simple
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/chapter21/simple
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 67eeb3c2..0162bc84 100644
--- a/chapter21/src/main/java/com/seaofnodes/simple/codegen/Encoding.java
+++ b/chapter21/src/main/java/com/seaofnodes/simple/codegen/Encoding.java
@@ -53,6 +53,10 @@ public class Encoding {
public Encoding add1( int op ) { _bits.write(op); return this; }
+ public void add2( int op ) {
+ _bits.write(op );
+ _bits.write(op>> 8);
+ }
// Little endian write of a 32b opcode
public void add4( int op ) {
_bits.write(op );
diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/TFPARM.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/TFPARM.java
index 0ba47317..f42dae75 100644
--- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/TFPARM.java
+++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/TFPARM.java
@@ -4,9 +4,10 @@ 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.node.cpus.riscv.riscv;
import com.seaofnodes.simple.type.TypeFunPtr;
-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"; }
@Override public RegMask regmap(int i) { return null; }
@@ -22,6 +23,28 @@ public class TFPARM extends ConstantNode implements MachNode {
arm.imm_inst(enc,this,0b1001000100,0);
enc.add4(adrp);
}
+
+ @Override public byte encSize(int delta) {
+ if( -(1L<<12) <= delta && delta < (1L<<12) ) return 4;
+ throw Utils.TODO();
+ }
+
+ // Delta is from opcode start
+ @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) {
+ short rpc = enc.reg(this);
+ if(opLen ==4 ) {
+ // 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(enc, this, 0b1001000100, delta & 0xfff));
+ } else {
+ 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/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 b35f600a..4b0781f2 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
@@ -224,6 +224,13 @@ public class arm extends Machine {
enc.add4(body);
}
+ 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) {
diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/TFPRISC.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/TFPRISC.java
index 873a07ce..6cb219df 100644
--- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/TFPRISC.java
+++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/TFPRISC.java
@@ -6,7 +6,7 @@ import com.seaofnodes.simple.node.ConstantNode;
import com.seaofnodes.simple.node.MachNode;
import com.seaofnodes.simple.type.TypeFunPtr;
-public class TFPRISC extends ConstantNode implements MachNode {
+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; }
@@ -26,6 +26,27 @@ public class TFPRISC extends ConstantNode implements MachNode {
enc.add4(addi);
}
+ @Override public byte encSize(int delta) {
+ if( -(1L<<11) <= delta && delta < (1L<<11) ) return 4;
+ throw Utils.TODO();
+ }
+
+ // Delta is from opcode start
+ @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) {
+ short rpc = enc.reg(this);
+ if( opLen==4 ) {
+ // AUIPC (upper 20 bits)
+ // opstart of add
+ int next = opStart + opLen;
+ enc.patch4(opStart,riscv.u_type(0b0010111, rpc, delta));
+ // addi(low 12 bits)
+ enc.patch4(next,riscv.i_type(0b0010011, rpc, 0, rpc, delta & 0xFFF));
+ // addi
+ } else {
+ throw Utils.TODO();
+ }
+ }
+
@Override public void asm(CodeGen code, SB sb) {
_con.print(sb.p(code.reg(this)).p(" #"));
}
diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LoadX86.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LoadX86.java
index 39525ed1..6dd3e3dc 100644
--- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LoadX86.java
+++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LoadX86.java
@@ -4,6 +4,8 @@ import com.seaofnodes.simple.SB;
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.TypeInteger;
public class LoadX86 extends MemOpX86 {
LoadX86( LoadNode ld, Node base, Node idx, int off, int scale ) {
@@ -13,11 +15,42 @@ public class LoadX86 extends MemOpX86 {
@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.add1(x86_64_v2.rex(dst, ptr, idx));
- enc.add1(0x8B); // opcode
+ if (_declaredType != TypeInteger.U32) {
+ enc.add1(x86_64_v2.rex(dst, ptr, idx));
+ }
+
+ if(_declaredType == TypeInteger.I8) {
+ // sign extend: REX.W + 0F BE /r MOVSX r64, r/m8
+ enc.add1(0x0F);
+ enc.add1(0xBE);
+ } else if(_declaredType == TypeInteger.I16) {
+ // sign extend: REX.W + 0F BF /r MOVSX r64, r/m16
+ enc.add1(0x0F);
+ enc.add1(0xBF);
+ } else if(_declaredType == TypeInteger.I32) {
+ // sign extend: REX.W + 63 /r MOVSXD r64, r/m32
+ enc.add1(0x63);
+ } else if(_declaredType == TypeInteger.U8) {
+ // zero extend: REX.W + 0F B6 /r MOVZX r64, r/m8
+ enc.add1(0x0F);
+ enc.add1(0xB6);
+ } else if(_declaredType == TypeInteger.U16) {
+ // zero extend: REX.W + 0F B7 /r MOVZX r64, r/m16
+ enc.add1(0x0F);
+ enc.add1(0xB7);
+ } else if(_declaredType == TypeInteger.U32) {
+ // zero extend: 8B /r MOV r32, r/m32
+ enc.add1(0x8B);
+ } else if(_declaredType == TypeInteger.BOT) {
+ // REX.W + 8B /r MOV r64, r/m64
+ enc.add1(0x8B);
+ }
// includes modrm internally
x86_64_v2.indirectAdr(_scale, idx, ptr, _off, dst, enc);
diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/StoreX86.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/StoreX86.java
index 4a54663d..8c2e0612 100644
--- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/StoreX86.java
+++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/StoreX86.java
@@ -2,6 +2,7 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.codegen.*;
+import com.seaofnodes.simple.node.ConstantNode;
import com.seaofnodes.simple.node.Node;
import com.seaofnodes.simple.node.StoreNode;
import java.util.BitSet;
@@ -27,13 +28,27 @@ public class StoreX86 extends MemOpX86 {
short idx = enc.reg(idx());
short src = enc.reg(val());
- enc.add1(x86_64_v2.rex(src, ptr, idx));
+ int imm_op = x86_64_v2.selectOpcodeForImmStore(_imm);
+ if(src == -1 && _imm != 0) {
+ if(imm_op == -1) {enc.add1(x86_64_v2.rex(src, ptr, idx));enc.add1(0xC7); }
+ else enc.add1(imm_op);
+ } else {
- // switch on opcode depending on instruction
- enc.add1( src == -1 ? 0xC7 : 0x89 );
+ if(_declaredType.log_size() == 0) enc.add1(0x88);
+ if(_declaredType.log_size() == 1) enc.add1(0x89);
+ if(_declaredType.log_size() == 2) enc.add1(0x89);
+ if(_declaredType.log_size() == 3) {enc.add1(x86_64_v2.rex(src, ptr, idx)); enc.add1(0x89);}
+ }
x86_64_v2.indirectAdr(_scale, idx, ptr, _off, src, enc);
- if( src == -1 ) enc.add4(_imm);
+ if( src == -1 ) {
+ switch (x86_64_v2.imm_size(_imm)) {
+ case 8: enc.add1(_imm); break;
+ case 16: enc.add2(_imm); break;
+ case 32: enc.add4(_imm); break;
+ case 64: enc.add8(_imm); break;
+ }
+ }
}
// General form: "stN [base + idx<<2 + 12],val"
diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TFPX86.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TFPX86.java
index 13b9f8a3..be4d1a83 100644
--- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TFPX86.java
+++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TFPX86.java
@@ -3,11 +3,12 @@ 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.node.cpus.riscv.riscv;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeFunPtr;
// 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";
@@ -42,6 +43,15 @@ public class TFPX86 extends ConstantNode implements MachNode {
enc.add4(0);
}
+ @Override public byte encSize(int delta) {
+ return 7;
+ }
+
+ // 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,
// indentation, leading address or block labels not printed here.
// Just something like "ld4\tR17=[R18+12] // Load array base".
diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java
index 9d7b1a26..1d30bc4b 100644
--- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java
+++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java
@@ -109,6 +109,20 @@ public class x86_64_v2 extends Machine {
if( 8 <= idx && idx <= 15 ) rex |= 0b00000010; // REX.X
return rex;
}
+ // return opcode for optimised immediate store
+ public static int selectOpcodeForImmStore(long imm) {
+ if(imm8(imm)) return 0xC6;
+ if(imm16(imm)) return 0xC7;
+ if(imm32(imm)) return 0xC7;
+ return -1;
+ }
+ // return the size of the immediate
+ public static int imm_size(long imm) {
+ if(imm8(imm)) return 8;
+ if(imm16(imm)) return 16;
+ if(imm32(imm)) return 32;
+ return 64;
+ }
public static int rex(int reg, int ptr, int idx) {
return rex(reg, ptr, idx, true);
@@ -410,16 +424,17 @@ public class x86_64_v2 extends Machine {
}
public static boolean imm8( long imm ) { return -128 <= imm && imm <= 127; }
+ public static boolean imm16(long imm) {return -32768 <= imm && imm <= 32767; }
public static boolean imm32( long imm ) { return (int)imm==imm; }
// Attempt a full LEA-style break down.
private Node add(AddNode add) {
Node lhs = add.in(1);
Node rhs = add.in(2);
- if( lhs instanceof LoadNode ld && ld.nOuts() == 1 )
+ if( lhs instanceof LoadNode ld && ld.nOuts() == 1 && ld._declaredType.log_size() >= 3)
return new AddMemX86(add, address(ld), ld.ptr(), idx, off, scale, imm(rhs), val);
- if(rhs instanceof LoadNode ld && ld.nOuts() == 1)
+ 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.