summaryrefslogtreecommitdiff
path: root/chapter21
diff options
context:
space:
mode:
Diffstat (limited to 'chapter21')
-rw-r--r--chapter21/README.md520
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/IntARM.java37
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/UJmpARM.java3
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java2
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/UJmpRISC.java4
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java4
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/IntX86.java17
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TFPX86.java14
8 files changed, 576 insertions, 25 deletions
diff --git a/chapter21/README.md b/chapter21/README.md
index a4efb00d..ee62cadd 100644
--- a/chapter21/README.md
+++ b/chapter21/README.md
@@ -4,9 +4,42 @@
# Table of Contents
1. [Instruction Encodings](#instruction-encodings)
-2. [AMD64](#amd64)
-3. [ARM](#arm)
-4. [RISCV](#riscv)
+2. [Infrastructure](#infrastructure)
+3. [AMD64](#amd64)
+ - [REX PREFIX](#rex-prefix)
+ - [LARGE CONSTANT](#large-constants)
+ - [MODR/M](#modrm)
+ - [SIB](#sib)
+ - [Displacement](#displacement)
+ - [Immediate](#immediate)
+ - [Float](#float)
+ - [Indirect](#indirect)
+ - [Conditional flags](#conditional-flags)
+4. [ARM](#arm)
+ - [Reg-form](#reg-form)
+ - [Shift immediates](#shift-immediates)
+ - [asri](#asrimmediate)
+ - [LSL(immediate)](#lslimmediate)
+ - [LSR(immediate)](#lsrimmediate)
+
+ - [Logical immediates](#logical-immediates)
+ - [Float](#float)
+ - [Function constant](#function-constant)
+ - [Immediate](#immediate)
+ - [Conditional flags](#conditional-flags)
+ - [Indirect](#indirect)
+ - [Branching](#branching)
+ - [Rip relative](#rip-relative)
+ - [Compare](#compare)
+
+5. [RISCV](#riscv)
+ - [Instruction formats](#instruction-formats)
+ - [FLOAT](#float)
+ - [INDIRECT(MemOp)](#indirectmemop)
+ - [BRANCH](#branch)
+ - [Function Constant](#function-constant)
+ - [LUI](#lui)
+ - [Large constants](#large-constants)
This chapter will add instruction encodings.
Instruction encoding refers to the representation of
@@ -33,27 +66,504 @@ For *arm*(aarch64) we use this collection for the [encoding rules](https://docsm
*Note*: We only support 64 bits ARM encodings.
---
+
## Instruction Encodings
+## Infrastructure
+
+The instruction selection phase creates the machine friendly nodes from ideal nodes.
+These machine friendly nodes define an *encoding* function that is called from the Encoding driver found in CodeGen.
+
+```java
+for( CFGNode bb : _code._cfg )
+ for( Node n : bb._outputs )
+ if( n instanceof MachNode mach ) {
+ int off = _bits.size();
+ mach.encoding(this);
+ _opLen[n._nid] = (byte)(_bits.size()-off);
+ }
+```
+Based on the selected target, we either need to look out for little endian or big endian encodings.
+These are being handled by `add4` which adds to the bit stream in the correct order.
+As of now the bitstream is a simple BAOS stucture.
+
+```java
+@Override public void encoding( Encoding enc ) {
+ 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.
+
+## 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:
+
+```java
+// Little endian write of a 32b opcode
+public void add4( int op ) {
+ _bits.write(op );
+ _bits.write(op>> 8);
+ _bits.write(op>>16);
+ _bits.write(op>>24);
+}
+```
+For example if we have this 32 bits 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.
+
## AMD64
+As opposed to riscv arhitectures, where the instruction width is fixed, *x86-64* has variable width instructions.
+This is common with *CISC(Complex instruction set computer)* architectures, where the instruction width can vary from 1 to 15 bytes.
+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.
+
+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);
+ - using 64-bit operand size and the instruction does not default to 64-bit operand size(most ALU ops)
+
+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.
-#### REX PREFIX
+In Simple the REX prefix is just appneded to the beginning of the bit stream.
+The layout is the following:
+
+| **Field** | **Length** | **Description** |
+|:---------:|:----------:|---------------------------------------------------------------------------------|
+| `0100` | 4 bits | Fixed bit pattern |
+| `W` | 1 bit | When 1, a 64-bit operand size is used. Otherwise, 32-bit operand size is used. |
+| `R` | 1 bit | Extension to the **MODRM.reg** field. |
+| `X` | 1 bit | Extension to the **SIB.index** field. |
+| `B` | 1 bit | Extension to the **MODRM.rm** or **SIB.base** field. |
+
+
+```java
+ public static int REX_W = 0x48;
+
+ ...
+```
+```java
+public static int rex(int reg, int ptr, int idx) {
+ // assuming 64 bit by default so: 0100 1000
+ int rex = REX_W; // Default REX.W
+ if( 8 <= reg && reg <= 15 ) rex |= 0b00000100; // REX.R
+ if( 8 <= ptr && ptr <= 15 ) rex |= 0b00000001; // REX.B
+ if( 8 <= idx && idx <= 15 ) rex |= 0b00000010; // REX.X
+ return rex;
+
+ ...
+ enc.add1(x86_64_v2.rex(dst, src, 0));
+}
+```
+
+Setting THE `W` bit gives us:
+01001000 = 0x48;
+
+#### Opcode
+In our case, the opcode is a 1 byte field that specifies the operation to be performed.
+```
+enc.add1(opcode()); // opcode
+```
#### MODR/M
+
+| **Field** | **Length** | **Description** |
+|:-------------:|:----------:|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| **MODRM.mod** | 2 bits | In general, when this field is `b11`, then register-direct addressing mode is used; otherwise register-indirect addressing mode is used. |
+| **MODRM.reg** | 3 bits | This field can have one of two values:<br><br>• A 3-bit opcode extension, which is used by some instructions but has no further meaning other than distinguishing the instruction from other instructions.<br><br>• A 3-bit register reference, which can be used as the source or the destination of an instruction (depending on the instruction). The referenced register depends on the operand-size of the instruction and the instruction itself. The `REX.R`, `VEX.~R` or `XOP.~R` field can extend this field with 1 most-significant bit to 4 bits total. |
+| **MODRM.rm** | 3 bits | Specifies a direct or indirect register operand, optionally with a displacement. The `REX.B`, `VEX.~B` or `XOP.~B` field can extend this field with 1 most-significant bit to 4 bits total. |
+
+
+We deal with the MODR/M byte in a similar way:
+
+The first thing we need for the modrm byte is the 2 bits mod.
+
+##### mod
+The layout for mod is the following:
+```
+public enum MOD {
+INDIRECT, // [mem]
+INDIRECT_disp8, // [mem + 0x12]
+INDIRECT_disp32,// [mem + 0x12345678]
+DIRECT, // mem
+};
+```
+
+##### reg
+The reg field is a 3 bit field capable of encoding 15 different registers.
+
+##### rm
+The rm field specifies a direct or indirect register operand and is capable of encoding 15 different registers.
+
+
+```java
+public static int modrm(MOD mod, int reg, int m_r) {
+ // combine all the bits
+ return (mod.ordinal() << 6) | ((reg & 0x07) << 3) | m_r & 0x07;
+}
+```
+The `modrm` function is then used to build up the `ModR/M` byte.
+Usually this follows after the opcode.
+
+```
+enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, src));
+```
+
+Encoding a reg form might look like this:
+```
+short dst = enc.reg(this ); // 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));
+```
+
#### SIB
+The SIB byte has the following fields:
+
+| **Field** | **Length** | **Description** |
+|:-------------:|:----------:|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| **SIB.scale** | 2 bits | This field indicates the scaling factor of `SIB.index`, where **s** (as used in the tables) equals 2^SIB.scale^. |
+| **SIB.index** | 3 bits | The index register to use. See Registers for the values to use for each of the registers. The `REX.X`, `VEX.~X` or `XOP.~X` field can extend this field with 1 most-significant bit to 4 bits total. |
+| **SIB.base** | 3 bits | The base register to use. See Registers for the values to use for each of the registers. The `REX.B`, `VEX.~B` or `XOP.~B` field can extend this field with 1 most-significant bit to 4 bits total. |
+
+It has the same layout as the MODR/M byte.
+```java
+ public static int sib(int scale, int index, int base) {
+ return (scale << 6) | ((index & 0x07) << 3) | base & 0x07;
+ }
+```
+
+```
+ enc.add1(x86_64_v2.sib(_scale, idx, x86_64_v2.RBP));
+```
+
#### Displacement
+Displacement can happen in 2 forms:
+```java
+public enum MOD {
+INDIRECT, // [mem]
+INDIRECT_disp8, // [mem + 0x12]
+INDIRECT_disp32,// [mem + 0x12345678]
+DIRECT, // mem
+};
+```
+
+```java
+if( mod == MOD.INDIRECT_disp8 ) {
+ enc.add1(offset);
+} else if( mod == MOD.INDIRECT_disp32 ) {
+ enc.add4(offset);
+}
+```
+The displacement goes before the immediate, so for example:
+```
+sub [eax + 2], 4
+```
+```
+bytes.write(0x83);
+bytes.write(modrm(INDIRECT8, 5, 0 /* eax */));
+bytes.write(2); // displcement goes first
+bytes.write(4);
+```
+
#### Immediate
+The immediate byte/bytes go after the `ModR/M` byte, we are capable of encoding 4 bytes of immediates depending on the need.
+```java
+// immediate(4 bytes) 32 bits or (1 byte)8 bits
+if( x86_64_v2.imm8(_imm) ) enc.add1(_imm);
+else enc.add4(_imm);
+```
+
#### Indirect
+TBD
+
+#### Float
+For float operations we use `SEE SIMD` instructions that operate on scalar single precision floating0-point values located in the
+XMM registers or memory.
+*Note*: When using XMM registers for ALU operations, the encoding is really similar with the exception that we need
+to subtract the `XMM_OFFSET` from the register number.
+We count the registers continuously, so we need to subtract the offset to get the GPR register pair that corresponds to the XMM register.
+
+```java
+short dst = (short)(enc.reg(this ) - x86_64_v2.XMM_OFFSET);
+```
+
+```java
+public static int XMM_OFFSET = 16;
+```
+
+| **X.Reg** | **8-bit GP** | **16-bit GP** | **32-bit GP** | **64-bit GP** | **80-bit x87** | **64-bit MMX** | **128-bit XMM** | **256-bit YMM** | **16-bit Segment** | **32-bit Control** | **32-bit Debug** |
+|:--------:|:------------:|:-------------:|:-------------:|:-------------:|:-------------:|:-------------:|:--------------:|:--------------:|:-----------------:|:-----------------:|:---------------:|
+| `0.000 (0)` | AL | AX | EAX | RAX | ST0 | MMX0 | XMM0 | YMM0 | ES | CR0 | DR0 |
+
+We see that `XMM0` corresponds to `RAX` and so on.
+
+#### Large constants
+
+##### INT: We just use `MOV r64, imm64` instruction form.
+
+##### FLOAT: We use relocation and use constant from memory.
+
+#### Function constant
+To load a function constant we use lea which will load the address into the specified reg
+relative to the instruction pointer.
+
+#### Conditional flags:
+The FLAGS register is the status register that contains the current state of an x86 CPU.
+
+Let's consider a simple example that presents all the instructions that we need to encode for
+conditional code.
+
+```
+int square(int a) {
+ // cmp DWORD PTR [rbp-0x8],0x1
+ // `sete al`
+ bool da = a == 1;
+
+ // cmp eax,0x1
+ if(da == 1) {
+ return 1;
+ }
+ return 2;
+}
+```
+First we need a compare instruction comparing `a` with 1.
+```
+cmp DWORD PTR [rbp-0x8],0x1
+```
+
+This compare instruction then sets the conditional flags:
+> The CF, OF, SF, ZF, AF, and PF flags are set according to the result.
+
+We then use the `sete` instruction to save out the result of the comparison to a register:
+
+`sete al`
+
+For the if condition we do an extra comparison based on the result of the first bool.
+```
+ cmp eax,0x1
+```
+And then a conditional branch that is relying on the flags set by the comparison(`a == 1`).
+```
+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.
+Arm is a RISC architecture, it prioritizes "register-to-register" forms.
+
+#### Reg-form
+We encode the regs forms as [shifted registers](https://docsmirror.github.io/A64/2023-06/orr_log_shift.html).
+```
+orr reg1, reg2
+```
+```
+x0 = x0 | (x1 << 0)
+```
+The same thing applies for all the other reg forms.
+Because of this similarity, we can lift the encoding of the reg form to a common function.
+
+```java
+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);
+enc.add4(body);
+```
+*imm6* and
+*shift*
+are irelevant for the reg form, so we just set them to 0.
+
+### Shift immediates
+This refers to: *asri, lsli, lsri*
+Encode to shift immediates if the shift amount is between [0, 63] inclusive.
+
+#### ASR(immediate)
+For arithmetic Shift Right immediates, we set *immr* to the immediate we wish to encode, and imms to `63` = `111111`.
+This is following the rules of [asri encoding](https://docsmirror.github.io/A64/2023-06/asr_sbfm.html).
+
+The `sf` bit is included in the opcode, `N` is implicitly added to the bitstream after the execution of the
+`imm_shift` function.
+```java
+enc.add4(arm.imm_shift(0b100100110,_imm, 0b111111, rn, rd));
+```
+
+#### LSL(immediate)
+
+```java
+// UBFM <Xd>, <Xn>, #(-<shift> MOD 64), #(63-<shift>)
+// immr must be (-<shift> MOD 64) = 64 - shift
+enc.add4(arm.imm_shift(0b110100110, 64 - _imm, (64 - _imm) - 1, rn, rd));
+```
+
+We know that `_imm` is never a negative number and is between 0 and 63, so we can safely use `64 - _imm` as the `immr` value.
+We take away this form of the immediate from 64 to invert the bits.
+
+For *imms* - the following condition must hold *imms + 1 = immr*;
+After subbing in the values we get:
+
+> imms + 1 = 64 - _imm;
+
+> imms = (64 - _imm) - 1;
+
+Hence the `imms` value is `(64 - _imm) - 1`.
+
+#### LSR(immediate)
+Same as *ASR*.
+
+
+#### Logical immediates
+Logical immediates refer to *andi, xori, orri*.
+
+```java
+// 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
+ while (val < 0 || (val & 1)==0) {
+ val = (val >>> 63) | (val << 1);
+ immr++;
+ }
+ 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;
+}
+```
+
+imm12Logical returns the 12-bit immediate encoding for logical operations if it is possible to encode it, otherwise -1.
+```java
+int imm12;
+return and.in(2) instanceof ConstantNode off &&
+off._con instanceof TypeInteger ti && (imm12 = imm12Logical(ti)) != -1
+? new AndIARM(and, imm12)
+```
+```java
+arm.imm_inst(enc,this,0b100100100,_imm);
+```
+
+#### Float
+Similarly to x86, we have to subtract the offset to get the GPR register pair that corresponds to the *D* register.
+```
+short dst = (short)(enc.reg(this ) - arm.D_OFFSET);
+```
+For loading float constants we use the `ldr(literal)` instruction.
+The offset is added to the PC register to get the address of the literal.
+
+```java
+// Any number that can be expressed as +/-n * 2-r,where n and r are integers, 16 <= n <= 31, 0 <= r <= 7.
+int body = arm.load_pc(0b01011100, 0, dst);
+```
+
+#### Large constants
+
+##### INT:
+
+##### FLOAT: We use the *LDR (immediate, SIMD&FP)* instruction relative to the
+
+
+#### Function constant
+To load a function constant we store the current PC into x0 and use that to access the constant.
+Generally, we try to avoid load as adding is faster.
+```
+adrp x0, 0
+add x0, x0, 0
+```
+
+#### Conditional flags
+In arm we set the conditional flags after a comparison -
+this works similarly to x86.
+
+```
+int square(int a) {
+// subs
+// cset w8, eq // eq = none
+bool da = a == 1;
+ // bne
+ if(da == 1) {
+ return 1;
+ }
+ return 2;
+}
+```
+In arm comparisons are done with `subss` which does a subtraction between the two
+operands and also sets the flags.
+`CSET` is used to look up the flags and save it out to a register.
+
+Later on `b.ne` relies on this flag again when it attempts to conditionally execute a branch.
+
+#### Indirect
+TBD
+
+#### 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.
+
+> cond = is one of the standard conditions.
+
+---
## RISCV
+####
+#### Instruction formats
+
+#### FLOAT
+
+#### INDIRECT(MemOp)
+
+#### BRANCH
+
+
+#### Large constants
+
+##### FLOAT: HANDLED ALREADY
+##### INT: HANDLED ALREADY
+
+#### Function Constant
+
+#### LUI
+Constant hacking from cpp discord.
+
+
+
+
+
+## Relocation
+
### Lots of Bits
+
[^1]: [Intel](smt here)
[^2]: ()
diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/IntARM.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/IntARM.java
index f9b53f6b..9f9c6db6 100644
--- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/IntARM.java
+++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/IntARM.java
@@ -20,10 +20,39 @@ public class IntARM extends ConstantNode implements MachNode {
@Override public void encoding( Encoding enc ) {
short self = enc.reg(this);
long x = ((TypeInteger)_con).value();
- if( (short)x == x )
- enc.add4(arm.mov(485, 0, (int)(x&0xFFFF), self));
- else
- throw Utils.TODO("Handle cases bigger than 16 bits");
+ 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 = 0b110100101;
+ } else {
+ // More F blocks then 0 blocks, use movn
+ pattern = 0xFFFF;
+ op = 0b100100101;
+ }
+ 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 = 0b111100101;
+ invert = 0;
+ }
+ }
+ if (op != 0b111100101) {
+ // 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/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 cc0b2085..e08010f4 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
@@ -20,8 +20,7 @@ public class UJmpARM extends CFGNode implements MachNode {
@Override public Node idealize() { throw Utils.TODO(); }
@Override public void encoding( Encoding enc ) {
enc.jump(this,uctrl());
- // TODO: unconditional jump
- int body = arm.b_cond(0b01010100, 0, arm.make_condition("<"));
+ int body = arm.b(0b01010100, 0);
enc.add4(body);
}
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 c6bb1973..bff08458 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
@@ -252,7 +252,7 @@ public class arm extends Machine {
enc.add4(body);
}
- // encodes movk
+ // encodes movk, movn, and movz
public static int mov(int opcode, int shift, int imm16, int rd) {
return (opcode << 23) | (shift << 21) | (imm16 << 5) | rd;
}
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 bcffa544..cf330ed5 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
@@ -19,9 +19,9 @@ 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
enc.jump(this,uctrl());
- // TODO: Unconditional jump
- int body = riscv.b_type(0x63, 0, riscv.jumpop("<"), 0, 0, 0);
+ int body = riscv.j_type(0b0110111, 0, 0);
enc.add4(body);
}
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 08239288..aa5de1aa 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
@@ -131,7 +131,9 @@ 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 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;
diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/IntX86.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/IntX86.java
index d9836e00..6d9110f2 100644
--- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/IntX86.java
+++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/IntX86.java
@@ -36,14 +36,27 @@ public class IntX86 extends ConstantNode implements MachNode {
// Simply move the constant into a GPR
// Conditional encoding based on 64 or 32 bits
//REX.W + C7 /0 id MOV r/m64, imm32
- enc.add1(x86_64_v2.rex(0, dst, 0));
long imm = ((TypeInteger)_con).value();
- if( (int)imm == imm ) {
+ 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
}
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 4167962b..a80531b6 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
@@ -33,15 +33,13 @@ public class TFPX86 extends ConstantNode implements MachNode {
return;
}
+ // lea to load function pointer address
+ // lea rax, [rip+disp32]
enc.relo(this,(TypeFunPtr)_con);
- // Simply move the constant into a GPR
- // Conditional encoding based on 64 or 32 bits
- //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);
- throw Utils.TODO();
+ 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);
}
// Human-readable form appended to the SB. Things like the encoding,