libqbe/lisc/emit.c
Quentin Carbonneaux 2c6f6b4b2d add truncation operation
It would be nicer to have some kind of subtyping where a
l variable can be used in place of a w variable but it is
more complications than a truncation insutruction.

In some cases, it might be less efficient than the above
solution by requiring two registers instead of one.
(I.e when both the truncated variable and the long variable
live together.)

We have to see what are the usage patterns.
2015-09-15 23:01:32 -04:00

355 lines
7.1 KiB
C

#include "lisc.h"
enum { SLong, SWord, SShort, SByte };
static char *rsub[][4] = {
[RXX] = {"BLACK CAT", "BROKEN MIRROR", "666", "NOOOO!"},
[RAX] = {"rax", "eax", "ax", "al"},
[RBX] = {"rbx", "ebx", "bx", "bl"},
[RCX] = {"rcx", "ecx", "cx", "cl"},
[RDX] = {"rdx", "edx", "dx", "dl"},
[RSI] = {"rsi", "esi", "si", "sil"},
[RDI] = {"rdi", "edi", "di", "dil"},
[RBP] = {"rbp", "ebp", "bp", "bpl"},
[RSP] = {"rsp", "esp", "sp", "spl"},
[R8 ] = {"r8" , "r8d", "r8w", "r8b"},
[R9 ] = {"r9" , "r9d", "r9w", "r9b"},
[R10] = {"r10", "r10d", "r10w", "r10b"},
[R11] = {"r11", "r11d", "r11w", "r11b"},
[R12] = {"r12", "r12d", "r12w", "r12b"},
[R13] = {"r13", "r13d", "r13w", "r13b"},
[R14] = {"r14", "r14d", "r14w", "r14b"},
[R15] = {"r15", "r15d", "r15w", "r15b"},
};
static char *ctoa[NCmp] = {
[Ceq ] = "e",
[Csle] = "le",
[Cslt] = "l",
[Csgt] = "g",
[Csge] = "ge",
[Cne ] = "ne",
};
static char *
rtoa(int r)
{
if (r < EAX)
return rsub[r][SLong];
else
return rsub[RBASE(r)][SWord];
}
static int
cneg(int cmp)
{
switch (cmp) {
default: diag("cneg: unhandled comparison");
case Ceq: return Cne;
case Csle: return Csgt;
case Cslt: return Csge;
case Csgt: return Csle;
case Csge: return Cslt;
case Cne: return Ceq;
}
}
static void
econ(Con *c, FILE *f)
{
switch (c->type) {
case CAddr:
fprintf(f, "%s", c->label);
if (c->val)
fprintf(f, "%+"PRId64, c->val);
break;
case CNum:
fprintf(f, "%"PRId64, c->val);
break;
default:
diag("econ: invalid constant");
}
}
static void
eref(Ref r, Fn *fn, FILE *f)
{
switch (rtype(r)) {
default:
diag("emit: invalid reference");
case RTmp:
assert(r.val < Tmp0);
fprintf(f, "%%%s", rtoa(r.val));
break;
case RSlot:
fprintf(f, "-%d(%%rbp)", 4 * r.val);
break;
case RCon:
fprintf(f, "$");
econ(&fn->con[r.val], f);
break;
}
}
static void
emem(Ref r, Fn *fn, FILE *f)
{
switch (rtype(r)) {
default:
diag("emit: invalid memory reference");
case RSlot:
eref(r, fn, f);
break;
case RCon:
econ(&fn->con[r.val], f);
break;
case RTmp:
assert(r.val < EAX);
fprintf(f, "(%%%s)", rtoa(r.val));
break;
}
}
static void
eop(char *op, Ref a, Ref b, Fn *fn, FILE *f)
{
fprintf(f, "\t%s ", op);
eref(a, fn, f);
if (!req(b, R)) {
fprintf(f, ", ");
eref(b, fn, f);
}
fprintf(f, "\n");
}
static void
eins(Ins i, Fn *fn, FILE *f)
{
static char *otoa[NOp] = {
[OAdd] = "add",
[OSub] = "sub",
[OMul] = "imul",
[OAnd] = "and",
[OSext] = "movslq",
[OZext] = "movzlq",
[OLoad] = "mov",
[OLoadss] = "movsw",
[OLoadus] = "movzw",
[OLoadsb] = "movsb",
[OLoadub] = "movzb",
[OXCmpw] = "cmpl",
[OXCmpl] = "cmpq",
[OXTestw] = "testl",
[OXTestl] = "testq",
};
static char *stoa[] = {
[OStorel - OStorel] = "q",
[OStorew - OStorel] = "l",
[OStores - OStorel] = "w",
[OStoreb - OStorel] = "b",
};
Ref r0, r1;
int reg;
int64_t val;
switch (i.op) {
case OMul:
if (rtype(i.arg[1]) == RCon) {
r0 = i.arg[1];
r1 = i.arg[0];
} else {
r0 = i.arg[0];
r1 = i.arg[1];
}
if (rtype(r0) == RCon && rtype(r1) == RTmp) {
val = fn->con[r0.val].val;
fprintf(f, "\timul $%"PRId64", %%%s, %%%s\n",
val, rtoa(r1.val), rtoa(i.to.val));
break;
}
/* fall through */
case OAdd:
case OSub:
case OAnd:
if (req(i.to, i.arg[1])) {
if (i.op == OSub) {
eop("neg", i.to, R, fn, f);
eop("add", i.arg[0], i.to, fn, f);
break;
}
i.arg[1] = i.arg[0];
i.arg[0] = i.to;
}
if (!req(i.to, i.arg[0]))
eop("mov", i.arg[0], i.to, fn, f);
eop(otoa[i.op], i.arg[1], i.to, fn, f);
break;
case OSext:
case OZext:
if (rtype(i.to) != RTmp || i.to.val >= EAX
|| (rtype(i.arg[0]) == RTmp && i.arg[0].val < EAX))
diag("emit: invalid extension");
eop(otoa[i.op], i.arg[0], i.to, fn, f);
break;
case OTrunc:
if (rtype(i.to) != RTmp || i.to.val < EAX
|| (rtype(i.arg[0]) == RTmp && i.arg[0].val >= EAX))
diag("emit: invalid truncation");
if (rtype(i.arg[0]) == RTmp)
i.arg[0] = TMP(RWORD(i.arg[0].val));
/* fall through */
case OCopy:
if (req(i.to, R))
break;
if (i.to.val < EAX && rtype(i.arg[0]) == RCon) {
val = fn->con[i.arg[0].val].val;
if (0 <= val && val <= UINT32_MAX) {
fprintf(f, "\tmov $%"PRId64", %%%s\n",
val, rsub[i.to.val][SWord]);
break;
}
} else if (!req(i.arg[0], i.to))
eop("mov", i.arg[0], i.to, fn, f);
break;
case OStorel:
case OStorew:
case OStores:
case OStoreb:
fprintf(f, "\tmov%s ", stoa[i.op - OStorel]);
if (rtype(i.arg[0]) == RTmp) {
assert(i.arg[0].val < Tmp0);
reg = RBASE(i.arg[0].val);
fprintf(f, "%%%s", rsub[reg][i.op - OStorel]);
} else
eref(i.arg[0], fn, f);
fprintf(f, ", ");
emem(i.arg[1], fn, f);
fprintf(f, "\n");
break;
case OLoad:
case OLoadss:
case OLoadus:
case OLoadsb:
case OLoadub:
fprintf(f, "\t%s", otoa[i.op]);
if (i.to.val < EAX)
fprintf(f, "q ");
else
fprintf(f, "l ");
emem(i.arg[0], fn, f);
fprintf(f, ", ");
eref(i.to, fn, f);
fprintf(f, "\n");
break;
case OAlloc:
eop("sub", i.arg[0], TMP(RSP), fn, f);
if (!req(i.to, R))
eop("mov", TMP(RSP), i.to, fn ,f);
break;
case OAddr:
if (rtype(i.arg[0]) != RSlot)
diag("emit: invalid addr instruction");
eop("lea", i.arg[0], i.to, fn, f);
break;
case OSwap:
eop("xchg", i.arg[0], i.arg[1], fn, f);
break;
case OSign:
if (req(i.to, TMP(RDX)) && req(i.arg[0], TMP(RAX)))
fprintf(f, "\tcqto\n");
else if (req(i.to, TMP(EDX)) && req(i.arg[0], TMP(EAX)))
fprintf(f, "\tcltd\n");
else
diag("emit: unhandled instruction (2)");
break;
case OXDiv:
eop("idiv", i.arg[0], R, fn, f);
break;
case OXCmpw:
case OXCmpl:
case OXTestw:
case OXTestl:
eop(otoa[i.op], i.arg[0], i.arg[1], fn, f);
break;
case ONop:
break;
default:
if (OXSet <= i.op && i.op <= OXSet1) {
fprintf(f, "\tset%s %%%s\n",
ctoa[i.op-OXSet],
rsub[RBASE(i.to.val)][SByte]);
fprintf(f, "\tmovzb %%%s, %%%s\n",
rsub[RBASE(i.to.val)][SByte],
rtoa(i.to.val));
break;
}
diag("emit: unhandled instruction (3)");
}
}
static int
framesz(Fn *fn)
{
int i, a, f;
f = 0;
for (i=NAlign-1, a=1<<i; i>=0; i--, a/=2)
if (f == 0 || f - a == fn->svec[i])
f = fn->svec[i];
a = 1 << (NAlign-2);
while ((f + a) % (2 * a) != a)
f += a - f % a;
return f * 16 / (1 << (NAlign-1));
}
void
emitfn(Fn *fn, FILE *f)
{
Blk *b, *s;
Ins *i;
int c, fs;
fprintf(f,
".text\n"
".globl liscf\n"
".type liscf, @function\n"
"liscf:\n"
"\tpush %%rbp\n"
"\tmov %%rsp, %%rbp\n"
);
fs = framesz(fn);
if (fs)
fprintf(f, "\tsub $%d, %%rsp\n", fs);
for (b=fn->start; b; b=b->link) {
fprintf(f, ".L%s:\n", b->name);
for (i=b->ins; i-b->ins < b->nins; i++)
eins(*i, fn, f);
switch (b->jmp.type) {
case JRet:
fprintf(f,
"\tleave\n"
"\tret\n"
);
break;
case JJmp:
if (b->s1 != b->link)
fprintf(f, "\tjmp .L%s\n", b->s1->name);
break;
default:
c = b->jmp.type - JXJc;
if (0 <= c && c <= NCmp) {
if (b->link == b->s2) {
s = b->s1;
} else if (b->link == b->s1) {
c = cneg(c);
s = b->s2;
} else
diag("emit: unhandled jump (1)");
fprintf(f, "\tj%s .L%s\n", ctoa[c], s->name);
break;
}
diag("emit: unhandled jump (2)");
}
}
}