commit 43aef38f713b6666452dc13b9c57a95456d10929
parent 7fb78b667bc347cb999ac004aaaaa10fbbc32da5
Author: Nihal Jere <nihal@nihaljere.xyz>
Date: Fri, 14 Jan 2022 21:15:05 -0600
ir: add value type
This makes instructions more flexible, allowing immediates to be used
directly in instructions, rather than being loaded into a temporary
first.
Diffstat:
M | ir.c | | | 156 | ++++++++++++++++++++++++++++++++++++++++++++++--------------------------------- |
M | ir.h | | | 7 | +++++++ |
M | x64.c | | | 12 | ++++++++++++ |
3 files changed, 110 insertions(+), 65 deletions(-)
diff --git a/ir.c b/ir.c
@@ -18,7 +18,7 @@ extern struct exprs exprs;
extern struct assgns assgns;
extern struct toplevel toplevel;
-#define STARTINS(op, val) putins((out), (op), (val)) ; curi++ ;
+#define STARTINS(op, val, valtype) putins((out), (op), (val), (valtype)) ; curi++ ;
#define NEWTMP tmpi++; interval.start = curi + 1; interval.end = curi + 1; array_add((&out->temps), interval);
#define PTRSIZE 8
@@ -70,44 +70,74 @@ procindex(struct slice *s)
return 0;
}
-// some ops don't take temporaries, so only bump on ops that take temporaries
-void
-bumpinterval(struct iproc *out, struct instr *instr, size_t index) {
- switch (instr->op) {
- case IR_NONE:
- case IR_IMM:
- case IR_CALL:
- case IR_RETURN:
- case IR_LABEL:
- case IR_CONDJUMP:
- case IR_JUMP:
- case IR_ALLOC:
- break;
- case IR_STORE:
- case IR_LOAD:
- case IR_ADD:
- case IR_CEQ:
- case IR_ZEXT:
- case IR_ASSIGN:
- case IR_CALLARG:
- case IR_IN:
- case IR_EXTRA:
- out->temps.data[index].end = curi;
- break;
- default:
- die("bumpinterval");
- }
-}
-
static void
-putins(struct iproc *out, int op, uint64_t val)
+putins(struct iproc *out, int op, uint64_t val, int valtype)
{
+ assert(op);
+ assert(valtype);
struct instr ins = {
.val = val,
.op = op,
+ .valtype = valtype
};
- bumpinterval(out, &ins, val);
+ switch (valtype) {
+ case VT_TEMP:
+ switch (op) {
+ case IR_STORE:
+ case IR_LOAD:
+ case IR_ADD:
+ case IR_CEQ:
+ case IR_ZEXT:
+ case IR_ASSIGN:
+ case IR_CALLARG:
+ case IR_EXTRA:
+ break;
+ default:
+ die("putins: bad op for VT_TEMP");
+ }
+ out->temps.data[val].end = curi;
+ break;
+ case VT_LABEL:
+ switch (op) {
+ case IR_LABEL:
+ case IR_JUMP:
+ case IR_CONDJUMP:
+ break;
+ default:
+ die("putins: bad op for VT_LABEL");
+ }
+ break;
+ case VT_FUNC:
+ switch (op) {
+ case IR_CALL:
+ break;
+ default:
+ die("putins: bad op for VT_FUNC");
+ }
+ break;
+ case VT_IMM:
+ switch (op) {
+ case IR_IN:
+ case IR_IMM:
+ case IR_ALLOC:
+ break;
+ default:
+ die("putins: bad op for VT_IMM");
+ }
+ break;
+ case VT_EMPTY:
+ switch (op) {
+ case IR_RETURN:
+ break;
+ default:
+ die("putins: bad op for VT_EMPTY");
+ }
+ break;
+ default:
+ die("putins: unknown valtype");
+ }
+
array_add(out, ins);
}
@@ -115,7 +145,7 @@ static size_t
assign(struct iproc *out, uint8_t size)
{
size_t t = NEWTMP;
- STARTINS(IR_ASSIGN, t);
+ STARTINS(IR_ASSIGN, t, VT_TEMP);
out->temps.data[t].size = size;
return t;
}
@@ -123,39 +153,33 @@ assign(struct iproc *out, uint8_t size)
static size_t
immediate(struct iproc *out, uint8_t size, uint64_t val)
{
- size_t t = NEWTMP;
- STARTINS(IR_ASSIGN, t);
- out->temps.data[t].size = size;
- putins(out, IR_IMM, val);
+ size_t t = assign(out, size);
+ putins(out, IR_IMM, val, VT_IMM);
return t;
}
static size_t
load(struct iproc *out, uint8_t size, uint64_t index)
{
- size_t t = NEWTMP;
- STARTINS(IR_ASSIGN, t);
- out->temps.data[t].size = size;
- putins(out, IR_LOAD, index);
+ size_t t = assign(out, size);;
+ putins(out, IR_LOAD, index, VT_TEMP);
return t;
}
static size_t
alloc(struct iproc *out, uint8_t size, uint64_t count)
{
- size_t t = NEWTMP;
- STARTINS(IR_ASSIGN, t);
- out->temps.data[t].size = size;
+ size_t t = assign(out, size);
out->temps.data[t].flags = TF_PTR;
- putins(out, IR_ALLOC, count);
+ putins(out, IR_ALLOC, count, VT_IMM);
return t;
}
static void
store(struct iproc *out, uint8_t size, uint64_t src, uint64_t dest)
{
- STARTINS(IR_STORE, src);
- putins(out, IR_EXTRA, dest);
+ STARTINS(IR_STORE, src, VT_TEMP);
+ putins(out, IR_EXTRA, dest, VT_TEMP);
}
static uint64_t
@@ -204,24 +228,24 @@ genexpr(struct iproc *out, size_t expri)
uint64_t right = genexpr(out, expr->d.bop.right), right2;
if (out->temps.data[left].size < out->temps.data[right].size) {
left2 = assign(out, out->temps.data[right].size);
- putins(out, IR_ZEXT, left);
+ putins(out, IR_ZEXT, left, VT_TEMP);
} else left2 = left;
if (out->temps.data[left].size > out->temps.data[right].size) {
right2 = assign(out, out->temps.data[left].size);
- putins(out, IR_ZEXT, right);
+ putins(out, IR_ZEXT, right, VT_TEMP);
} else right2 = right;
temp1 = assign(out, out->temps.data[left2].size);
switch (expr->d.bop.kind) {
case BOP_PLUS:
- putins(out, IR_ADD, left2);
+ putins(out, IR_ADD, left2, VT_TEMP);
break;
case BOP_EQUAL:
- putins(out, IR_CEQ, left2);
+ putins(out, IR_CEQ, left2, VT_TEMP);
break;
default:
die("genexpr: EXPR_BINARY: unhandled binop kind");
}
- putins(out, IR_EXTRA, right2);
+ putins(out, IR_EXTRA, right2, VT_TEMP);
break;
}
case EXPR_UNARY: {
@@ -257,9 +281,9 @@ genexpr(struct iproc *out, size_t expri)
out_index = alloc(out, 8, 1);
}
params[expr->d.call.params.len] = out_index;
- STARTINS(IR_CALL, proc);
+ STARTINS(IR_CALL, proc, VT_FUNC);
for (size_t i = expr->d.call.params.len; i <= expr->d.call.params.len; i--) {
- putins(out, IR_CALLARG, params[i]);
+ putins(out, IR_CALLARG, params[i], VT_TEMP);
}
out_index = 0;
@@ -270,13 +294,15 @@ genexpr(struct iproc *out, size_t expri)
size_t condtmp = genexpr(out, expr->d.cond.cond);
size_t elselabel = labeli++;
size_t endlabel = labeli++;
- STARTINS(IR_CONDJUMP, elselabel);
- putins(out, IR_EXTRA, condtmp);
+ STARTINS(IR_CONDJUMP, elselabel, VT_LABEL);
+ putins(out, IR_EXTRA, condtmp, VT_TEMP);
genblock(out, &expr->d.cond.bif);
- STARTINS(IR_JUMP, endlabel);
- putins(out, IR_LABEL, elselabel);
- genblock(out, &expr->d.cond.belse);
- putins(out, IR_LABEL, endlabel);
+ STARTINS(IR_JUMP, endlabel, VT_LABEL);
+ if (expr->d.cond.belse.len) {
+ putins(out, IR_LABEL, elselabel, VT_LABEL);
+ genblock(out, &expr->d.cond.belse);
+ putins(out, IR_LABEL, endlabel, VT_LABEL);
+ }
break;
}
default:
@@ -355,7 +381,7 @@ genblock(struct iproc *out, struct block *block)
genexpr(out, item->idx);
break;
case ITEM_RETURN:
- STARTINS(IR_RETURN, 0);
+ STARTINS(IR_RETURN, 0, VT_EMPTY);
break;
default:
die("ir_genproc: unreachable");
@@ -409,10 +435,10 @@ genproc(struct decl *decl, struct proc *proc)
type = &types.data[proc->in.data[j].type];
size_t what = NEWTMP;
decl->index = what;
- STARTINS(IR_ASSIGN, what);
+ STARTINS(IR_ASSIGN, what, VT_TEMP);
iproc.temps.data[what].flags = TF_INT; // FIXME: move this to a separate function?
iproc.temps.data[what].size = type->size; // FIXME: should we check that it's a power of 2?
- putins(out, IR_IN, i);
+ putins(out, IR_IN, i, VT_IMM);
}
for (size_t j = 0; j < proc->out.len; j++, i++) {
@@ -420,10 +446,10 @@ genproc(struct decl *decl, struct proc *proc)
type = &types.data[proc->out.data[j].type];
size_t what = NEWTMP;
decl->index = what;
- STARTINS(IR_ASSIGN, what);
+ STARTINS(IR_ASSIGN, what, VT_TEMP);
iproc.temps.data[what].flags = TF_PTR; // FIXME: move this to a separate function?
iproc.temps.data[what].size = type->size; // FIXME: should we check that it's a power of 2?
- putins(out, IR_IN, i);
+ putins(out, IR_IN, i, VT_IMM);
}
genblock(out, &proc->block);
diff --git a/ir.h b/ir.h
@@ -29,6 +29,13 @@ struct instr {
IR_IN,
IR_EXTRA,
} op;
+ enum {
+ VT_EMPTY = 1,
+ VT_TEMP,
+ VT_IMM,
+ VT_FUNC,
+ VT_LABEL,
+ } valtype;
};
struct temp {
diff --git a/x64.c b/x64.c
@@ -777,16 +777,19 @@ emitblock(struct data *text, struct iproc *proc, struct instr *start, struct ins
switch (ins->op) {
// FIXME: we don't handle jumps backward yet
case IR_JUMP:
+ assert(ins->valtype == VT_LABEL);
total += jmp(text, emitblock(NULL, proc, ins + 1, end, ins->val));
NEXT;
break;
case IR_RETURN:
+ assert(ins->valtype == VT_EMPTY);
total += add_r64_imm(text, RSP, localalloc);
total += pop_r64(text, RBP);
total += ret(text);
NEXT;
break;
case IR_STORE:
+ assert(ins->valtype == VT_TEMP);
src = proc->temps.data[ins->val].reg;
NEXT;
assert(ins->op == IR_EXTRA);
@@ -794,6 +797,7 @@ emitblock(struct data *text, struct iproc *proc, struct instr *start, struct ins
NEXT;
break;
case IR_ASSIGN:
+ assert(ins->valtype == VT_TEMP);
tmp = ins->val;
dest = proc->temps.data[ins->val].reg;
size = proc->temps.data[ins->val].size;
@@ -833,6 +837,7 @@ emitblock(struct data *text, struct iproc *proc, struct instr *start, struct ins
}
NEXT;
if (ins->op == IR_CONDJUMP) {
+ assert(ins->valtype == VT_LABEL);
curi++;
label = ins->val;
NEXT;
@@ -844,12 +849,14 @@ emitblock(struct data *text, struct iproc *proc, struct instr *start, struct ins
}
break;
case IR_ADD:
+ assert(ins->valtype == VT_TEMP);
total += mov_r64_r64(text, dest, proc->temps.data[ins->val].reg);
NEXT;
total += add_r64_r64(text, dest, proc->temps.data[ins->val].reg);
NEXT;
break;
case IR_ZEXT:
+ assert(ins->valtype == VT_TEMP);
if (size == 8) {
switch (proc->temps.data[ins->val].size) {
case 1:
@@ -890,6 +897,7 @@ emitblock(struct data *text, struct iproc *proc, struct instr *start, struct ins
NEXT;
break;
case IR_IMM:
+ assert(ins->valtype == VT_IMM);
total += mov_r64_imm(text, dest, ins->val);
NEXT;
break;
@@ -898,6 +906,7 @@ emitblock(struct data *text, struct iproc *proc, struct instr *start, struct ins
NEXT;
break;
case IR_LOAD:
+ assert(ins->valtype == VT_TEMP);
switch (size) {
case 8:
total += mov_r64_mr64(text, dest, proc->temps.data[ins->val].reg);
@@ -917,6 +926,7 @@ emitblock(struct data *text, struct iproc *proc, struct instr *start, struct ins
NEXT;
break;
case IR_ALLOC:
+ assert(ins->valtype == VT_IMM);
total += mov_r64_r64(text, dest, RSP);
total += sub_r64_imm(text, RSP, 8); // FIXME: hardcoding
localalloc += 8;
@@ -927,6 +937,7 @@ emitblock(struct data *text, struct iproc *proc, struct instr *start, struct ins
}
break;
case IR_CALL:
+ assert(ins->valtype == VT_FUNC);
count = 0;
dest = ins->val;
@@ -955,6 +966,7 @@ emitblock(struct data *text, struct iproc *proc, struct instr *start, struct ins
}
break;
case IR_LABEL:
+ assert(ins->valtype == VT_LABEL);
if (ins->val == end_label)
goto done;