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
parent
1383484e06
commit
68ce32a6df
27
src/asm.c
27
src/asm.c
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
11
src/format.c
11
src/format.c
|
@ -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;
|
||||
}
|
||||
|
|
79
src/ir.c
79
src/ir.c
|
@ -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);
|
||||
|
|
5
src/ir.h
5
src/ir.h
|
@ -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);
|
||||
|
||||
|
|
12
src/lang.c
12
src/lang.c
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue