summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--chapter21/README.md38
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/codegen/ElfFile.java90
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/TFPARM.java7
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java12
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/TFPRISC.java3
5 files changed, 75 insertions, 75 deletions
diff --git a/chapter21/README.md b/chapter21/README.md
index 769633ec..7d08036c 100644
--- a/chapter21/README.md
+++ b/chapter21/README.md
@@ -13,7 +13,7 @@
- [Displacement](#displacement)
- [Immediate](#immediate)
- [Float](#float)
- - [Indirect](#indirect)
+ - [Indirect](#indirectmemop)
- [Conditional flags](#conditional-flags)
4. [ARM](#arm)
- [Reg-form](#reg-form)
@@ -22,20 +22,20 @@
- [LSL(immediate)](#lslimmediate)
- [LSR(immediate)](#lsrimmediate)
- - [Logical immediates](#logical-immediates)
+ - [Logical Immediates](#logical-immediates)
- [Float](#float)
- [Function constant](#function-constant)
- [Immediate](#immediate)
- [Conditional flags](#conditional-flags)
- - [Indirect](#indirect)
- - [Branching](#branching)
+ - [Indirect](#indirectmemop-1)
+ - [Branching](#branching-)
- [Rip relative](#rip-relative)
- [Compare](#compare)
5. [RISCV](#riscv)
- [Instruction formats](#instruction-formats)
- [FLOAT](#float)
- - [INDIRECT(MemOp)](#indirectmemop)
+ - [Indirect](#indirectmemop-2)
- [BRANCH](#branch)
- [Function Constant](#function-constant)
- [LUI](#lui)
@@ -122,7 +122,7 @@ _bits.write(op );
## 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.
+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.
@@ -134,10 +134,10 @@ 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.
-*Note:* When encoding SSE instruction, the *REX* prefix(0x40) must come after the prefix.
+*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 appneded to the beginning of the bit stream.
+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** |
@@ -155,13 +155,14 @@ The layout is the following:
...
```
```java
-public static int rex(int reg, int ptr, int idx) {
+public static int rex(int reg, int ptr, int idx, boolean wide) {
// assuming 64 bit by default so: 0100 1000
- int rex = REX_W; // Default REX.W
+ int rex = wide ? REX_W : REX;
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));
@@ -240,13 +241,13 @@ The SIB byte has the following fields:
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;
- }
+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));
+```java
+enc.add1(x86_64_v2.sib(_scale, idx, x86_64_v2.RBP));
```
#### Displacement
@@ -287,7 +288,8 @@ else enc.add4(_imm);
```
#### INDIRECT(MemOp)
-TBD
+X86-64 support multiple indirect addressing modes:
+
#### Float
For float operations we use `SEE SIMD` instructions that operate on scalar single precision floating0-point values located in the
@@ -427,7 +429,7 @@ Hence, the `imms` value is `(64 - _imm) - 1`.
Same as *ASR*.
-#### Logical immediates
+#### Logical Immediates
Logical immediates refer to *andi, xori, orri*.
```java
@@ -535,7 +537,7 @@ Arm supports the following indirect forms:
[Store](https://docsmirror.github.io/A64/2023-06/strb_imm.html): `str dst, [base + #offset]`
If the offset is not a con
-#### Branching
+#### 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.
diff --git a/chapter21/src/main/java/com/seaofnodes/simple/codegen/ElfFile.java b/chapter21/src/main/java/com/seaofnodes/simple/codegen/ElfFile.java
index 6c8a1fc1..654d6678 100644
--- a/chapter21/src/main/java/com/seaofnodes/simple/codegen/ElfFile.java
+++ b/chapter21/src/main/java/com/seaofnodes/simple/codegen/ElfFile.java
@@ -69,6 +69,10 @@ public class ElfFile {
// top 4bits are the "bind", bottom 4 are "type"
int _info;
+ // bind 1011
+ // 00001111
+ // 10110000
+
Symbol(String name, int parent, int bind, int type) {
_name = name;
_parent = parent;
@@ -78,6 +82,7 @@ public class ElfFile {
void writeHeader(ByteBuffer out) {
out.putInt(_name_pos); // name
out.put((byte) _info); // info
+ // default visibility
out.put((byte) 0); // other
out.putShort((short) _parent); // shndx
out.putLong(_value); // value
@@ -140,7 +145,7 @@ public class ElfFile {
}
@Override
- void writeHeader(ByteBuffer out) {
+ void writeHeader(ByteBuffer out) {
// points to string table section
_link = 1;
@@ -160,16 +165,15 @@ public class ElfFile {
@Override
void write(ByteBuffer out) {
+ // Index 0 both designates the first entry in the table and serves as the undefined symbol index
for( int i = 0; i < SYMBOL_SIZE/4; i++ ) {
out.putInt(0);
}
- int num=1;
+ // index is already set
for( Symbol s : _loc ) {
- s._index = num++;
s.writeHeader(out);
}
for( Symbol s : _symbols ) {
- s._index = num++;
s.writeHeader(out);
}
}
@@ -210,6 +214,7 @@ public class ElfFile {
s._index = _sections._len;
}
+ /* creates function and stores where it starts*/
private final HashMap<TypeFunPtr,Symbol> _funcs = new HashMap<>();
private void encodeFunctions(SymbolSection symbols, DataSection text) {
int func_start = 0;
@@ -236,31 +241,6 @@ public class ElfFile {
}
}
- public final HashMap<Type,Symbol> _bigCons = new HashMap<>();
- private void encodeConstants(SymbolSection symbols, DataSection rdata) {
- int cnt = 0;
- for (Map.Entry<Node,Type> e : _code._encoding._bigCons.entrySet()) {
- if (_bigCons.get(e.getValue()) != null) {
- continue;
- }
-
- Symbol glob = new Symbol("GLOB$"+cnt, rdata._index, SYM_BIND_GLOBAL, SYM_TYPE_FUNC);
- glob._value = rdata._contents.size();
- symbols.push(glob);
-
- Type t = e.getValue();
- if ( t instanceof TypeFloat tf ) {
- write8(rdata._contents, Double.doubleToLongBits(tf._con));
- } else {
- throw Utils.TODO();
- }
-
- glob._size = rdata._contents.size() - glob._value;
- _bigCons.put(e.getValue(), glob);
- cnt++;
- }
- }
-
public void export(String fname) throws IOException {
DataSection strtab = new DataSection(".strtab", 3 /* SHT_SYMTAB */);
// first byte is reserved for an empty string
@@ -271,6 +251,7 @@ public class ElfFile {
SymbolSection symbols = new SymbolSection(".symtab", 2 /* SHT_STRTAB */);
pushSection(symbols);
+ // zero flag by default
// we've already constructed this entire section in the encoding phase
DataSection text = new DataSection(".text", 1 /* SHT_PROGBITS */, _code._encoding._bits);
text._flags = SHF_ALLOC | SHF_WRITE | SHF_EXECINSTR;
@@ -282,15 +263,34 @@ public class ElfFile {
// populate function symbols
encodeFunctions(symbols, text);
- // populate big constants
- // encodeConstants(symbols, rdata);
+ int idx = 1;
+ for( Section s : _sections ) {
+ Symbol sym = new Symbol(s._name, idx++, SYM_BIND_LOCAL, SYM_TYPE_SECTION);
+ // we can reuse the same name pos from the actual section
+ sym._name_pos = s._name_pos;
+ sym._size = s.size();
+ symbols.push(sym);
+ }
+
+ // calculate local index
+ int num = 1;
+ for( Symbol s : symbols._loc ) {
+ s._index = num++;
+ }
+ int start_global = symbols._loc.size();
+ for(Symbol a: symbols._symbols) {
+ a._index = start_global++;
+ }
// create .text relocations
DataSection text_rela = new DataSection(".rela.text", 4 /* SHT_RELA */);
for( Node n : _code._encoding._externals.keySet()) {
int nid = n._nid;
String extern = _code._encoding._externals.get(n);
- int sym_id = _funcs.get(extern)._index;
+ if(!extern.isEmpty()) throw Utils.TODO();
+ // Todo: fix this up later when external functions will be in TU
+ //int sym_id = _funcs.get(extern)._index;
+ int sym_id = 0;
int offset = _code._encoding._opStart[nid] + _code._encoding._opLen[nid] - 4;
// u64 offset
@@ -300,38 +300,22 @@ public class ElfFile {
// i64 addend
write8(text_rela._contents, -4);
}
- // relocations to constants
- if (false) for (Map.Entry<Node,Type> e : _code._encoding._bigCons.entrySet()) {
- int nid = e.getKey()._nid;
- int sym_id = _bigCons.get(e.getValue())._index;
- int offset = _code._encoding._opStart[nid] + _code._encoding._opLen[nid] - 4;
- // u64 offset
- write8(text_rela._contents, offset);
- // u64 info
- write8(text_rela._contents, ((long)sym_id << 32L) | 1L /* PC32 */);
- // i64 addend
- write8(text_rela._contents, -4);
- }
text_rela._flags = SHF_INFO_LINK;
text_rela._link = 2;
text_rela._info = text._index;
pushSection(text_rela);
+ Symbol sym = new Symbol(text_rela._name, idx++, SYM_BIND_LOCAL, SYM_TYPE_SECTION);
+ sym._name_pos = text_rela._name_pos;
+ sym._size = text_rela.size();
+ symbols.push(sym);
+
// populate string table
for( Section s : _sections ) { s._name_pos = writeCString(strtab, s._name); }
for( Symbol s : symbols._symbols ) { s._name_pos = writeCString(strtab, s._name); }
for( Symbol s : symbols._loc ) { s._name_pos = writeCString(strtab, s._name); }
- int idx = 1;
- for( Section s : _sections ) {
- Symbol sym = new Symbol(s._name, idx++, SYM_BIND_LOCAL, SYM_TYPE_SECTION);
- // we can reuse the same name pos from the actual section
- sym._name_pos = s._name_pos;
- sym._size = s.size();
- symbols.push(sym);
- }
-
int size = 64; // ELF header
// size of all section data
for( Section s : _sections ) {
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 f42dae75..8aae5911 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
@@ -20,8 +20,8 @@ public class TFPARM extends ConstantNode implements MachNode, RIPRelSize {
// adrp x0, 0
int adrp = arm.adrp(1,0, 0b10000, 0,self);
// add x0, x0, 0
- arm.imm_inst(enc,this,0b1001000100,0);
enc.add4(adrp);
+ arm.imm_inst(enc,0b1001000100,0, 0);
}
@Override public byte encSize(int delta) {
@@ -32,15 +32,16 @@ public class TFPARM extends ConstantNode implements MachNode, RIPRelSize {
// 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 ) {
+ if(opLen == 8 ) {
// 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));
+ enc.patch4(next, arm.imm_inst_l(0b1001000100, delta & 0xfff, rpc));
} else {
+ // should not happen as one instruction is 4 byte, and TFP arm encodes 2.
throw Utils.TODO();
}
}
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 9ac5a718..ce78f85b 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
@@ -225,6 +225,12 @@ public class arm extends Machine {
enc.add4(body);
}
+ // for cases where rs1 and dst are the same, eg add x0, x0, 1
+ public static void imm_inst(Encoding enc, int opcode, int imm12, int self) {
+ int body = imm_inst(opcode, imm12&0xFFF, self, self);
+ enc.add4(body);
+ }
+
public static void imm_inst_n(Encoding enc, Node n, int opcode, int imm13) {
short self = enc.reg(n);
short reg1 = enc.reg(n.in(1));
@@ -239,6 +245,12 @@ public class arm extends Machine {
return (opcode << 23) | (imm13 << 10) | (rn << 5) | rd;
}
+ public static int imm_inst_l(int opcode, int imm12, int self) {
+ int body = imm_inst(opcode, imm12&0xFFF, self, self);
+ return body;
+ }
+
+ // for cases where rs1 and dst are the same, eg add x0, x0, 1
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));
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 6cb219df..1eef56e9 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
@@ -34,7 +34,7 @@ public class TFPRISC extends ConstantNode implements MachNode, RIPRelSize {
// 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 ) {
+ if( opLen==8 ) {
// AUIPC (upper 20 bits)
// opstart of add
int next = opStart + opLen;
@@ -43,6 +43,7 @@ public class TFPRISC extends ConstantNode implements MachNode, RIPRelSize {
enc.patch4(next,riscv.i_type(0b0010011, rpc, 0, rpc, delta & 0xFFF));
// addi
} else {
+ // should not happen as one instruction is 4 byte, and TFP arm encodes 2.
throw Utils.TODO();
}
}