nooc

nooc programming language compiler
git clone git://git.nihaljere.xyz/nooc
Log | Files | Refs | LICENSE

commit 8196ebe9084c0e9341677b105e96edeeefe64e66
parent 7fe9805c274d37befaaead9e01a7caf8e7cc75f0
Author: Nihal Jere <nihal@nihaljere.xyz>
Date:   Mon, 10 Jan 2022 12:19:58 -0600

x64: use relative offsets for call

This saves a register, that would have been used to load an address

Diffstat:
Mir.c | 4+---
Mx64.c | 60++++++++++++++++++++++++++++++++----------------------------
Mx64.h | 2+-
3 files changed, 34 insertions(+), 32 deletions(-)

diff --git a/ir.c b/ir.c @@ -191,6 +191,7 @@ genexpr(struct iproc *out, size_t expri) break; } case EXPR_FCALL: { + what = 1; // value doesn't matter uint64_t proc = procindex(out->top, &expr->d.call.name); size_t params[20]; assert(expr->d.call.params.len < 20); @@ -205,9 +206,6 @@ genexpr(struct iproc *out, size_t expri) STARTINS(IR_ALLOC, 1); // don't hardcode size } params[expr->d.call.params.len] = out_index; - what = NEWTMP; - STARTINS(IR_ASSIGN, what); - PUTINS(IR_SIZE, 8); PUTINS(IR_CALL, proc); for (size_t i = expr->d.call.params.len; i <= expr->d.call.params.len; i--) { PUTINS(IR_CALLARG, params[i]); diff --git a/x64.c b/x64.c @@ -74,6 +74,7 @@ emitblock(char *buf, struct iproc *proc, struct instr *start, struct instr *end, end = end ? end : &proc->data[proc->len]; uint64_t dest, src, size, count, tmp, label; + int64_t offset; uint64_t localalloc = 0; size_t total = 0; @@ -160,31 +161,34 @@ emitblock(char *buf, struct iproc *proc, struct instr *start, struct instr *end, localalloc += 8; NEXT; break; - case IR_CALL: - count = 0; - total += mov_r64_imm(buf ? buf + total : NULL, dest, proc->top->code.data[ins->id].addr); - NEXT; - - for (int i = 0; i < 16; i++) { - total += push_r64(buf ? buf + total : NULL, i); - } - - while (ins->op == IR_CALLARG) { - count++; - total += push_r64(buf ? buf + total : NULL, proc->intervals.data[ins->id].reg); - NEXT; - } - total += call(buf ? buf + total : NULL, dest); - // FIXME: this won't work with non-64-bit things - total += add_r64_imm(buf ? buf + total : NULL, RSP, 8*count); - for (int i = 15; i >= 0; i--) { - total += pop_r64(buf ? buf + total : NULL, i); - } - break; default: die("x64 emitblock: unhandled assign instruction"); } break; + case IR_CALL: + count = 0; + dest = ins->id; + + for (int i = 0; i < 16; i++) { + total += push_r64(buf ? buf + total : NULL, i); + } + + NEXT; + while (ins->op == IR_CALLARG) { + count++; + total += push_r64(buf ? buf + total : NULL, proc->intervals.data[ins->id].reg); + NEXT; + } + + // we assume call is constant width - this should probably change + offset = -(proc->addr + total - proc->top->code.data[dest].addr + call(NULL, 0)); + total += call(buf ? buf + total : NULL, offset); + // FIXME: this won't work with non-64-bit things + total += add_r64_imm(buf ? buf + total : NULL, RSP, 8*count); + for (int i = 15; i >= 0; i--) { + total += pop_r64(buf ? buf + total : NULL, i); + } + break; case IR_LABEL: if (ins->id == end_label) goto done; @@ -781,17 +785,17 @@ jmp(char *buf, int64_t offset) } size_t -call(char *buf, enum reg reg) +call(char *buf, int32_t offset) { if (buf) { - if (reg >= 8) - *(buf++) = REX_B; - - *(buf++) = 0xFF; - *(buf++) = (MOD_DIRECT << 6) | (2 << 3) | (reg & 7); + *(buf++) = 0xE8; + *(buf++) = (uint32_t) offset & 0xff; + *(buf++) = ((uint32_t) offset >> 8) & 0xff; + *(buf++) = ((uint32_t) offset >> 16) & 0xff; + *(buf++) = ((uint32_t) offset >> 24) & 0xff; } - return reg >= 8 ? 3 : 2; + return 5; } size_t diff --git a/x64.h b/x64.h @@ -59,7 +59,7 @@ size_t jng(char *buf, int64_t offset); size_t jg(char *buf, int64_t offset); size_t jne(char *buf, int64_t offset); size_t jmp(char *buf, int64_t offset); -size_t call(char *buf, enum reg reg); +size_t call(char *buf, int32_t offset); size_t ret(char *buf); size_t push_r64(char *buf, enum reg reg); size_t pop_r64(char *buf, enum reg reg);