riscv: More insns, operands and arg slots

* load/store of FP ops
* load/store from symbols, VT_LLOCAL, some with large addend
* load of VT_JMP result
* calls with many args (stack slots)
* calls with FP args
* more operations: and/or/xor/div
mob
Michael Matz 2019-07-14 00:26:38 +02:00
parent b1c7520886
commit 9214087259
2 changed files with 194 additions and 48 deletions

2
configure vendored
View File

@ -360,7 +360,7 @@ if test -z "$cross_prefix" ; then
fi
if test -z "$triplet"; then
if test $cpu = "x86_64" -o $cpu = "aarch64" ; then
if test $cpu = "x86_64" -o $cpu = "aarch64" -o $cpu = "riscv64" ; then
if test -f "/usr/lib64/crti.o" ; then
tcc_lddir="lib64"
fi

View File

@ -34,6 +34,7 @@
#define XLEN 8
#define TREG_RA 17
#define TREG_SP 18
ST_DATA const int reg_classes[NB_REGS] = {
RC_INT | RC_R(0),
@ -58,13 +59,21 @@ static int ireg(int r)
{
if (r == TREG_RA)
return 1; // ra
if (r == TREG_SP)
return 2; // sp
assert(r >= 0 && r < 8);
return r + 10; // tccrX --> aX == x(10+X)
}
static int is_ireg(int r)
{
return r < 8 || r == TREG_RA;
return r < 8 || r == TREG_RA || r == TREG_SP;
}
static int freg(int r)
{
assert(r >= 8 && r < 16);
return r - 8 + 10; // tccfX --> faX == f(10+X)
}
static int is_freg(int r)
@ -122,37 +131,71 @@ ST_FUNC void load(int r, SValue *sv)
{
int fr = sv->r;
int v = fr & VT_VALMASK;
int rr = ireg(r);
int rr = is_ireg(r) ? ireg(r) : freg(r);
int fc = sv->c.i;
int bt = sv->type.t & VT_BTYPE;
int align, size = type_size(&sv->type, &align);
if (fr & VT_LVAL) {
int bt = sv->type.t & VT_BTYPE;
int align, size = type_size(&sv->type, &align);
int func3;
if (is_float(bt))
tcc_error("unimp: load-local(float)");
else if (bt == VT_FUNC)
size = PTR_SIZE;
func3 = size == 1 ? 0 : size == 2 ? 1 : size == 4 ? 2 : 3;
if (size < 8 && (sv->type.t & VT_UNSIGNED))
func3 |= 4;
int func3, opcode = 0x03;
if (is_freg(r)) {
assert(bt == VT_DOUBLE || bt == VT_FLOAT);
opcode = 0x07;
func3 = bt == VT_DOUBLE ? 3 : 2;
} else {
assert(is_ireg(r));
if (bt == VT_FUNC)
size = PTR_SIZE;
func3 = size == 1 ? 0 : size == 2 ? 1 : size == 4 ? 2 : 3;
if (size < 8 && !is_float(sv->type.t) && (sv->type.t & VT_UNSIGNED))
func3 |= 4;
}
if (v == VT_LOCAL) {
if (((unsigned)fc + (1 << 11)) >> 12)
tcc_error("unimp: load(large local ofs) (0x%x)", fc);
EI(0x03, func3, rr, 8, fc); // l[bhwd][u] RR, fc(s0)
EI(opcode, func3, rr, 8, fc); // l[bhwd][u]/fl[wd] RR, fc(s0)
} else if (v < VT_CONST) {
EI(0x03, func3, rr, ireg(v), 0); // l[bhwd][u] RR, 0(V)
/*if (((unsigned)fc + (1 << 11)) >> 12)
tcc_error("unimp: load(large addend) (0x%x)", fc);*/
fc = 0; // XXX store ofs in LVAL(reg)
EI(opcode, func3, rr, ireg(v), fc); // l[bhwd][u] RR, 0(V)
} else if (v == VT_CONST && (fr & VT_SYM)) {
static Sym label;
int addend = 0, tempr;
if (1 || ((unsigned)fc + (1 << 11)) >> 12)
addend = fc, fc = 0;
greloca(cur_text_section, sv->sym, ind,
R_RISCV_PCREL_HI20, addend);
if (!label.v) {
label.v = tok_alloc(".L0 ", 4)->tok;
label.type.t = VT_VOID | VT_STATIC;
}
label.c = 0; /* force new local ELF symbol */
put_extern_sym(&label, cur_text_section, ind, 0);
tempr = is_ireg(r) ? rr : ireg(get_reg(RC_INT));
o(0x17 | (tempr << 7)); // auipc TR, 0 %pcrel_hi(sym)+addend
greloca(cur_text_section, &label, ind,
R_RISCV_PCREL_LO12_I, 0);
EI(opcode, func3, rr, tempr, fc); // l[bhwd][u] RR, fc(TR)
} else if (v == VT_LLOCAL) {
int tempr = rr;
if (((unsigned)fc + (1 << 11)) >> 12)
tcc_error("unimp: load(large local ofs) (0x%x)", fc);
if (!is_ireg(r))
tempr = ireg(get_reg(RC_INT));
EI(0x03, 3, tempr, 8, fc); // ld TEMPR, fc(s0)
EI(opcode, func3, rr, tempr, 0); // l[bhwd][u] RR, 0(TEMPR)
} else {
tcc_error("unimp: load(non-local lval)");
}
} else if (v == VT_CONST) {
int rb = 0;
assert(!is_float(sv->type.t) && is_ireg(r));
if (fc != sv->c.i)
tcc_error("unimp: load(very large const)");
if (((unsigned)fc + (1 << 11)) >> 12)
tcc_error("unimp: load(large const) (0x%x)", fc);
if (fr & VT_SYM) {
static Sym label;
greloca(cur_text_section, vtop->sym, ind,
greloca(cur_text_section, sv->sym, ind,
R_RISCV_PCREL_HI20, fc);
if (!label.v) {
label.v = tok_alloc(".L0 ", 4)->tok;
@ -164,24 +207,42 @@ ST_FUNC void load(int r, SValue *sv)
greloca(cur_text_section, &label, ind,
R_RISCV_PCREL_LO12_I, 0);
rb = rr;
fc = 0;
}
if (is_float(sv->type.t))
tcc_error("unimp: load(float)");
EI(0x13, 0, rr, rb, fc); // addi R, x0|R, FC
if (((unsigned)fc + (1 << 11)) >> 12)
o(0x37 | (rr << 7) | ((0x800 + fc) & 0xfffff000)), rb = rr; //lui RR, upper(fc)
EI(0x13, 0, rr, rb, fc << 20 >> 20); // addi R, x0|R, FC
} else if (v == VT_LOCAL) {
assert(is_ireg(r));
if (((unsigned)fc + (1 << 11)) >> 12)
tcc_error("unimp: load(addr large local ofs) (0x%x)", fc);
EI(0x13, 0, rr, 8, fc); // addi R, s0, FC
} else if (v < VT_CONST) {
/* reg-reg */
//assert(!fc); XXX support offseted regs
if (is_freg(r) && is_freg(v))
tcc_error("unimp load: float reg-reg move");
o(0x53 | (rr << 7) | (freg(v) << 15) | (freg(v) << 20) | ((bt == VT_DOUBLE ? 0x11 : 0x10) << 25)); //fsgnj.[sd] RR, V, V == fmv.[sd] RR, V
else if (is_ireg(r) && is_ireg(v))
EI(0x13, 0, rr, ireg(v), 0); // addi RR, V, 0 == mv RR, V
else
tcc_error("ICE: inter-unit reg-reg move");
else {
int func7 = is_ireg(r) ? 0x70 : 0x78;
if (size == 8)
func7 |= 1;
assert(size == 4 || size == 8);
o(0x53 | (rr << 7) | ((is_freg(v) ? freg(v) : ireg(v)) << 15)
| (func7 << 25)); // fmv.{w.x, x.w, d.x, x.d} RR, VR
}
} else if (v == VT_CMP) { // we rely on cmp_r to be the correct result
EI(0x13, 0, rr, vtop->cmp_r, 0); // mv RR, CMP_R
} else if ((v & ~1) == VT_JMP) {
int t = v & 1;
assert(is_ireg(r));
EI(0x13, 0, rr, 0, t); // addi RR, x0, t
gjmp_addr(ind + 8);
gsym(fc);
EI(0x13, 0, rr, 0, t ^ 1); // addi RR, x0, !t
} else
tcc_error("unimp: load(non-const)");
}
@ -189,26 +250,58 @@ ST_FUNC void load(int r, SValue *sv)
ST_FUNC void store(int r, SValue *sv)
{
int fr = sv->r & VT_VALMASK;
int rr = ireg(r);
int rr = is_ireg(r) ? ireg(r) : freg(r);
int fc = sv->c.i;
int ft = sv->type.t;
int bt = ft & VT_BTYPE;
int align, size = type_size(&sv->type, &align);
assert(!is_float(bt) || is_freg(r));
if (bt == VT_STRUCT)
tcc_error("unimp: store(struct)");
if (size > 8)
tcc_error("unimp: large sized store");
assert(sv->r & VT_LVAL);
if (fr == VT_LOCAL) {
if (((unsigned)fc + (1 << 11)) >> 12)
tcc_error("unimp: store(large local off) (0x%x)", fc);
if (is_float(bt))
tcc_error("unimp: store(float)");
if (bt == VT_STRUCT)
tcc_error("unimp: store(struct)");
if (size > 8)
tcc_error("unimp: large sized store");
ES(0x23, size == 1 ? 0 : size == 2 ? 1 : size == 4 ? 2 : 3,
8, rr, fc); // s[bhwd] RR, fc(s0)
if (is_freg(r))
ES(0x27, size == 4 ? 2 : 3, 8, rr, fc); // fs[wd] RR, fc(s0)
else
ES(0x23, size == 1 ? 0 : size == 2 ? 1 : size == 4 ? 2 : 3,
8, rr, fc); // s[bhwd] RR, fc(s0)
} else if (fr < VT_CONST) {
int ptrreg = ireg(fr);
ES(0x23, size == 1 ? 0 : size == 2 ? 1 : size == 4 ? 2 : 3,
ptrreg, rr, 0); // s[bhwd] RR, 0(PTRREG)
/*if (((unsigned)fc + (1 << 11)) >> 12)
tcc_error("unimp: store(large addend) (0x%x)", fc);*/
fc = 0; // XXX support offsets regs
if (is_freg(r))
ES(0x27, size == 4 ? 2 : 3, ptrreg, rr, fc); // fs[wd] RR, fc(PTRREG)
else
ES(0x23, size == 1 ? 0 : size == 2 ? 1 : size == 4 ? 2 : 3,
ptrreg, rr, fc); // s[bhwd] RR, fc(PTRREG)
} else if (sv->r == (VT_CONST | VT_SYM | VT_LVAL)) {
static Sym label;
int tempr, addend = 0;
if (1 || ((unsigned)fc + (1 << 11)) >> 12)
addend = fc, fc = 0;
tempr = ireg(get_reg(RC_INT));
greloca(cur_text_section, sv->sym, ind,
R_RISCV_PCREL_HI20, addend);
if (!label.v) {
label.v = tok_alloc(".L0 ", 4)->tok;
label.type.t = VT_VOID | VT_STATIC;
}
label.c = 0; /* force new local ELF symbol */
put_extern_sym(&label, cur_text_section, ind, 0);
o(0x17 | (tempr << 7)); // auipc TEMPR, 0 %pcrel_hi(sym)+addend
greloca(cur_text_section, &label, ind,
R_RISCV_PCREL_LO12_S, 0);
if (is_freg(r))
ES(0x27, size == 4 ? 2 : 3, tempr, rr, fc); // fs[wd] RR, fc(TEMPR)
else
ES(0x23, size == 1 ? 0 : size == 2 ? 1 : size == 4 ? 2 : 3,
tempr, rr, fc); // s[bhwd] RR, fc(TEMPR)
} else
tcc_error("implement me: %s(!local)", __FUNCTION__);
}
@ -235,23 +328,68 @@ static void gcall(void)
ST_FUNC void gfunc_call(int nb_args)
{
int i, align, size, aireg;
aireg = 0;
int i, align, size, aireg, afreg;
int info[nb_args ? nb_args : 1];
int stack_adj = 0, ofs;
SValue *sv;
Sym *sa;
aireg = afreg = 0;
sa = vtop[-nb_args].type.ref->next;
for (i = 0; i < nb_args; i++) {
size = type_size(&vtop[-i].type, &align);
if (size > 8 || ((vtop[-i].type.t & VT_BTYPE) == VT_STRUCT)
|| is_float(vtop[-i].type.t))
int *pareg;
sv = &vtop[1 + i - nb_args];
sv->type.t &= ~VT_ARRAY; // XXX this should be done in tccgen.c
size = type_size(&sv->type, &align);
if (size > 8 || ((sv->type.t & VT_BTYPE) == VT_STRUCT))
tcc_error("unimp: call arg %d wrong type", nb_args - i);
if (aireg >= 8)
tcc_error("unimp: too many register args");
vrotb(i+1);
gv(RC_R(nb_args - 1 - aireg));
vrott(i+1);
aireg++;
pareg = sa && is_float(sv->type.t) ? &afreg : &aireg;
if (*pareg < 8) {
info[i] = *pareg + (sa && is_float(sv->type.t) ? 8 : 0);
(*pareg)++;
} else {
info[i] = 16;
stack_adj += (size + align - 1) & -align;
}
if (sa)
sa = sa->next;
}
stack_adj = (stack_adj + 15) & -16;
if (stack_adj) {
EI(0x13, 0, 2, 2, -stack_adj); // addi sp, sp, -adj
for (i = ofs = 0; i < nb_args; i++) {
if (1 && info[i] >= 16) {
vrotb(nb_args - i);
size = type_size(&vtop->type, &align);
/* Once we support offseted regs we can do this:
vset(&vtop->type, TREG_SP | VT_LVAL, ofs);
to construct the lvalue for the outgoing stack slot,
until then we have to jump through hoops. */
vset(&char_pointer_type, TREG_SP, 0);
vpushi(ofs);
gen_op('+');
indir();
vtop->type = vtop[-1].type;
vswap();
vstore();
vrott(nb_args - i);
ofs += (size + align - 1) & -align;
ofs = (ofs + 7) & -8;
}
}
}
for (i = 0; i < nb_args; i++) {
int r = info[nb_args - 1 - i];
if (r < 16) {
vrotb(i+1);
gv(r < 8 ? RC_R(r) : RC_F(r - 8));
vrott(i+1);
}
}
vrotb(nb_args + 1);
gcall();
vtop -= nb_args + 1;
if (stack_adj)
EI(0x13, 0, 2, 2, stack_adj); // addi sp, sp, adj
}
static int func_sub_sp_offset;
@ -433,10 +571,6 @@ static void gen_opil(int op, int ll)
d = ireg(d);
switch (op) {
case '%':
case '&':
case '/':
case '^':
case '|':
case TOK_SAR:
case TOK_SHR:
case TOK_UDIV:
@ -457,6 +591,18 @@ static void gen_opil(int op, int ll)
case '*':
o(0x33 | (d << 7) | (a << 15) | (b << 20) | (0x01 << 25)); //mul d, a, b
break;
case '/':
o(0x33 | (d << 7) | (a << 15) | (b << 20) | (0x01 << 25) | (4 << 12)); //div d, a, b
break;
case '&':
o(0x33 | (d << 7) | (a << 15) | (b << 20) | (7 << 12)); // and d, a, b
break;
case '^':
o(0x33 | (d << 7) | (a << 15) | (b << 20) | (4 << 12)); // xor d, a, b
break;
case '|':
o(0x33 | (d << 7) | (a << 15) | (b << 20) | (6 << 12)); // or d, a, b
break;
case TOK_ULT:
case TOK_UGE: