1496 lines · 46.9 KB · C
RawDownload
#ifdef TARGET_DEFS_ONLY // Number of registers available to allocator:#define NB_REGS 19 // x10-x17 aka a0-a7, f10-f17 aka fa0-fa7, xxx, ra, sp#define CONFIG_TCC_ASM #define TREG_R(x) (x) // x = 0..7#define TREG_F(x) (x + 8) // x = 0..7 // Register classes sorted from more general to more precise:#define RC_INT (1 << 0)#define RC_FLOAT (1 << 1)#define RC_R(x) (1 << (2 + (x))) // x = 0..7#define RC_F(x) (1 << (10 + (x))) // x = 0..7 #define RC_IRET (RC_R(0)) // int return register class#define RC_IRE2 (RC_R(1)) // int 2nd return register class#define RC_FRET (RC_F(0)) // float return register class #define REG_IRET (TREG_R(0)) // int return register number#define REG_IRE2 (TREG_R(1)) // int 2nd return register number#define REG_FRET (TREG_F(0)) // float return register number #define PTR_SIZE 8 #define LDOUBLE_SIZE 16#define LDOUBLE_ALIGN 16 #define MAX_ALIGN 16 #define CHAR_IS_UNSIGNED /* define if return values need to be extended explicitely   at caller side (for interfacing with non-TCC compilers) */#define PROMOTE_RET #else#define USING_GLOBALS#include "tcc.h"#include <assert.h> #define UPPER(x)	(((unsigned)(x) + 0x800u) & 0xfffff000)#define LOW_OVERFLOW(x)	UPPER(x)#define SIGN7(x)	((((x) & 0xff) ^ 0x80) - 0x80)#define SIGN11(x)	((((x) & 0xfff) ^ 0x800) - 0x800) ST_DATA const char * const target_machine_defs =    "__riscv\0"    "__riscv_xlen 64\0"    "__riscv_flen 64\0"    "__riscv_div\0"    "__riscv_mul\0"    "__riscv_fdiv\0"    "__riscv_fsqrt\0"    "__riscv_float_abi_double\0"    ; #define XLEN 8 #define TREG_RA 17#define TREG_SP 18 ST_DATA const int reg_classes[NB_REGS] = {  RC_INT | RC_R(0),  RC_INT | RC_R(1),  RC_INT | RC_R(2),  RC_INT | RC_R(3),  RC_INT | RC_R(4),  RC_INT | RC_R(5),  RC_INT | RC_R(6),  RC_INT | RC_R(7),  RC_FLOAT | RC_F(0),  RC_FLOAT | RC_F(1),  RC_FLOAT | RC_F(2),  RC_FLOAT | RC_F(3),  RC_FLOAT | RC_F(4),  RC_FLOAT | RC_F(5),  RC_FLOAT | RC_F(6),  RC_FLOAT | RC_F(7),  0,  1 << TREG_RA,  1 << TREG_SP}; #if defined(CONFIG_TCC_BCHECK)static addr_t func_bound_offset;static unsigned long func_bound_ind;ST_DATA int func_bound_add_epilog;#endif 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 (unsigned)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){    return r >= 8 && r < 16;} ST_FUNC void o(unsigned int c){    int ind1 = ind + 4;    if (nocode_wanted)        return;    if (ind1 > cur_text_section->data_allocated)        section_realloc(cur_text_section, ind1);    write32le(cur_text_section->data + ind, c);    ind = ind1;} static void EIu(uint32_t opcode, uint32_t func3,               uint32_t rd, uint32_t rs1, uint32_t imm){    o(opcode | (func3 << 12) | (rd << 7) | (rs1 << 15) | (imm << 20));} static void ER(uint32_t opcode, uint32_t func3,               uint32_t rd, uint32_t rs1, uint32_t rs2, uint32_t func7){    o(opcode | func3 << 12 | rd << 7 | rs1 << 15 | rs2 << 20 | func7 << 25);} static void EI(uint32_t opcode, uint32_t func3,               uint32_t rd, uint32_t rs1, uint32_t imm){    assert(! LOW_OVERFLOW(imm));    EIu(opcode, func3, rd, rs1, imm);} static void ES(uint32_t opcode, uint32_t func3,               uint32_t rs1, uint32_t rs2, uint32_t imm){    assert(! LOW_OVERFLOW(imm));    o(opcode | (func3 << 12) | ((imm & 0x1f) << 7) | (rs1 << 15)      | (rs2 << 20) | ((imm >> 5) << 25));} // Patch all branches in list pointed to by t to branch to a:ST_FUNC void gsym_addr(int t_, int a_){    uint32_t t = t_;    uint32_t a = a_;    while (t) {        unsigned char *ptr = cur_text_section->data + t;        uint32_t next = read32le(ptr);        uint32_t r = a - t, imm;        if ((r + (1 << 21)) & ~((1U << 22) - 2))          tcc_error("out-of-range branch chain");        imm = (((r >> 12) &  0xff) << 12)            | (((r >> 11) &     1) << 20)            | (((r >>  1) & 0x3ff) << 21)            | (((r >> 20) &     1) << 31);        write32le(ptr, r == 4 ? 0x33 : 0x6f | imm); // nop || j imm        t = next;    }} static int load_symofs(int r, SValue *sv, int forstore, int *new_fc){    int rr, doload = 0, large_addend = 0;    int fc = sv->c.i, v = sv->r & VT_VALMASK;    if (sv->r & VT_SYM) {        Sym label = {0};        assert(v == VT_CONST);        if (sv->sym->type.t & VT_TLS) {            /* TLS Local Exec model: lui + addi + add tp */            rr = is_ireg(r) ? ireg(r) : 5;            greloca(cur_text_section, sv->sym, ind,                    R_RISCV_TPREL_HI20, sv->c.i);            o(0x37 | (rr << 7));  // lui RR, 0 %tprel_hi(sym)            greloca(cur_text_section, sv->sym, ind,                    R_RISCV_TPREL_LO12_I, sv->c.i);            EI(0x13, 0, rr, rr, 0); // addi RR, RR, 0 %tprel_lo(sym)            ER(0x33, 0, rr, rr, 4, 0); // add RR, RR, tp            *new_fc = 0;            return rr;        }        if (sv->sym->type.t & VT_STATIC) { // XXX do this per linker relax            greloca(cur_text_section, sv->sym, ind,                    R_RISCV_PCREL_HI20, sv->c.i);            *new_fc = 0;        } else {            if (LOW_OVERFLOW(fc)){              large_addend = 1;            }            greloca(cur_text_section, sv->sym, ind,                    R_RISCV_GOT_HI20, 0);            doload = 1;        }        label.type.t = VT_VOID | VT_STATIC;	if (!nocode_wanted)            put_extern_sym(&label, cur_text_section, ind, 0);        rr = is_ireg(r) ? ireg(r) : 5;        o(0x17 | (rr << 7));   // auipc RR, 0 %pcrel_hi(sym)+addend        greloca(cur_text_section, &label, ind,                doload || !forstore                  ? R_RISCV_PCREL_LO12_I : R_RISCV_PCREL_LO12_S, 0);        if (doload) {            EI(0x03, 3, rr, rr, 0); // ld RR, 0(RR)            if (large_addend) {                o(0x37 | (6 << 7) | UPPER(fc)); //lui t1, high(fc)                ER(0x33, 0, rr, rr, 6, 0); // add RR, RR, t1                *new_fc = SIGN11(fc);            }        }    } else if (v == VT_LOCAL || v == VT_LLOCAL) {        rr = 8; // s0        if (fc != sv->c.i)          tcc_error("unimp: store(giant local off) (0x%lx)", (long)sv->c.i);        if (LOW_OVERFLOW(fc)) {            rr = is_ireg(r) ? ireg(r) : 5; // t0            o(0x37 | (rr << 7) | UPPER(fc)); //lui RR, upper(fc)            ER(0x33, 0, rr, rr, 8, 0); // add RR, RR, s0            *new_fc = SIGN11(fc);        }    } else      tcc_error("uhh");    return rr;} static void load_large_constant(int rr, int fc, uint32_t pi){    if (fc < 0)	pi++;    o(0x37 | (rr << 7) | UPPER(pi)); // lui RR, up(up(fc))    EI(0x13, 0, rr, rr, SIGN11(pi));   // addi RR, RR, lo(up(fc))    EI(0x13, 1, rr, rr, 12); // slli RR, RR, 12    EI(0x13, 0, rr, rr, SIGN11(((unsigned)fc + (1 << 19)) >> 20)); // addi RR, RR, up(lo(fc))    EI(0x13, 1, rr, rr, 12); // slli RR, RR, 12    EI(0x13, 0, rr, rr, SIGN11(((unsigned)fc + (1 << 7)) >> 8));  // addi RR, RR, lo1(lo(fc))    EI(0x13, 1, rr, rr, 8); // slli RR, RR, 8} ST_FUNC void load(int r, SValue *sv){    int fr = sv->r;    int v = fr & VT_VALMASK;    int rr = is_ireg(r) ? ireg(r) : freg(r);    int fc = sv->c.i;    int bt = sv->type.t & VT_BTYPE;    int align, size;    if (fr & VT_LVAL) {        int func3, opcode = is_freg(r) ? 0x07 : 0x03, br;        size = type_size(&sv->type, &align);        assert (!is_freg(r) || bt == VT_FLOAT || bt == VT_DOUBLE);        if (bt == VT_PTR || bt == VT_FUNC) /* XXX should be done in generic code */          size = PTR_SIZE;        func3 = size == 1 ? 0 : size == 2 ? 1 : size == 4 ? 2 : 3;        if (size < 4 && !is_float(sv->type.t) && (sv->type.t & VT_UNSIGNED))          func3 |= 4;        if (v == VT_LOCAL || (fr & VT_SYM)) {            br = load_symofs(r, sv, 0, &fc);        } else if (v < VT_CONST) {            br = ireg(v);            /*if (LOW_OVERFLOW(fc))              tcc_error("unimp: load(large addend) (0x%x)", fc);*/            fc = 0; // XXX store ofs in LVAL(reg)        } else if (v == VT_LLOCAL) {            br = load_symofs(r, sv, 0, &fc);            EI(0x03, 3, rr, br, fc); // ld RR, fc(BR)            br = rr;            fc = 0;        } else if (v == VT_CONST) {            int64_t si = sv->c.i;            si >>= 32;            if (si != 0) {		load_large_constant(rr, fc, si);                fc = SIGN7(fc);            } else {                o(0x37 | (rr << 7) | UPPER(fc)); //lui RR, upper(fc)                fc = SIGN11(fc);	    }            br = rr;	} else {            tcc_error("unimp: load(non-local lval)");        }        EI(opcode, func3, rr, br, fc); // l[bhwd][u] / fl[wd] RR, fc(BR)    } else if (v == VT_CONST) {        int rb = 0, do32bit = 8, zext = 0;        if (is_float(sv->type.t) && bt != VT_LDOUBLE) {            /* load float/double constant: move bit pattern from int reg */            uint64_t val = sv->c.i;            int is_dbl = bt == VT_DOUBLE;            if (val == 0) {                o(0x53 | (rr << 7) | ((unsigned)(0x78 | is_dbl) << 25));                return;            }            if (is_dbl) {                load_large_constant(6, (int)val, (int)(val >> 32));            } else {                if (LOW_OVERFLOW(fc))                    o(0x37 | (6 << 7) | UPPER(fc)); // lui t1, upper                EI(0x13 | 8, 0, 6, LOW_OVERFLOW(fc) ? 6 : 0, SIGN11(fc)); // addiw t1,...            }            o(0x53 | (rr << 7) | (6 << 15) | ((unsigned)(0x78 | is_dbl) << 25));            return;        }        assert(is_ireg(r) || bt == VT_LDOUBLE);        if (fr & VT_SYM) {            rb = load_symofs(r, sv, 0, &fc);            do32bit = 0;        }        if (do32bit && fc != sv->c.i) {            int64_t si = sv->c.i;            si >>= 32;            if (si != 0) {		load_large_constant(rr, fc, si);                fc = SIGN7(fc);                rb = rr;                do32bit = 0;            } else if (bt == VT_LLONG) {                /* A 32bit unsigned constant for a 64bit type.                   lui always sign extends, so we need to do an explicit zext.*/                zext = 1;            }        }        if (LOW_OVERFLOW(fc))            o(0x37 | (rr << 7) | UPPER(fc)), rb = rr; //lui RR, upper(fc)        if (fc || (rr != rb) || do32bit || (fr & VT_SYM))          EI(0x13 | do32bit, 0, rr, rb, SIGN11(fc)); // addi[w] R, x0|R, FC        if (zext) {            EI(0x13, 1, rr, rr, 32); // slli RR, RR, 32            EI(0x13, 5, rr, rr, 32); // srli RR, RR, 32        }    } else if (v == VT_LOCAL) {        int br = load_symofs(r, sv, 0, &fc);        assert(is_ireg(r));        EI(0x13, 0, rr, br, 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))          ER(0x53, 0, rr, freg(v), freg(v), bt == VT_DOUBLE ? 0x11 : 0x10); //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 {            int func7 = is_ireg(r) ? 0x70 : 0x78;            size = type_size(&sv->type, &align);            if (size == 8)              func7 |= 1;            assert(size == 4 || size == 8);            o(0x53 | (rr << 7) | ((is_freg(v) ? freg(v) : ireg(v)) << 15)              | ((unsigned)func7 << 25)); // fmv.{w.x, x.w, d.x, x.d} RR, VR        }    } else if (v == VT_CMP) {        int op = vtop->cmp_op;        int a = vtop->cmp_r & 0xff;        int b = (vtop->cmp_r >> 8) & 0xff;        int inv = 0;        switch (op) {            case TOK_ULT:            case TOK_UGE:            case TOK_ULE:            case TOK_UGT:            case TOK_LT:            case TOK_GE:            case TOK_LE:            case TOK_GT:                if (op & 1) { // remove [U]GE,GT                    inv = 1;                    op--;                }                if ((op & 7) == 6) { // [U]LE                    int t = a; a = b; b = t;                    inv ^= 1;                }                ER(0x33, (op > TOK_UGT) ? 2 : 3, rr, a, b, 0); // slt[u] d, a, b                if (inv)                  EI(0x13, 4, rr, rr, 1); // xori d, d, 1                break;            case TOK_NE:            case TOK_EQ:                if (rr != a || b)                  ER(0x33, 0, rr, a, b, 0x20); // sub d, a, b                if (op == TOK_NE)                  ER(0x33, 3, rr, 0, rr, 0); // sltu d, x0, d == snez d,d                else                  EI(0x13, 3, rr, rr, 1); // sltiu d, d, 1 == seqz d,d                break;        }    } 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)");} ST_FUNC void store(int r, SValue *sv){    int fr = sv->r & VT_VALMASK;    int rr = is_ireg(r) ? ireg(r) : freg(r), ptrreg;    int fc = sv->c.i;    int bt = sv->type.t & VT_BTYPE;    int align, size = type_size(&sv->type, &align);    assert(!is_float(bt) || is_freg(r) || bt == VT_LDOUBLE);    /* long doubles are in two integer registers, but the load/store       primitives only deal with one, so do as if it's one reg.  */    if (bt == VT_LDOUBLE)      size = align = 8;    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 || (sv->r & VT_SYM)) {        ptrreg = load_symofs(-1, sv, 1, &fc);    } else if (fr < VT_CONST) {        ptrreg = ireg(fr);        /*if (LOW_OVERFLOW(fc))          tcc_error("unimp: store(large addend) (0x%x)", fc);*/        fc = 0; // XXX support offsets regs    } else if (fr == VT_CONST) {        int64_t si = sv->c.i;        ptrreg = 8; // s0        si >>= 32;        if (si != 0) {	    load_large_constant(ptrreg, fc, si);            fc = SIGN7(fc);        } else {            o(0x37 | (ptrreg << 7) | UPPER(fc)); //lui RR, upper(fc)            fc = SIGN11(fc);	}    } else      tcc_error("implement me: %s(!local)", __FUNCTION__);    ES(is_freg(r) ? 0x27 : 0x23,                          // fs... | s...       size == 1 ? 0 : size == 2 ? 1 : size == 4 ? 2 : 3, // ... [wd] | [bhwd]       ptrreg, rr, fc);                                   // RR, fc(base)} static void gcall_or_jmp(int docall){    int tr = docall ? 1 : 5; // ra or t0    if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST &&        ((vtop->r & VT_SYM) && vtop->c.i == (int)vtop->c.i)) {        /* constant symbolic case -> simple relocation */        greloca(cur_text_section, vtop->sym, ind,                R_RISCV_CALL_PLT, (int)vtop->c.i);        o(0x17 | (tr << 7));   // auipc TR, 0 %call(func)        EI(0x67, 0, tr, tr, 0);// jalr  TR, r(TR)    } else if (vtop->r < VT_CONST) {        int r = ireg(vtop->r);        EI(0x67, 0, tr, r, 0);      // jalr TR, 0(R)    } else {        int r = TREG_RA;        load(r, vtop);        r = ireg(r);        EI(0x67, 0, tr, r, 0);      // jalr TR, 0(R)    }} #if defined(CONFIG_TCC_BCHECK) static void gen_bounds_call(int v){    Sym *sym = external_helper_sym(v);     greloca(cur_text_section, sym, ind, R_RISCV_CALL_PLT, 0);    o(0x17 | (1 << 7));   // auipc TR, 0 %call(func)    EI(0x67, 0, 1, 1, 0); // jalr  TR, r(TR)} static void gen_bounds_prolog(void){    /* leave some room for bound checking code */    func_bound_offset = lbounds_section->data_offset;    func_bound_ind = ind;    func_bound_add_epilog = 0;    o(0x00000013);  /* ld a0,#lbound section pointer */    o(0x00000013);    o(0x00000013);  /* nop -> call __bound_local_new */    o(0x00000013);} static void gen_bounds_epilog(void){    addr_t saved_ind;    addr_t *bounds_ptr;    Sym *sym_data;    Sym label = {0}; 

Lines 1–500 of 1496.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t