Skip to content

Commit

Permalink
x86: fix load/store of local variables
Browse files Browse the repository at this point in the history
Also extend rat/ to be able to name partial registers
for correctly emitting load/store (mov) of different width.

So for local variables the compiler now respects the width of them when
load/store.

Also needs to be done for the function arguments.
  • Loading branch information
pointbazaar committed Dec 31, 2024
1 parent 5d1df96 commit 65c4b01
Show file tree
Hide file tree
Showing 11 changed files with 185 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,15 @@ static void case_local_var(struct RAT* rat, struct TAC* tac, struct LVST* lvst,

const ssize_t offset = lvst_stack_frame_offset_x86(lvst, name);

const uint32_t nbytes = lvst_sizeof_var(lvst, name, true);
assert(nbytes > 0);

// stack frame is pointed to by rbp
uint32_t rscratch = rat_scratch_reg(rat);
mov_const(rscratch, -offset, c);
add(rscratch, SD_REG_RBP, c);
mov_load(reg_dest, rscratch, c);

mov_load_width(reg_dest, rscratch, nbytes, c);
}

void compile_tac_load_local_x86(struct RAT* rat, struct TAC* tac, struct Ctx* ctx, struct IBuffer* ibu) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,6 @@ void compile_tac_store_local_x86(struct RAT* rat, struct TAC* tac, struct Ctx* c

mov_const(rscratch, -offset, c);
add(rscratch, rat_base_ptr(rat), c);
mov_store(rscratch, reg, c);

mov_store_width(rscratch, reg, var_width, c);
}
52 changes: 52 additions & 0 deletions docs/html/calling-convention.html
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,58 @@ <h3>caller/callee saved registers </h3>
<li> caller saved: others </li>
</ul>

<h3> Example (x86-64) </h3>

<p>
Example to see the stack usage for local variables. This is implementation detail (not part of calling convention).
</p>

<h5>Example Code</h5>
<pre>
fn main () ~> int {
int16 x = 0; // local var 0, 2 bytes
int8 y = 0; // local var 1, 1 byte
x += 1;
return x
}
</pre>

<h5>Example Stack Fragments</h5>
<pre>
... |
return address byte 1 |
return address byte 0 | &lt;- rsp
| ...
| 0x0


... |
return address byte 1 |
return address byte 0 | &lt;- rbp, rsp
| ...
| 0x0

... |
return address byte 0 | &lt;- rbp
| ...
| &lt;- rsp (has been decremented by 3 bytes to make space for local vars)
| ...
| 0x0

... |
return address byte 0 | &lt;- rbp
local var 0, byte 1 |
local var 0, byte 0 |
local var 1, byte 0 | &lt;- rsp
| ...
| 0x0

... |
return address byte 0 | &lt;- rbp, rsp (rsp has been reset before return)
| ...
| 0x0
</pre>

<h3> References for x86-64 Calling Conventions</h3>
<ul>
<li>
Expand Down
31 changes: 25 additions & 6 deletions ibuffer/ibuffer_write.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include <stdio.h>
#include <inttypes.h>
#include <string.h>
#include <assert.h>

#include "ibuffer.h"
#include "ibuffer_write.h"
Expand All @@ -11,7 +12,7 @@

extern char* MNEM[];

static void write_middle(enum IKEY key, int64_t x1, int64_t x2, char* str, char* s);
static void write_middle(enum IKEY key, int64_t x1, int64_t x2, char* str, char* s, int32_t x3);

static void strcat_reg(char* s, uint8_t reg);
static void strcat_num(char* s, int num);
Expand All @@ -23,21 +24,19 @@ void ibu_write_instr(enum IKEY key, int32_t x1, int32_t x2, int32_t x3, char* st
return;
}

if (x3 != 0) return;

fprintf(f, " "); //indentation

//write the mnemonic
fprintf(f, "%-5s ", MNEM[key]);

size_t l = 42;
size_t l = 72;
if (str != NULL) {
l += strlen(str);
}
char* s = malloc(l);
strcpy(s, "");

write_middle(key, x1, x2, str, s);
write_middle(key, x1, x2, str, s, x3);

fprintf(f, "%-s", s);

Expand All @@ -50,7 +49,19 @@ void ibu_write_instr(enum IKEY key, int32_t x1, int32_t x2, int32_t x3, char* st
free(s);
}

static void write_middle(enum IKEY key, int64_t x1, int64_t x2, char* str, char* s) {
static void write_middle(enum IKEY key, int64_t x1, int64_t x2, char* str, char* s, int32_t x3) {

const uint8_t nbytes = x3;
char* width_strs[] = {
"byte", "word", "dword", "qword"};

char* width_str = NULL;
switch (x3) {
case 1: width_str = "byte"; break;
case 2: width_str = "word"; break;
case 4: width_str = "dword"; break;
case 8: width_str = "qword"; break;
}

switch (key) {

Expand Down Expand Up @@ -162,6 +173,14 @@ static void write_middle(enum IKEY key, int64_t x1, int64_t x2, char* str, char*
case X86_MOV_STORE:
sprintf(s, "[%s], %s", rat_regname_x86(x1), rat_regname_x86(x2));
break;
case X86_MOV_LOAD_WIDTH:
assert(width_str != NULL);
sprintf(s, "%s, %s [%s]", rat_regname_x86_width(x1, nbytes), width_str, rat_regname_x86(x2));
break;
case X86_MOV_STORE_WIDTH:
assert(width_str != NULL);
sprintf(s, "%s [%s], %s", width_str, rat_regname_x86(x1), rat_regname_x86_width(x2, nbytes));
break;
case X86_PUSH:
case X86_POP:
case X86_INC:
Expand Down
2 changes: 2 additions & 0 deletions ibuffer/ibuffer_x86.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ void ibu_push4(struct IBuffer* ibu, enum IKEY key, int64_t x1, int64_t x2, int64
#define mov_regs(dest, src, c) ibu2(X86_MOV_REGS, dest, src, c)
#define mov_load(dest, src, c) ibu2(X86_MOV_LOAD, dest, src, c)
#define mov_store(dest, src, c) ibu2(X86_MOV_STORE, dest, src, c)
#define mov_load_width(dest, src, width, c) ibu3(X86_MOV_LOAD_WIDTH, dest, src, width, c)
#define mov_store_width(dest, src, width, c) ibu3(X86_MOV_STORE_WIDTH, dest, src, width, c)

#define cmove(dest, src, c) ibu2(X86_CMOVE, dest, src, c)
#define xchg(dest, src, c) ibu2(X86_XCHG, dest, src, c)
Expand Down
2 changes: 2 additions & 0 deletions ibuffer/ikey.h
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,8 @@ enum IKEY {
X86_MOV_REGS, // mov rdest, rsrc
X86_MOV_LOAD, // mov rdest, [rsrc]
X86_MOV_STORE, // mov [rdest], rsrc
X86_MOV_LOAD_WIDTH, // e.g. mov dword rdest, [rsrc]
X86_MOV_STORE_WIDTH, // e.g. mov qword [rdest], rsrc
X86_CMOVE,
X86_XCHG,

Expand Down
2 changes: 2 additions & 0 deletions ibuffer/mnem.c
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,8 @@ char* MNEM[] = {
[X86_MOV_REGS] = "mov",
[X86_MOV_LOAD] = "mov",
[X86_MOV_STORE] = "mov",
[X86_MOV_LOAD_WIDTH] = "mov",
[X86_MOV_STORE_WIDTH] = "mov",
[X86_CMOVE] = "cmove",
[X86_XCHG] = "xchg",
[X86_PUSH] = "push",
Expand Down
84 changes: 84 additions & 0 deletions rat/rat_x86.c
Original file line number Diff line number Diff line change
Expand Up @@ -90,13 +90,97 @@ const char* regnames_x86[] = {
[SD_REG_R15] = "r15",
};

const char* regnames_x86_4bytes[] = {
[SD_REG_RAX] = "eax",
[SD_REG_RBX] = "ebx",
[SD_REG_RCX] = "ecx",
[SD_REG_RDX] = "edx",
[SD_REG_RDI] = "edi",
[SD_REG_RSI] = "esi",

[SD_REG_RSP] = "esp",
[SD_REG_RBP] = "ebp",

[SD_REG_R8] = "r8d",
[SD_REG_R9] = "r9d",
[SD_REG_R10] = "r10d",
[SD_REG_R11] = "r11d",
[SD_REG_R12] = "r12d",
[SD_REG_R13] = "r13d",
[SD_REG_R14] = "r14d",
[SD_REG_R15] = "r15d",
};

const char* regnames_x86_2bytes[] = {
[SD_REG_RAX] = "ax",
[SD_REG_RBX] = "bx",
[SD_REG_RCX] = "cx",
[SD_REG_RDX] = "dx",
[SD_REG_RDI] = "di",
[SD_REG_RSI] = "si",

[SD_REG_RSP] = "sp",
[SD_REG_RBP] = "bp",

[SD_REG_R8] = "r8w",
[SD_REG_R9] = "r9w",
[SD_REG_R10] = "r10w",
[SD_REG_R11] = "r11w",
[SD_REG_R12] = "r12w",
[SD_REG_R13] = "r13w",
[SD_REG_R14] = "r14w",
[SD_REG_R15] = "r15w",
};

const char* regnames_x86_1bytes[] = {
[SD_REG_RAX] = "al",
[SD_REG_RBX] = "bl",
[SD_REG_RCX] = "cl",
[SD_REG_RDX] = "dl",
[SD_REG_RDI] = "dil",
[SD_REG_RSI] = "sil",

[SD_REG_RSP] = "spl",
[SD_REG_RBP] = "bpl",

[SD_REG_R8] = "r8b",
[SD_REG_R9] = "r9b",
[SD_REG_R10] = "r10b",
[SD_REG_R11] = "r11b",
[SD_REG_R12] = "r12b",
[SD_REG_R13] = "r13b",
[SD_REG_R14] = "r14b",
[SD_REG_R15] = "r15b",
};

char* rat_regname_x86(size_t i) {

assert(i > SD_REG_START_X86);
assert(i < SD_REG_END_X86);
return (char*)regnames_x86[i];
}

char* rat_regname_x86_width(size_t i, uint8_t nbytes) {

assert(i > SD_REG_START_X86);
assert(i < SD_REG_END_X86);
assert(nbytes <= 8);
switch (nbytes) {
case 8:
return (char*)regnames_x86[i];
case 4:
return (char*)regnames_x86_4bytes[i];
case 2:
return (char*)regnames_x86_2bytes[i];
case 1:
return (char*)regnames_x86_1bytes[i];
}

fprintf(stderr, "error in %s\n", __func__);
exit(1);
return NULL;
}

void rat_print_regname_x86(struct RAT* rat, size_t i) {

assert(i > SD_REG_START_X86);
Expand Down
7 changes: 7 additions & 0 deletions rat/rat_x86.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,11 @@ enum SD_REGISTER rat_param_reg_x86(uint32_t index);

// x86 specific
char* rat_regname_x86(size_t i);

// @returns the register name considering the number of bytes
// e.g. (rax, 8) -> rax
// e.g. (rax, 4) -> eax
// e.g. (rax, 1) -> al
char* rat_regname_x86_width(size_t i, uint8_t nbytes);

void rat_print_regname_x86(struct RAT* rat, size_t reg);
6 changes: 3 additions & 3 deletions tables/lvst/lvst.c
Original file line number Diff line number Diff line change
Expand Up @@ -310,11 +310,11 @@ ssize_t lvst_stack_frame_offset_x86(struct LVST* lvst, char* local_var_name) {

if (line->is_arg == true) continue;

offset += lvst_sizeof_type(line->type, true);

if (strcmp(line->name, local_var_name) == 0) {
return offset + 8;
return offset;
}

offset += lvst_sizeof_type(line->type, true);
}

printf("fatal error in lvst_stack_frame_offset_x86.");
Expand Down
2 changes: 1 addition & 1 deletion tables/test/test.c
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ void test_tables_lvst_stack_frame_offset_x86_int8() {

lvst_add(lvst, line);

assert(lvst_stack_frame_offset_x86(lvst, "x") == 8);
assert(lvst_stack_frame_offset_x86(lvst, "x") == 1);

lvst_free(lvst);
}
Expand Down

0 comments on commit 65c4b01

Please sign in to comment.