diff options
Diffstat (limited to 'chapter21')
| -rw-r--r-- | chapter21/README.md | 88 |
1 files changed, 57 insertions, 31 deletions
diff --git a/chapter21/README.md b/chapter21/README.md index 7d08036c..58969581 100644 --- a/chapter21/README.md +++ b/chapter21/README.md @@ -1,5 +1,18 @@ # Chapter 21: Instruction Encoding and ELF +## Meta-Issues / Code-Dev Issues of Instruction Selection, Register Allocation and Encodings + +The output of Encodings - conversion of machine Nodes to instruction bits - is +difficult to test in bulk without an execution strategy. We won't have an +execution stratgy until all three of Instruction Selection, Register Allocation +and Encodings are done. Hence in and around the completion of Encoding we +expect to find lots of bugs in Instruction Selection and Register Allocation. +We certainly hand-inspect the output and fix obvious problems, but still lots +bugs linger until we can actually run the code. + +This means that other chapters have updates and bug-fixes to the work being +described here. + # Table of Contents @@ -41,10 +54,9 @@ - [LUI](#lui) - [Large constants](#large-constants) -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). +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). After completing instruction selection, the next step is to encode the @@ -53,6 +65,7 @@ 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: For *x86-64(amd64):*, we use the latest Intel manual for the [encoding rules](https://www.felixcloutier.com/x86/). @@ -60,8 +73,6 @@ For *x86-64(amd64):*, we use the latest Intel manual for the [encoding rules](ht For *riscv* we currently target [RVA23U64](https://msyksphinz-self.github.io/riscv-isadoc/html/). - - ### ARMV8/AArch64 : For *arm*(aarch64) we use this collection for the [encoding rules](https://docsmirror.github.io/A64/2023-06/index.html). *Note*: We only support 64 bits ARM encodings. @@ -73,8 +84,9 @@ For *arm*(aarch64) we use this collection for the [encoding rules](https://docsm ## 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. +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 ) @@ -85,9 +97,11 @@ for( CFGNode bb : _code._cfg ) _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. + +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 ) { @@ -98,7 +112,9 @@ 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 -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. +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 @@ -117,28 +133,37 @@ We append the least significant byte first, then the next byte, and so on. _bits.write(op ); ``` -`_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. +`_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. -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. + +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 are targeting the 64-bit version of x86, we need to handle this prefix. In 32-bit mode, however, it is typically unnecessary. + +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); - 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. + - using one of the high byte registers AH, CH, BH or DH (not done currently in Simple) -*Note:* When encoding SSE instruction, the *REX* prefix(0x40) must come after the `SSE`prefix. -In all other cases, it is ignored. +*Note:* When encoding SSE instruction, the *REX* prefix(0x40) must come after +the `SSE`prefix. In all other cases, it is ignored. -In Simple, the REX prefix is just appended to the beginning of the bit stream(except SEE float instructions). -The layout is the following: +In Simple, the REX prefix is just appended to the beginning of the bit stream +(except SEE float instructions). The layout is the following: | **Field** | **Length** | **Description** | |:---------:|:----------:|---------------------------------------------------------------------------------| @@ -151,9 +176,8 @@ The layout is the following: ```java public static int REX_W = 0x48; - - ... ``` + ```java public static int rex(int reg, int ptr, int idx, boolean wide) { // assuming 64 bit by default so: 0100 1000 @@ -166,7 +190,6 @@ public static int rex(int reg, int ptr, int idx, boolean wide) { ... enc.add1(x86_64_v2.rex(dst, src, 0)); -} ``` Setting THE `W` bit gives us: @@ -181,7 +204,7 @@ enc.add1(opcode()); // opcode | **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.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. | @@ -194,10 +217,10 @@ The first thing we need for the modrm byte is the 2 bits mod. The layout for mod is the following: ``` public enum MOD { -INDIRECT, // [mem] -INDIRECT_disp8, // [mem + 0x12] -INDIRECT_disp32,// [mem + 0x12345678] -DIRECT, // mem + INDIRECT, // [mem] + INDIRECT_disp8, // [mem + 0x12] + INDIRECT_disp32,// [mem + 0x12345678] + DIRECT, // mem }; ``` @@ -492,7 +515,7 @@ int body = arm.load_pc(0b01011100, 0, dst); ##### 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 +##### FLOAT: We use the *LDR (immediate, SIMD&FP)* instruction relative to the PC #### Function constant @@ -661,6 +684,9 @@ we store the current PC into a register and use that as a base to access the con // auipc t0,0 // addi t1,t0 + #0 ``` + + + ## Relocation ## Local paches: TBD |
