Slightly unfuck codegen for relative jumps and blocks.

Still pretty horribly broken. I was generating dispacements
backwards, failing to emit stuff like the beginning of
loops, the logic for `if` jumps was backwards, etc.
I was also completely forgetting to increment file_here
for appends. I don't remember what all I did; check the diff.
master
James T. Martin 2022-10-19 12:21:46 -07:00
parent 1383484e06
commit 68ce32a6df
Signed by: james
GPG Key ID: D6FB2F9892F9B225
9 changed files with 178 additions and 35 deletions

View File

@ -13,8 +13,31 @@ void inst_jump(symbol sym) {
int32_t disp = symbol_offset(sym, X86_JMP_DISP8_SIZE);
if (disp >= INT8_MIN && disp <= INT8_MAX) {
x86_inst_jmp_disp8(disp);
} else {
x86_inst_jmp_disp32_op();
relocate_pc32(sym);
}
x86_inst_jmp_disp32_op();
relocate_pc32(sym);
// TODO: support 64-bit jumps?
}
void inst_jump_if_zero(symbol sym, reg reg) {
x86_inst_test_r64_r64(reg, reg);
int32_t disp = symbol_offset(sym, X86_JZ_DISP8_SIZE);
if (disp >= INT8_MIN && disp <= INT8_MAX) {
x86_inst_jz_disp8(disp);
} else {
x86_inst_jz_disp32_op();
relocate_pc32(sym);
}
}
void inst_jump_if_not_zero(symbol sym, reg reg) {
x86_inst_test_r64_r64(reg, reg);
int32_t disp = symbol_offset(sym, X86_JNZ_DISP8_SIZE);
if (disp >= INT8_MIN && disp <= INT8_MAX) {
x86_inst_jnz_disp8(disp);
} else {
x86_inst_jnz_disp32_op();
relocate_pc32(sym);
}
}

View File

@ -2,8 +2,15 @@
#define _ASM_H
#include "format.h"
#include "x86encode.h"
/// Jump to a known address.
void inst_jump(symbol sym);
/// Jump to a known address if the argument is zero.
void inst_jump_if_zero(symbol sym, reg reg);
/// Jump to a known address if the argument is not zero.
void inst_jump_if_not_zero(symbol sym, reg reg);
#endif

View File

