diff --git a/src/asm.c b/src/asm.c index 04e42b3..78917ae 100644 --- a/src/asm.c +++ b/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); + } +} diff --git a/src/asm.h b/src/asm.h index 4e7e196..235bccf 100644 --- a/src/asm.h +++ b/src/asm.h @@ -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 diff --git a/src/format.c b/src/format.c index d5f8e43..4840410 100644 --- a/src/format.c +++ b/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; } diff --git a/src/ir.c b/src/ir.c index f89cb42..73c064f 100644 --- a/src/ir.c +++ b/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); diff --git a/src/ir.h b/src/ir.h index 9f8034e..408cd27 100644 --- a/src/ir.h +++ b/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); diff --git a/src/lang.c b/src/lang.c index 99bf983..9337271 100644 --- a/src/lang.c +++ b/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; } diff --git a/src/main.c b/src/main.c index 5530d50..d426591 100644 --- a/src/main.c +++ b/src/main.c @@ -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; } diff --git a/src/x86encode.c b/src/x86encode.c index f806ec4..f4c7dc2 100644 --- a/src/x86encode.c +++ b/src/x86encode.c @@ -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) { diff --git a/src/x86encode.h b/src/x86encode.h index 0ff4570..96f7091 100644 --- a/src/x86encode.h +++ b/src/x86encode.h @@ -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);