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:
M | ir.c | | | 4 | +--- |
M | x64.c | | | 60 | ++++++++++++++++++++++++++++++++---------------------------- |
M | x64.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);