3059 lines · 105.2 KB · C
RawDownload
/*************************************************************//* *  RISCV64 assembler for TCC * */ #ifdef TARGET_DEFS_ONLY #define CONFIG_TCC_ASM/* 32 general purpose + 32 floating point registers */#define NB_ASM_REGS 64 ST_FUNC void g(int c);ST_FUNC void gen_le16(int c);ST_FUNC void gen_le32(int c); /*************************************************************/#else/*************************************************************/#define USING_GLOBALS#include "tcc.h" enum {    OPT_REG,    OPT_IM12S,    OPT_IM32,};// Registers go from 0 to 31. We use next bit to choose general/float#define REG_FLOAT_MASK 0x20#define REG_IS_FLOAT(register_index) ((register_index) & REG_FLOAT_MASK)#define REG_VALUE(register_index)    ((register_index) & (REG_FLOAT_MASK-1))#define C_ENCODE_RS1(register_index) (REG_VALUE(register_index) << 7)#define C_ENCODE_RS2(register_index) (REG_VALUE(register_index) << 2)#define ENCODE_RD(register_index)  (REG_VALUE(register_index) << 7)#define ENCODE_RS1(register_index) (REG_VALUE(register_index) << 15)#define ENCODE_RS2(register_index) (REG_VALUE(register_index) << 20)#define NTH_BIT(b, n) ((b >> n) & 1)#define OP_IM12S (1 << OPT_IM12S)#define OP_IM32 (1 << OPT_IM32)#define OP_REG (1 << OPT_REG) typedef struct Operand {    uint32_t type;    union {        uint8_t reg;        uint16_t regset;        ExprValue e;    };} Operand; static const Operand zero = { OP_REG, { 0 }};static const Operand ra = { OP_REG, { 1 }};static const Operand zimm = { OP_IM12S }; static void asm_binary_opcode(TCCState* s1, int token);ST_FUNC void asm_clobber(uint8_t *clobber_regs, const char *str);ST_FUNC void asm_compute_constraints(ASMOperand *operands, int nb_operands, int nb_outputs, const uint8_t *clobber_regs, int *pout_reg);static void asm_emit_a(int token, uint32_t opcode, const Operand *rs1, const Operand *rs2, const Operand *rd1, int aq, int rl);static void asm_emit_b(int token, uint32_t opcode, const Operand *rs1, const Operand *rs2, const Operand *imm);static void asm_emit_i(int token, uint32_t opcode, const Operand *rd, const Operand *rs1, const Operand *rs2);static void asm_emit_j(int token, uint32_t opcode, const Operand *rd, const Operand *rs2);static void asm_emit_opcode(uint32_t opcode);static void asm_emit_r(int token, uint32_t opcode, const Operand *rd, const Operand *rs1, const Operand *rs2);static void asm_emit_s(int token, uint32_t opcode, const Operand *rs1, const Operand *rs2, const Operand *imm);static void asm_emit_u(int token, uint32_t opcode, const Operand *rd, const Operand *rs2);static void asm_emit_f(int token, uint32_t opcode, const Operand *rd, const Operand *rs1, const Operand *rs2);static void asm_emit_fb(int token, uint32_t opcode, const Operand *rd, const Operand *rs);static void asm_emit_fq(int token, uint32_t opcode, const Operand *rd, const Operand *rs1, const Operand *rs2, const Operand *rs3);ST_FUNC void asm_gen_code(ASMOperand *operands, int nb_operands, int nb_outputs, int is_output, uint8_t *clobber_regs, int out_reg);static void asm_nullary_opcode(TCCState *s1, int token);ST_FUNC void asm_opcode(TCCState *s1, int token);static int asm_parse_csrvar(int t);ST_FUNC int asm_parse_regvar(int t);static void asm_ternary_opcode(TCCState *s1, int token);static void asm_unary_opcode(TCCState *s1, int token);static void asm_branch_opcode(TCCState *s1, int token, int argc);ST_FUNC void gen_expr32(ExprValue *pe);static void parse_operand(TCCState *s1, Operand *op);static void parse_branch_offset_operand(TCCState *s1, Operand *op);static void parse_operands(TCCState *s1, Operand *ops, int count);static void parse_mem_access_operands(TCCState *s1, Operand* ops);ST_FUNC void subst_asm_operand(CString *add_str, SValue *sv, int modifier);/* C extension */static void asm_emit_ca(int token, uint16_t opcode, const Operand *rd, const Operand *rs2);static void asm_emit_cb(int token, uint16_t opcode, const Operand *rs1, const Operand *imm);static void asm_emit_ci(int token, uint16_t opcode, const Operand *rd, const Operand *imm);static void asm_emit_ciw(int token, uint16_t opcode, const Operand *rd, const Operand *imm);static void asm_emit_cj(int token, uint16_t opcode, const Operand *imm);static void asm_emit_cl(int token, uint16_t opcode, const Operand *rd, const Operand *rs1, const Operand *imm);static void asm_emit_cr(int token, uint16_t opcode, const Operand *rd, const Operand *rs2);static void asm_emit_cs(int token, uint16_t opcode, const Operand *rs2, const Operand *rs1, const Operand *imm);static void asm_emit_css(int token, uint16_t opcode, const Operand *rs2, const Operand *imm); /* XXX: make it faster ? */ST_FUNC void g(int c){    int ind1;    if (nocode_wanted)        return;    ind1 = ind + 1;    if (ind1 > cur_text_section->data_allocated)        section_realloc(cur_text_section, ind1);    cur_text_section->data[ind] = c;    ind = ind1;} ST_FUNC void gen_le16 (int i){    g(i);    g(i>>8);} ST_FUNC void gen_le32 (int i){    int ind1;    if (nocode_wanted)        return;    ind1 = ind + 4;    if (ind1 > cur_text_section->data_allocated)        section_realloc(cur_text_section, ind1);    cur_text_section->data[ind++] = i & 0xFF;    cur_text_section->data[ind++] = (i >> 8) & 0xFF;    cur_text_section->data[ind++] = (i >> 16) & 0xFF;    cur_text_section->data[ind++] = (i >> 24) & 0xFF;} ST_FUNC void gen_expr32(ExprValue *pe){    gen_le32(pe->v);} static void asm_emit_opcode(uint32_t opcode) {    gen_le32(opcode);} static void asm_nullary_opcode(TCCState *s1, int token){    switch (token) {    // Sync instructions     case TOK_ASM_fence_i: // I        asm_emit_opcode((0x3 << 2) | 3| (1 << 12));        return;     // System calls     case TOK_ASM_ecall: // I (pseudo)        asm_emit_opcode((0x1C << 2) | 3 | (0 << 12));        return;    case TOK_ASM_ebreak: // I (pseudo)        asm_emit_opcode((0x1C << 2) | 3 | (0 << 12) | (1 << 20));        return;     // Other     case TOK_ASM_nop:        asm_emit_i(token, (4 << 2) | 3, &zero, &zero, &zimm);        return;     case TOK_ASM_wfi:        asm_emit_opcode((0x1C << 2) | 3 | (0x105 << 20));        return;     /* Pseudoinstructions */    case TOK_ASM_ret:        /* jalr zero, x1, 0 */        asm_emit_opcode( 0x67 | (0 << 12) | ENCODE_RS1(1) );        return;     /* C extension */    case TOK_ASM_c_ebreak:        asm_emit_cr(token, 2 | (9 << 12), &zero, &zero);        return;    case TOK_ASM_c_nop:        asm_emit_ci(token, 1, &zero, &zimm);        return;     default:        expect("nullary instruction");    }} /* Parse a text containing operand and store the result in OP */static void parse_operand(TCCState *s1, Operand *op){    ExprValue e = {0};    Sym label = {0};    int8_t reg;     op->type = 0;     if ((reg = asm_parse_regvar(tok)) != -1) {        next(); // skip register name        op->type = OP_REG;        op->reg = (uint8_t) reg;        return;    } else if (tok == '$') {        /* constant value */        next(); // skip '#' or '$'    } else if ((e.v = asm_parse_csrvar(tok)) != -1) {        next();    } else {        asm_expr(s1, &e);    }    op->type = OP_IM32;    op->e = e;    /* compare against unsigned 12-bit maximum */    if (!op->e.sym) {        if ((int) op->e.v >= -0x1000 && (int) op->e.v < 0x1000)            op->type = OP_IM12S;    } else if (op->e.sym->type.t & (VT_EXTERN | VT_STATIC)) {        /* see also: "RISC-V ABIs Specification" V1.0            section 5.2 recommends using a GOT for           "possibly-undefined weak symbols"            section 5.3: "Medium position independent code model"           if this is a non-local symbol: use a GOT           non-local: outside of a pc-relative +- 2 GiB range        */         label.type.t = VT_VOID | VT_STATIC;         /* use the medium PIC model: GOT, auipc, lw */        if (op->e.sym->type.t & VT_STATIC)            greloca(cur_text_section, op->e.sym, ind, R_RISCV_PCREL_HI20, 0);        else            greloca(cur_text_section, op->e.sym, ind, R_RISCV_GOT_HI20, 0);        put_extern_sym(&label, cur_text_section, ind, 0);        greloca(cur_text_section, &label, ind+4, R_RISCV_PCREL_LO12_I, 0);         op->type = OP_IM12S;        op->e.v = 0;    } else {        expect("operand");    }} static void parse_branch_offset_operand(TCCState *s1, Operand *op){    ExprValue e = {0};     asm_expr(s1, &e);    op->type = OP_IM32;    op->e = e;    /* compare against unsigned 12-bit maximum */    if (!op->e.sym) {        if ((int) op->e.v >= -0x1000 && (int) op->e.v < 0x1000)            op->type = OP_IM12S;    } else if (op->e.sym->type.t & (VT_EXTERN | VT_STATIC)) {        /* For extern/static symbols, always use far-branch expansion           since linker relaxation (R_RISCV_RELAX) is not implemented. */        op->type = OP_IM32;        op->e.v = 0;    } else {        expect("operand");    }} static void parse_jump_offset_operand(TCCState *s1, Operand *op){    ExprValue e = {0};     asm_expr(s1, &e);    op->type = OP_IM32;    op->e = e;    /* compare against unsigned 12-bit maximum */    if (!op->e.sym) {        if ((int) op->e.v >= -0x1000 && (int) op->e.v < 0x1000)            op->type = OP_IM12S;    } else if (op->e.sym->type.t & (VT_EXTERN | VT_STATIC)) {        greloca(cur_text_section, op->e.sym, ind, R_RISCV_JAL, 0);        op->type = OP_IM12S;        op->e.v = 0;    } else {        expect("operand");    }} static void parse_operands(TCCState *s1, Operand* ops, int count){    int i;    for (i = 0; i < count; i++) {        if ( i != 0 )            skip(',');        parse_operand(s1, &ops[i]);    }} /* parse `X, imm(Y)` to {X, Y, imm} operands */static void parse_mem_access_operands(TCCState *s1, Operand* ops){     Operand op;     parse_operand(s1, &ops[0]);    skip(',');    if ( tok == '(') {        /* `X, (Y)` case*/        next();        parse_operand(s1, &ops[1]);        skip(')');        ops[2] = zimm;    } else {        parse_operand(s1, &ops[2]);        if ( tok == '('){            /* `X, imm(Y)` case*/            next();            parse_operand(s1, &ops[1]);            skip(')');        } else {            /* `X, Y` case*/            /* we parsed Y thinking it was imm, swap and default imm to zero */            op = ops[2];            ops[1] = ops[2];            ops[2] = op;            ops[2] = zimm;        }    }} /* This is special: First operand is optional */static void asm_jal_opcode(TCCState *s1, int token){    Operand ops[2];     if (token == TOK_ASM_j ){        ops[0] = zero; // j offset    } else if (asm_parse_regvar(tok) == -1) {        ops[0] = ra;   // jal offset    } else {        // jal reg, offset        parse_operand(s1, &ops[0]);        if ( tok == ',') next(); else expect("','");    }    parse_jump_offset_operand(s1, &ops[1]);    asm_emit_j(token, 0x6f, &ops[0], &ops[1]);} /* This is special: It can be a pseudointruction or a instruction */static void asm_jalr_opcode(TCCState *s1, int token){    Operand ops[3];    Operand op;     parse_operand(s1, &ops[0]);    if ( tok == ',')        next();    else {        /* no more operands, it's the pseudoinstruction:         *  jalr rs         * Expand to:         *  jalr ra, 0(rs)         */        asm_emit_i(token, 0x67 | (0 << 12), &ra, &ops[0], &zimm);        return;    }     if ( tok == '(') {        /* `X, (Y)` case*/        next();        parse_operand(s1, &ops[1]);        skip(')');        ops[2] = zimm;    } else {        parse_operand(s1, &ops[2]);        if ( tok == '('){            /* `X, imm(Y)` case*/            next();            parse_operand(s1, &ops[1]);            skip(')');        } else {            /* `X, Y` case*/            /* we parsed Y thinking it was imm, swap and default imm to zero */            op = ops[2];            ops[1] = ops[2];            ops[2] = op;            ops[2] = zimm;        }    }    /* jalr(RD, RS1, IMM); I-format */    asm_emit_i(token, 0x67 | (0 << 12), &ops[0], &ops[1], &ops[2]);}  static void asm_unary_opcode(TCCState *s1, int token){    uint32_t opcode = (0x1C << 2) | 3 | (2 << 12);    Operand op;     parse_operands(s1, &op, 1);    /* Note: Those all map to CSR--so they are pseudo-instructions. */    opcode |= ENCODE_RD(op.reg);     switch (token) {    /* pseudoinstructions */    case TOK_ASM_rdcycle:        asm_emit_opcode(opcode | (0xC00 << 20));        return;    case TOK_ASM_rdcycleh:        asm_emit_opcode(opcode | (0xC80 << 20));        return;    case TOK_ASM_rdtime:        asm_emit_opcode(opcode | (0xC01 << 20) | ENCODE_RD(op.reg));        return;    case TOK_ASM_rdtimeh:        asm_emit_opcode(opcode | (0xC81 << 20) | ENCODE_RD(op.reg));        return;    case TOK_ASM_rdinstret:        asm_emit_opcode(opcode | (0xC02 << 20) | ENCODE_RD(op.reg));        return;    case TOK_ASM_rdinstreth:        asm_emit_opcode(opcode | (0xC82 << 20) | ENCODE_RD(op.reg));        return;    case TOK_ASM_frflags:        asm_emit_opcode(opcode | (0x001 << 20) | ENCODE_RD(op.reg));        return;    case TOK_ASM_frrm:        asm_emit_opcode(opcode | (0x002 << 20) | ENCODE_RD(op.reg));        return;    case TOK_ASM_frcsr:        asm_emit_opcode(opcode | (0x003 << 20) | ENCODE_RD(op.reg));        return;     case TOK_ASM_jr:        /* jalr zero, 0(rs)*/        asm_emit_i(token, 0x67 | (0 << 12), &zero, &op, &zimm);        return;    case TOK_ASM_call:        /* auipc ra, 0 */        greloca(cur_text_section, op.e.sym, ind, R_RISCV_CALL, 0);        asm_emit_opcode(3 | (5 << 2) | ENCODE_RD(1));        /* jalr zero, 0(ra) */        asm_emit_opcode(0x67 | (0 << 12) | ENCODE_RS1(1));        return;    case TOK_ASM_tail:        /* auipc x6, 0 */        greloca(cur_text_section, op.e.sym, ind, R_RISCV_CALL, 0);        asm_emit_opcode(3 | (5 << 2) | ENCODE_RD(6));        /* jalr zero, 0(x6) */        asm_emit_opcode(0x67 | (0 << 12) | ENCODE_RS1(6));        return;     /* C extension */    case TOK_ASM_c_j:        asm_emit_cj(token, 1 | (5 << 13), &op);        return;    case TOK_ASM_c_jal: /* RV32C-only */        asm_emit_cj(token, 1 | (1 << 13), &op);        return;    case TOK_ASM_c_jalr:        asm_emit_cr(token, 2 | (9 << 12), &op, &zero);        return;    case TOK_ASM_c_jr:        asm_emit_cr(token, 2 | (8 << 12), &op, &zero);        return;     default:        expect("unary instruction");    }} static void asm_emit_u(int token, uint32_t opcode, const Operand* rd, const Operand* rs2){    if (rd->type != OP_REG) {        tcc_error("'%s': Expected destination operand that is a register", get_tok_str(token, NULL));    }    if (rs2->type != OP_IM12S && rs2->type != OP_IM32) {        tcc_error("'%s': Expected second source operand that is an immediate value", get_tok_str(token, NULL));    } else if (rs2->e.v >= 0x100000) {        tcc_error("'%s': Expected second source operand that is an immediate value between 0 and 0xfffff", get_tok_str(token, NULL));    }    /* U-type instruction:	      31...12 imm[31:12]	      11...7 rd	      6...0 opcode */    gen_le32(opcode | ENCODE_RD(rd->reg) | (rs2->e.v << 12));} static int parse_fence_operand(){    int t = tok;    if ( tok == TOK_ASM_or ){        // we are in a fence instruction, parse as output read        t = TOK_ASM_or_fence;    }    next();    return t - (TOK_ASM_w_fence - 1);} static void asm_fence_opcode(TCCState *s1, int token){    // `fence` is both an instruction and a pseudoinstruction:    // `fence` expands to `fence iorw, iorw`    int succ = 0xF, pred = 0xF;    if (tok != TOK_LINEFEED && tok != ';' && tok != CH_EOF){        pred = parse_fence_operand();        if ( pred > 0xF || pred < 0) {            tcc_error("'%s': Expected first operand that is a valid predecessor operand", get_tok_str(token, NULL));        }        skip(',');        succ = parse_fence_operand();        if ( succ > 0xF || succ < 0) {            tcc_error("'%s': Expected second operand that is a valid successor operand", get_tok_str(token, NULL));        }    }    asm_emit_opcode((0x3 << 2) | 3 | (0 << 12) | succ<<20 | pred<<24);}

Lines 1–500 of 3059.

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