@ -100,7 +100,9 @@ void finish_executable(symbol entry_point) {
assert(rel.type == REL_PC32);
uint64_t vaddr = symbols[rel.symbol].vaddr;
assert(vaddr != (uint64_t) -1);
int64_t disp = (int64_t) rel.offset - (int64_t) vaddr + 4;
// PC-relative is from the *end* of the instruction,
// and the displacement is 4 bytes (32 bits).
int64_t disp = (int64_t) vaddr - ((int64_t) rel.offset + 4);
assert(disp >= INT32_MIN && disp <= INT32_MAX);
patch_u32(rel.offset, (int32_t) disp);
}
@ -132,14 +134,17 @@ void append_data(size_t size, const void* buf) {
}
void append_u8(uint8_t x) {
file_here += 1;
emit_u8(x);
}
void append_u32(uint32_t x) {
file_here += 4;
emit_u32(x);
}
void append_u64(uint64_t x) {
file_here += 8;
emit_u64(x);
}
@ -159,11 +164,11 @@ void relocate_pc32(symbol sym) {
append_u32((uint32_t) offset);
return;
}
append_u32(0);
struct relocation* rel = new_relocation();
rel->type = REL_PC32;
rel->offset = file_here;
rel->symbol = sym;
append_u32(0);
}
int32_t symbol_offset(symbol sym, int8_t off) {
@ -171,7 +176,7 @@ int32_t symbol_offset(symbol sym, int8_t off) {
if (vaddr == (uint64_t) -1) {
return INT32_MAX;
}
int64_t disp = (int64_t) file_here - (int64_t) vaddr + off;
int64_t disp = (int64_t) vaddr - ((int64_t) file_here + off);
if (disp >= INT32_MAX || disp <= INT32_MIN) {
return INT32_MAX;
}

View File

@ -34,13 +34,15 @@ struct label {
static uint32_t stack_depth = 0;
static uint32_t stack_frame = 0;
static struct stack_frame stack_frames[MAX_STACK_FRAMES];
static uint32_t label_depth;
static uint32_t label_depth = 0;
static struct label labels[MAX_LABELS];
void init(var* argc, var* argv, var* env) {
void init_ir(var* argc, var* argv, var* env) {
assert(stack_depth == 0);
// seems like this should be necessary. it really feels like there's some
// off-by-one arrows going on around here, but I can't figure it out.
//enter();
x86_inst_mov_r64_r64(BP, SP);
// TODO: replace with add, once I implement add
x86_inst_add_r64_imm8(BP, 8 * 3);
*env = stack_depth++;
*argv = stack_depth++;
@ -58,10 +60,10 @@ void enter(void) {
void leave(var* args) {
assert(stack_frame > 0);
struct stack_frame frame = stack_frames[stack_frame];
define(frame.label_depth, args);
struct stack_frame frame = stack_frames[stack_frame - 1];
stack_frame--;
stack_depth = frame.depth;
label_depth = frame.label_depth;
define(frame.label_depth, args);
}
label declare(uint32_t argc) {
@ -73,7 +75,7 @@ label declare(uint32_t argc) {
}
label declare_exit(uint32_t argc) {
label label = stack_frames[stack_frame].label_depth;
label label = stack_frames[stack_frame - 1].label_depth;
labels[label].argc = argc;
return label;
}
@ -81,19 +83,17 @@ label declare_exit(uint32_t argc) {
void define(label l, var* args) {
struct label* label = &labels[l];
define_executable_symbol(label->symbol);
}
void jump(label l, var* args) {
struct label* label = &labels[l];
inst_jump(label->symbol);
}
void jump_table(size_t branches, label* labels, var index, var* args) {
assert(0); // UNIMPLEMENTED
}
void jump_if(label label, var cond, var* args) {
//assert(0); // UNIMPLEMENTED
// possibly wrong. do I need to do any clean-up of the old frame here?
stack_frame = label->frame;
struct stack_frame* frame = &stack_frames[stack_frame - 1];
label_depth = frame->label_depth;
for (uint32_t i = 0; i < label->argc; i++) {
args[i] = frame->depth + i;
}
// probably wrong. seems like I ought to create a new frame or something?
// wouldn't this make the old frame too deep?
// but on the other hand, if I enter a new frame, how do I decide when to leave it?
frame->depth += label->argc;
}
void load_var(reg reg, var var) {
@ -108,6 +108,45 @@ var push_var(reg reg) {
return stack_depth++;
}
void load_args(struct label* label, var* args) {
struct stack_frame* cur_frame = &stack_frames[stack_frame - 1];
struct stack_frame* dest_frame = &stack_frames[label->frame];
uint32_t depth_diff = cur_frame->depth - dest_frame->depth;
if (depth_diff > 0) {
// FIXME: should be immX!!!
// FIXME: this should be necessary! stack depth is never getting decreased!
//x86_inst_add_r64_imm8(SP, depth_diff);
}
for (uint32_t arg = 0; arg < label->argc; arg++) {
load_var(AX, args[arg]);
x86_inst_push_r64(AX);
}
}
void jump(label l, var* args) {
struct label* label = &labels[l];
load_args(label, args);
inst_jump(label->symbol);
}
void jump_table(size_t branches, label* labels, var index, var* args) {
assert(0); // UNIMPLEMENTED
}
void jump_if(label l, var cond, var* args) {
struct label* label = &labels[l];
load_var(BX, cond);
load_args(label, args);
inst_jump_if_not_zero(label->symbol, BX);
}
void jump_unless(label l, var cond, var* args) {
struct label* label = &labels[l];
load_var(BX, cond);
load_args(label, args);
inst_jump_if_zero(label->symbol, BX);
}
var lit(uint64_t lit) {
x86_inst_mov_r64_imm(AX, lit);
x86_inst_push_r64(AX);

View File

@ -9,7 +9,7 @@ typedef uint32_t label;
/// Call this at the beginning of execution.
/// It performs initialization and stuff.
void init(var* argc, var* argv, var* env);
void init_ir(var* argc, var* argv, var* env);
/// Enter a new block.
///
@ -64,6 +64,9 @@ void jump_table(size_t branches, label* labels, var index, var* args);
/// Jump to label if `cond` is not zero.
void jump_if(label label, var cond, var* args);
/// Jump to label if `cond` is zero.
void jump_unless(label label, var cond, var* args);
/// Integer literal.
var lit(uint64_t lit);

View File

@ -286,7 +286,7 @@ static struct label_and_arity lookup_label(enum label_type type, char* name) {
exit(1);
}
void reduce_expression_binop(struct expr_crumb* exprc, var (*emit)(var arg1, var arg2)) {
static void reduce_expression_binop(struct expr_crumb* exprc, var (*emit)(var arg1, var arg2)) {
assert(exprc->argument_count >= 2);
var arg1 = exprc->arguments[0];
var arg2 = exprc->arguments[1];
@ -295,7 +295,7 @@ void reduce_expression_binop(struct expr_crumb* exprc, var (*emit)(var arg1, var
exprc->argument_count--;
}
var reduce_expression(struct expr_crumb* exprc) {
static var reduce_expression(struct expr_crumb* exprc) {
// TODO FIXME: operator precedence
if (exprc->operator_count > 0 || exprc->argument_count > 1) {
fprintf(stderr, "warning: expression reduction may be incorrect\n");
@ -350,6 +350,7 @@ void enter_block(void) {
struct loop_crumb* loopc = &ctx->data.loop;
assert(loopc->state == LOOP_CLEAN);
loopc->state = LOOP_BODY;
loopc->next = declare(loopc->assignment_count);
var args[MAX_ASSIGNMENTS];
define(loopc->next, args);
// TODO NOTE: is this the correct order?
@ -440,8 +441,7 @@ void exit_expr(void) {
assert(ctx->type == EXPR_CRUMB);
struct expr_crumb* exprc = &ctx->data.expr;
assert(exprc->argument_count > 0);
reduce_expression(exprc);
var ret = exprc->arguments[0];
var ret = reduce_expression(exprc);
context_depth--;
ctx = &context[context_depth - 1];
switch (ctx->type) {
@ -465,8 +465,8 @@ void exit_expr(void) {
case IF_CRUMB: {
struct if_crumb* ifc = &ctx->data.if_;
assert(ifc->state == IF_COND);
jump_if(ifc->then, ret, NULL);
jump(ifc->else_, NULL);
jump_unless(ifc->else_, ret, NULL);
//jump(ifc->then_, NULL);
ifc->state = IF_THEN;
break;
}

View File

@ -14,14 +14,14 @@ symbol compile(void) {
symbol entry_point = new_symbol();
define_executable_symbol(entry_point);
var argc, argv, env;
init(&argc, &argv, &env);
init_ir(&argc, &argv, &env);
parse();
var a = lit(52);
var b = lit(10);
var exit_code = sub(a, b);
var sys_exit = lit(60);
var args[2] = { sys_exit, exit_code };
syscall(2, args);
parse();
return entry_point;
}

View File

@ -226,6 +226,10 @@ void x86_inst_pop_r64(reg reg) {
x86_enc_opr(0x58, reg);
}
void x86_inst_test_r64_r64(reg r1, reg r2) {
x86_enc_rexw_modrr(0x85, r1, r2);
}
void x86_inst_jmp_disp8(int8_t disp) {
x86_enc_disp8(0xeb, disp);
}
@ -247,6 +251,52 @@ void x86_inst_jmp_disp32_op(void) {
append_u8(0xe9);
}
void x86_inst_jnz_disp8(int8_t disp) {
x86_enc_disp8(0x75, disp);
}
void x86_inst_jnz_disp32(int32_t disp) {
append_u8(0x0f);
x86_enc_disp32(0x85, disp);
}
void x86_inst_jnz_disp(int32_t disp) {
int32_t disp8 = disp + X86_JNZ_DISP8_SIZE;
if (disp8 >= INT8_MIN && disp8 <= INT8_MAX) {
x86_inst_jnz_disp8((int8_t) disp);
} else {
x86_inst_jnz_disp32(disp + X86_JNZ_DISP32_SIZE);
}
}
void x86_inst_jnz_disp32_op(void) {
append_u8(0x0f);
append_u8(0x84);
}
void x86_inst_jz_disp8(int8_t disp) {
x86_enc_disp8(0x74, disp);
}
void x86_inst_jz_disp32(int32_t disp) {
append_u8(0x0f);
x86_enc_disp32(0x84, disp);
}
void x86_inst_jz_disp(int32_t disp) {
int32_t disp8 = disp + X86_JZ_DISP8_SIZE;
if (disp8 >= INT8_MIN && disp8 <= INT8_MAX) {
x86_inst_jz_disp8((int8_t) disp);
} else {
x86_inst_jz_disp32(disp + X86_JZ_DISP32_SIZE);
}
}
void x86_inst_jz_disp32_op(void) {
append_u8(0x0f);
append_u8(0x84);
}
// TODO: special instructions for AX
void x86_inst_sub_r64_imm8(reg dest, int8_t imm) {
x86_enc_rexw_modxm_imm8(0x83, 5, dest, (uint8_t) imm);
@ -270,7 +320,7 @@ void x86_inst_add_r64_imm8(reg dest, int8_t imm) {
}
void x86_inst_add_r64_r64(reg dest, reg src) {
x86_enc_rexw_modrr(0x0, src, dest);
x86_enc_rexw_modrr(0x01, src, dest);
}
void x86_inst_syscall(void) {

View File

@ -42,6 +42,8 @@ void x86_inst_mov_m64_r64_disp(reg dest, reg src, int32_t disp);
void x86_inst_push_r64(reg reg);
void x86_inst_pop_r64(reg reg);
void x86_inst_test_r64_r64(reg r1, reg r2);
#define X86_JMP_DISP8_SIZE 2
void x86_inst_jmp_disp8(int8_t disp);
#define X86_JMP_DISP32_SIZE 5
@ -49,6 +51,20 @@ void x86_inst_jmp_disp32(int32_t disp);
void x86_inst_jmp_disp(int32_t disp);
void x86_inst_jmp_disp32_op(void);
#define X86_JNZ_DISP8_SIZE 2
void x86_inst_jnz_disp8(int8_t disp);
#define X86_JNZ_DISP32_SIZE 6
void x86_inst_jnz_disp32(int32_t disp);
void x86_inst_jnz_disp(int32_t disp);
void x86_inst_jnz_disp32_op(void);
#define X86_JZ_DISP8_SIZE 2
void x86_inst_jz_disp8(int8_t disp);
#define X86_JZ_DISP32_SIZE 6
void x86_inst_jz_disp32(int32_t disp);
void x86_inst_jz_disp(int32_t disp);
void x86_inst_jz_disp32_op(void);
void x86_inst_sub_r64_imm8(reg dest, int8_t imm);
void x86_inst_sub_r64_imm32(reg dest, int32_t imm);
void x86_inst_sub_r64_imm(reg dest, int32_t imm);