#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. Load more
#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};