commit c4821419279cd539275cbee67d8fcf76c7d3ec61
parent f1d18ddf65c8f1530e67d88b9c2e1736bd6f713e
Author: Nihal Jere <nihal@nihaljere.xyz>
Date: Thu, 13 Jan 2022 19:32:33 -0600
ir: add zext instruction to enable differentiating different widths
Currently, zext is used to load operands in binops, when one is smaller
than the other. This will change when signed integers are introduced.
Diffstat:
6 files changed, 107 insertions(+), 9 deletions(-)
diff --git a/ir.c b/ir.c
@@ -89,6 +89,7 @@ bumpinterval(struct iproc *out, struct instr *instr, size_t index) {
case IR_LOAD:
case IR_ADD:
case IR_CEQ:
+ case IR_ZEXT:
case IR_ASSIGN:
case IR_CALLARG:
case IR_IN:
@@ -182,25 +183,33 @@ genexpr(struct iproc *out, size_t expri)
} else if (decl->in) {
temp1 = decl->index;
} else {
- temp1 = load(out, PTRSIZE, decl->index);
+ temp1 = load(out, type->size, decl->index);
}
break;
}
case EXPR_BINARY: {
- uint64_t left = genexpr(out, expr->d.bop.left);
- uint64_t right = genexpr(out, expr->d.bop.right);
+ uint64_t left = genexpr(out, expr->d.bop.left), left2;
+ 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(IR_ZEXT, left);
+ } else left2 = left;
+ if (out->temps.data[left].size > out->temps.data[right].size) {
+ right2 = assign(out, out->temps.data[left].size);
+ PUTINS(IR_ZEXT, right);
+ } else right2 = right;
temp1 = assign(out, PTRSIZE);
switch (expr->d.bop.kind) {
case BOP_PLUS:
- PUTINS(IR_ADD, left); // FIXME: operand size?
+ PUTINS(IR_ADD, left2); // FIXME: operand size?
break;
case BOP_EQUAL:
- PUTINS(IR_CEQ, left);
+ PUTINS(IR_CEQ, left2);
break;
default:
die("genexpr: EXPR_BINARY: unhandled binop kind");
}
- PUTINS(IR_EXTRA, right);
+ PUTINS(IR_EXTRA, right2);
break;
}
case EXPR_UNARY: {
diff --git a/ir.h b/ir.h
@@ -20,6 +20,9 @@ struct instr {
// comparison
IR_CEQ,
+ // extension
+ IR_ZEXT,
+
// glue
IR_ASSIGN,
IR_CALLARG,
diff --git a/test/add_ext16.pass.nooc b/test/add_ext16.pass.nooc
@@ -0,0 +1,9 @@
+let main proc() = proc() {
+ let a i16 = 10
+ let b i64 = + 20 a
+ if = b 30 {
+ syscall2(60, 0)
+ } else {
+ syscall2(60, 1)
+ }
+}+
\ No newline at end of file
diff --git a/test/add_ext8.pass.nooc b/test/add_ext8.pass.nooc
@@ -0,0 +1,9 @@
+let main proc() = proc() {
+ let a i8 = 10
+ let b i64 = + 20 a
+ if = b 30 {
+ syscall2(60, 0)
+ } else {
+ syscall2(60, 1)
+ }
+}+
\ No newline at end of file
diff --git a/util.c b/util.c
@@ -145,7 +145,7 @@ dumpir(struct iproc *instrs)
fprintf(stderr, "imm %lu\n", instr->val);
break;
case IR_ASSIGN:
- fprintf(stderr, "%%%lu = ", instr->val);
+ fprintf(stderr, "%%%lu %hhu= ", instr->val, instrs->temps.data[instr->val].size);
break;
case IR_ALLOC:
fprintf(stderr, "alloc %lu\n", instr->val);
@@ -162,6 +162,9 @@ dumpir(struct iproc *instrs)
case IR_CEQ:
fprintf(stderr, "ceq %%%lu", instr->val);
break;
+ case IR_ZEXT:
+ fprintf(stderr, "zext %%%lu\n", instr->val);
+ break;
case IR_EXTRA:
fprintf(stderr, ", %%%lu\n", instr->val);
break;
diff --git a/x64.c b/x64.c
@@ -415,6 +415,36 @@ mov_disp8_r8_m8(struct data *text, enum reg dest, enum reg src, int8_t disp)
}
static size_t
+movzx_r8_r64(struct data *text, enum reg dest, enum reg src)
+{
+ uint8_t temp;
+ uint8_t rex = REX_W | (dest >= 8 ? REX_R : 0) | (src >= 8 ? REX_B : 0);
+ if (text) {
+ array_addlit(text, rex);
+ array_addlit(text, 0x0F);
+ array_addlit(text, 0xB6);
+ array_addlit(text, (MOD_DIRECT << 6) | (dest << 3) | src);
+ }
+
+ return 4;
+}
+
+static size_t
+movzx_r16_r64(struct data *text, enum reg dest, enum reg src)
+{
+ uint8_t temp;
+ uint8_t rex = REX_W | (dest >= 8 ? REX_R : 0) | (src >= 8 ? REX_B : 0);
+ if (text) {
+ array_addlit(text, rex);
+ array_addlit(text, 0x0F);
+ array_addlit(text, 0xB7);
+ array_addlit(text, (MOD_DIRECT << 6) | (dest << 3) | src);
+ }
+
+ return 4;
+}
+
+static size_t
lea_disp8(struct data *text, enum reg dest, enum reg src, int8_t disp)
{
uint8_t temp;
@@ -702,6 +732,7 @@ emitblock(struct data *text, struct iproc *proc, struct instr *start, struct ins
case IR_ASSIGN:
tmp = ins->val;
dest = proc->temps.data[ins->val].reg;
+ size = proc->temps.data[ins->val].size;
NEXT;
switch (ins->op) {
@@ -727,6 +758,23 @@ emitblock(struct data *text, struct iproc *proc, struct instr *start, struct ins
total += add_r64_r64(text, dest, proc->temps.data[ins->val].reg);
NEXT;
break;
+ case IR_ZEXT:
+ assert(size == 8); // FIXME: should handle all sizes
+ switch (proc->temps.data[ins->val].size) {
+ case 1:
+ total += movzx_r8_r64(text, dest, proc->temps.data[ins->val].reg);
+ break;
+ case 2:
+ total += movzx_r16_r64(text, dest, proc->temps.data[ins->val].reg);
+ break;
+ case 4: // upper 32-bits get cleared automatically in x64
+ total += mov_r32_r32(text, dest, proc->temps.data[ins->val].reg);
+ break;
+ default:
+ die("x64 emitblock: IR_ZEXT, bad size");
+ }
+ NEXT;
+ break;
case IR_IMM:
total += mov_r64_imm(text, dest, ins->val);
NEXT;
@@ -736,7 +784,22 @@ emitblock(struct data *text, struct iproc *proc, struct instr *start, struct ins
NEXT;
break;
case IR_LOAD:
- total += mov_r64_mr64(text, dest, proc->temps.data[ins->val].reg);
+ switch (size) {
+ case 8:
+ total += mov_r64_mr64(text, dest, proc->temps.data[ins->val].reg);
+ break;
+ case 4:
+ total += mov_r32_mr32(text, dest, proc->temps.data[ins->val].reg);
+ break;
+ case 2:
+ total += mov_r16_mr16(text, dest, proc->temps.data[ins->val].reg);
+ break;
+ case 1:
+ total += mov_r8_mr8(text, dest, proc->temps.data[ins->val].reg);
+ break;
+ default:
+ die("x64 emitblock: IR_LOAD: bad size");
+ }
NEXT;
break;
case IR_ALLOC:
@@ -787,7 +850,7 @@ emitblock(struct data *text, struct iproc *proc, struct instr *start, struct ins
case IR_ALLOC:
die("x64 emitblock: invalid start of instruction");
default:
- die("x64 emitproc: unknown instruction");
+ die("x64 emitblock: unknown instruction");
}
}