diff options
| author | Uneven Prankster <unevenprankster@protonmail.com> | 2023-07-12 21:05:57 -0300 |
|---|---|---|
| committer | Uneven Prankster <unevenprankster@protonmail.com> | 2023-07-12 21:05:57 -0300 |
| commit | 111c133b939c15c57c90cd474d55e84928c6307a (patch) | |
| tree | 3e6ed21eaaf21a8f8f4c5c9933972ea476d37b8e /tinycc/riscv64-asm.c | |
| parent | fa2bdd711212ba6b7a94a20971e8bfa281e73296 (diff) | |
Officially past the point of no return. C scripting works!
Diffstat (limited to 'tinycc/riscv64-asm.c')
| -rw-r--r-- | tinycc/riscv64-asm.c | 731 |
1 files changed, 0 insertions, 731 deletions
diff --git a/tinycc/riscv64-asm.c b/tinycc/riscv64-asm.c deleted file mode 100644 index 0c55492..0000000 --- a/tinycc/riscv64-asm.c +++ /dev/null @@ -1,731 +0,0 @@ -/*************************************************************/ -/* - * RISCV64 assembler for TCC - * - */ - -#ifdef TARGET_DEFS_ONLY - -#define CONFIG_TCC_ASM -#define NB_ASM_REGS 32 - -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" - -/* 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 - asm_emit_opcode((0x3 << 2) | 3 | (0 << 12)); - return; - case TOK_ASM_fence_i: // I - asm_emit_opcode((0x3 << 2) | 3| (1 << 12)); - return; - - // System calls - - case TOK_ASM_scall: // I (pseudo) - asm_emit_opcode((0x1C << 2) | 3 | (0 << 12)); - return; - case TOK_ASM_sbreak: // I (pseudo) - asm_emit_opcode((0x1C << 2) | 3 | (0 << 12) | (1 << 20)); - return; - - // Privileged Instructions - - case TOK_ASM_ecall: - asm_emit_opcode((0x1C << 2) | 3 | (0 << 20)); - return; - case TOK_ASM_ebreak: - asm_emit_opcode((0x1C << 2) | 3 | (1 << 20)); - return; - - // Other - - case TOK_ASM_wfi: - asm_emit_opcode((0x1C << 2) | 3 | (0x105 << 20)); - return; - - default: - expect("nullary instruction"); - } -} - -enum { - OPT_REG, - OPT_IM12S, - OPT_IM32, -}; -#define OP_REG (1 << OPT_REG) -#define OP_IM32 (1 << OPT_IM32) -#define OP_IM12S (1 << OPT_IM12S) - -typedef struct Operand { - uint32_t type; - union { - uint8_t reg; - uint16_t regset; - ExprValue e; - }; -} Operand; - -/* Parse a text containing operand and store the result in OP */ -static void parse_operand(TCCState *s1, Operand *op) -{ - ExprValue e; - 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 '$' - } - asm_expr(s1, &e); - op->type = OP_IM32; - op->e = e; - if (!op->e.sym) { - if ((int) op->e.v >= -2048 && (int) op->e.v < 2048) - op->type = OP_IM12S; - } else - expect("operand"); -} - -#define ENCODE_RS1(register_index) ((register_index) << 15) -#define ENCODE_RS2(register_index) ((register_index) << 20) -#define ENCODE_RD(register_index) ((register_index) << 7) - -// Note: Those all map to CSR--so they are pseudo-instructions. -static void asm_unary_opcode(TCCState *s1, int token) -{ - uint32_t opcode = (0x1C << 2) | 3 | (2 << 12); - Operand op; - parse_operand(s1, &op); - if (op.type != OP_REG) { - expect("register"); - return; - } - opcode |= ENCODE_RD(op.reg); - - switch (token) { - 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; - 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)); - return; - } - 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)); - return; - } 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)); - return; - } - /* 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 void asm_binary_opcode(TCCState* s1, int token) -{ - Operand ops[2]; - parse_operand(s1, &ops[0]); - if (tok == ',') - next(); - else - expect("','"); - parse_operand(s1, &ops[1]); - - switch (token) { - case TOK_ASM_lui: - asm_emit_u(token, (0xD << 2) | 3, &ops[0], &ops[1]); - return; - case TOK_ASM_auipc: - asm_emit_u(token, (0x05 << 2) | 3, &ops[0], &ops[1]); - return; - default: - expect("binary instruction"); - } -} - -/* caller: Add funct3, funct7 into opcode */ -static void asm_emit_r(int token, uint32_t opcode, const Operand* rd, const Operand* rs1, const Operand* rs2) -{ - if (rd->type != OP_REG) { - tcc_error("'%s': Expected destination operand that is a register", get_tok_str(token, NULL)); - return; - } - if (rs1->type != OP_REG) { - tcc_error("'%s': Expected first source operand that is a register", get_tok_str(token, NULL)); - return; - } - if (rs2->type != OP_REG) { - tcc_error("'%s': Expected second source operand that is a register or immediate", get_tok_str(token, NULL)); - return; - } - /* R-type instruction: - 31...25 funct7 - 24...20 rs2 - 19...15 rs1 - 14...12 funct3 - 11...7 rd - 6...0 opcode */ - gen_le32(opcode | ENCODE_RD(rd->reg) | ENCODE_RS1(rs1->reg) | ENCODE_RS2(rs2->reg)); -} - -/* caller: Add funct3 into opcode */ -static void asm_emit_i(int token, uint32_t opcode, const Operand* rd, const Operand* rs1, const Operand* rs2) -{ - if (rd->type != OP_REG) { - tcc_error("'%s': Expected destination operand that is a register", get_tok_str(token, NULL)); - return; - } - if (rs1->type != OP_REG) { - tcc_error("'%s': Expected first source operand that is a register", get_tok_str(token, NULL)); - return; - } - if (rs2->type != OP_IM12S) { - tcc_error("'%s': Expected second source operand that is an immediate value between 0 and 4095", get_tok_str(token, NULL)); - return; - } - /* I-type instruction: - 31...20 imm[11:0] - 19...15 rs1 - 14...12 funct3 - 11...7 rd - 6...0 opcode */ - - gen_le32(opcode | ENCODE_RD(rd->reg) | ENCODE_RS1(rs1->reg) | (rs2->e.v << 20)); -} - -static void asm_shift_opcode(TCCState *s1, int token) -{ - Operand ops[3]; - parse_operand(s1, &ops[0]); - if (tok == ',') - next(); - else - expect("','"); - parse_operand(s1, &ops[1]); - if (tok == ',') - next(); - else - expect("','"); - parse_operand(s1, &ops[2]); - - switch (token) { - case TOK_ASM_sll: - asm_emit_r(token, (0xC << 2) | 3 | (1 << 12), &ops[0], &ops[1], &ops[2]); - return; - case TOK_ASM_slli: - asm_emit_i(token, (4 << 2) | 3 | (1 << 12), &ops[0], &ops[1], &ops[2]); - return; - case TOK_ASM_srl: - asm_emit_r(token, (0xC << 2) | 3 | (4 << 12), &ops[0], &ops[1], &ops[2]); - return; - case TOK_ASM_srli: - asm_emit_i(token, (0x4 << 2) | 3 | (5 << 12), &ops[0], &ops[1], &ops[2]); - return; - case TOK_ASM_sra: - asm_emit_r(token, (0xC << 2) | 3 | (5 << 12) | (32 << 25), &ops[0], &ops[1], &ops[2]); - return; - case TOK_ASM_srai: - asm_emit_i(token, (0x4 << 2) | 3 | (5 << 12) | (16 << 26), &ops[0], &ops[1], &ops[2]); - return; - case TOK_ASM_sllw: - asm_emit_r(token, (0xE << 2) | 3 | (1 << 12), &ops[0], &ops[1], &ops[2]); - return; - case TOK_ASM_slliw: - asm_emit_i(token, (6 << 2) | 3 | (1 << 12), &ops[0], &ops[1], &ops[2]); - return; - case TOK_ASM_srlw: - asm_emit_r(token, (0xE << 2) | 3 | (5 << 12), &ops[0], &ops[1], &ops[2]); - return; - case TOK_ASM_srliw: - asm_emit_i(token, (0x6 << 2) | 3 | (5 << 12), &ops[0], &ops[1], &ops[2]); - return; - case TOK_ASM_sraw: - asm_emit_r(token, (0xE << 2) | 3 | (5 << 12), &ops[0], &ops[1], &ops[2]); - return; - case TOK_ASM_sraiw: - asm_emit_i(token, (0x6 << 2) | 3 | (5 << 12), &ops[0], &ops[1], &ops[2]); - return; - default: - expect("shift instruction"); - } -} - -static void asm_data_processing_opcode(TCCState* s1, int token) -{ - Operand ops[3]; - parse_operand(s1, &ops[0]); - if (tok == ',') - next(); - else - expect("','"); - parse_operand(s1, &ops[1]); - if (tok == ',') - next(); - else - expect("','"); - parse_operand(s1, &ops[2]); - - switch (token) { - // Arithmetic (RD,RS1,(RS2|IMM)); R-format, I-format or U-format - - case TOK_ASM_add: - asm_emit_r(token, (0xC << 2) | 3, &ops[0], &ops[1], &ops[2]); - return; - case TOK_ASM_addi: - asm_emit_i(token, (4 << 2) | 3, &ops[0], &ops[1], &ops[2]); - return; - case TOK_ASM_sub: - asm_emit_r(token, (0xC << 2) | 3 | (32 << 25), &ops[0], &ops[1], &ops[2]); - return; - case TOK_ASM_addw: - asm_emit_r(token, (0xE << 2) | 3 | (0 << 12), &ops[0], &ops[1], &ops[2]); - return; - case TOK_ASM_addiw: // 64 bit - asm_emit_i(token, (0x6 << 2) | 3 | (0 << 12), &ops[0], &ops[1], &ops[2]); - return; - case TOK_ASM_subw: - asm_emit_r(token, (0xE << 2) | 3 | (0 << 12) | (32 << 25), &ops[0], &ops[1], &ops[2]); - return; - - // Logical (RD,RS1,(RS2|IMM)); R-format or I-format - - case TOK_ASM_xor: - asm_emit_r(token, (0xC << 2) | 3 | (4 << 12), &ops[0], &ops[1], &ops[2]); - return; - case TOK_ASM_xori: - asm_emit_i(token, (0x4 << 2) | 3 | (4 << 12), &ops[0], &ops[1], &ops[2]); - return; - case TOK_ASM_or: - asm_emit_r(token, (0xC << 2) | 3 | (6 << 12), &ops[0], &ops[1], &ops[2]); - return; - case TOK_ASM_ori: - asm_emit_i(token, (0x4 << 2) | 3 | (6 << 12), &ops[0], &ops[1], &ops[2]); - return; - case TOK_ASM_and: - asm_emit_r(token, (0xC << 2) | 3 | (7 << 12), &ops[0], &ops[1], &ops[2]); - return; - case TOK_ASM_andi: - asm_emit_i(token, (0x4 << 2) | 3 | (7 << 12), &ops[0], &ops[1], &ops[2]); - return; - - // Compare (RD,RS1,(RS2|IMM)); R-format or I-format - - case TOK_ASM_slt: - asm_emit_r(token, (0xC << 2) | 3 | (2 << 12), &ops[0], &ops[1], &ops[2]); - return; - case TOK_ASM_slti: - asm_emit_i(token, (0x4 << 2) | 3 | (2 << 12), &ops[0], &ops[1], &ops[2]); - return; - case TOK_ASM_sltu: - asm_emit_r(token, (0xC << 2) | 3 | (3 << 12), &ops[0], &ops[1], &ops[2]); - return; - case TOK_ASM_sltiu: - asm_emit_i(token, (0x4 << 2) | 3 | (3 << 12), &ops[0], &ops[1], &ops[2]); - return; - default: - expect("known data processing instruction"); - } -} - -/* caller: Add funct3 to opcode */ -static void asm_emit_s(int token, uint32_t opcode, const Operand* rs1, const Operand* rs2, const Operand* imm) -{ - if (rs1->type != OP_REG) { - tcc_error("'%s': Expected first source operand that is a register", get_tok_str(token, NULL)); - return; - } - if (rs2->type != OP_REG) { - tcc_error("'%s': Expected second source operand that is a register", get_tok_str(token, NULL)); - return; - } - if (imm->type != OP_IM12S) { - tcc_error("'%s': Expected third operand that is an immediate value between 0 and 0xfff", get_tok_str(token, NULL)); - return; - } - { - uint16_t v = imm->e.v; - /* S-type instruction: - 31...25 imm[11:5] - 24...20 rs2 - 19...15 rs1 - 14...12 funct3 - 11...7 imm[4:0] - 6...0 opcode - opcode always fixed pos. */ - gen_le32(opcode | ENCODE_RS1(rs1->reg) | ENCODE_RS2(rs2->reg) | ((v & 0x1F) << 7) | ((v >> 5) << 25)); - } -} - -static void asm_data_transfer_opcode(TCCState* s1, int token) -{ - Operand ops[3]; - parse_operand(s1, &ops[0]); - if (ops[0].type != OP_REG) { - expect("register"); - return; - } - if (tok == ',') - next(); - else - expect("','"); - parse_operand(s1, &ops[1]); - if (ops[1].type != OP_REG) { - expect("register"); - return; - } - if (tok == ',') - next(); - else - expect("','"); - parse_operand(s1, &ops[2]); - - switch (token) { - // Loads (RD,RS1,I); I-format - - case TOK_ASM_lb: - asm_emit_i(token, (0x0 << 2) | 3, &ops[0], &ops[1], &ops[2]); - return; - case TOK_ASM_lh: - asm_emit_i(token, (0x0 << 2) | 3 | (1 << 12), &ops[0], &ops[1], &ops[2]); - return; - case TOK_ASM_lw: - asm_emit_i(token, (0x0 << 2) | 3 | (2 << 12), &ops[0], &ops[1], &ops[2]); - return; - case TOK_ASM_lbu: - asm_emit_i(token, (0x0 << 2) | 3 | (4 << 12), &ops[0], &ops[1], &ops[2]); - return; - case TOK_ASM_lhu: - asm_emit_i(token, (0x0 << 2) | 3 | (5 << 12), &ops[0], &ops[1], &ops[2]); - return; - // 64 bit - case TOK_ASM_ld: - asm_emit_i(token, (0x0 << 2) | 3 | (3 << 12), &ops[0], &ops[1], &ops[2]); - return; - case TOK_ASM_lwu: - asm_emit_i(token, (0x0 << 2) | 3 | (6 << 12), &ops[0], &ops[1], &ops[2]); - return; - - // Stores (RS1,RS2,I); S-format - - case TOK_ASM_sb: - asm_emit_s(token, (0x8 << 2) | 3 | (0 << 12), &ops[0], &ops[1], &ops[2]); - return; - case TOK_ASM_sh: - asm_emit_s(token, (0x8 << 2) | 3 | (1 << 12), &ops[0], &ops[1], &ops[2]); - return; - case TOK_ASM_sw: - asm_emit_s(token, (0x8 << 2) | 3 | (2 << 12), &ops[0], &ops[1], &ops[2]); - return; - case TOK_ASM_sd: - asm_emit_s(token, (0x8 << 2) | 3 | (3 << 12), &ops[0], &ops[1], &ops[2]); - return; - - default: - expect("known data transfer instruction"); - } -} - -static void asm_branch_opcode(TCCState* s1, int token) -{ - // Branch (RS1,RS2,IMM); SB-format - uint32_t opcode = (0x18 << 2) | 3; - uint32_t offset = 0; - Operand ops[3]; - parse_operand(s1, &ops[0]); - if (ops[0].type != OP_REG) { - expect("register"); - return; - } - if (tok == ',') - next(); - else - expect("','"); - parse_operand(s1, &ops[1]); - if (ops[1].type != OP_REG) { - expect("register"); - return; - } - if (tok == ',') - next(); - else - expect("','"); - parse_operand(s1, &ops[2]); - - if (ops[2].type != OP_IM12S) { - tcc_error("'%s': Expected third operand that is an immediate value between 0 and 0xfff", get_tok_str(token, NULL)); - return; - } - offset = ops[2].e.v; - if (offset & 1) { - tcc_error("'%s': Expected third operand that is an even immediate value", get_tok_str(token, NULL)); - return; - } - - switch (token) { - case TOK_ASM_beq: - opcode |= 0 << 12; - break; - case TOK_ASM_bne: - opcode |= 1 << 12; - break; - case TOK_ASM_blt: - opcode |= 4 << 12; - break; - case TOK_ASM_bge: - opcode |= 5 << 12; - break; - case TOK_ASM_bltu: - opcode |= 6 << 12; - break; - case TOK_ASM_bgeu: - opcode |= 7 << 12; - break; - default: - expect("known branch instruction"); - } - asm_emit_opcode(opcode | ENCODE_RS1(ops[0].reg) | ENCODE_RS2(ops[1].reg) | (((offset >> 1) & 0xF) << 8) | (((offset >> 5) & 0x1f) << 25) | (((offset >> 11) & 1) << 7) | (((offset >> 12) & 1) << 31)); -} - -ST_FUNC void asm_opcode(TCCState *s1, int token) -{ - switch (token) { - case TOK_ASM_fence: - case TOK_ASM_fence_i: - case TOK_ASM_scall: - case TOK_ASM_sbreak: - case TOK_ASM_ecall: - case TOK_ASM_ebreak: - case TOK_ASM_mrts: - case TOK_ASM_mrth: - case TOK_ASM_hrts: - case TOK_ASM_wfi: - asm_nullary_opcode(s1, token); - return; - - case TOK_ASM_rdcycle: - case TOK_ASM_rdcycleh: - case TOK_ASM_rdtime: - case TOK_ASM_rdtimeh: - case TOK_ASM_rdinstret: - case TOK_ASM_rdinstreth: - asm_unary_opcode(s1, token); - return; - - case TOK_ASM_lui: - case TOK_ASM_auipc: - asm_binary_opcode(s1, token); - return; - - case TOK_ASM_sll: - case TOK_ASM_slli: - case TOK_ASM_srl: - case TOK_ASM_srli: - case TOK_ASM_sra: - case TOK_ASM_srai: - case TOK_ASM_sllw: - case TOK_ASM_slld: - case TOK_ASM_slliw: - case TOK_ASM_sllid: - case TOK_ASM_srlw: - case TOK_ASM_srld: - case TOK_ASM_srliw: - case TOK_ASM_srlid: - case TOK_ASM_sraw: - case TOK_ASM_srad: - case TOK_ASM_sraiw: - case TOK_ASM_sraid: - asm_shift_opcode(s1, token); - return; - - case TOK_ASM_add: - case TOK_ASM_addi: - case TOK_ASM_sub: - case TOK_ASM_addw: - case TOK_ASM_addd: - case TOK_ASM_addiw: - case TOK_ASM_addid: - case TOK_ASM_subw: - case TOK_ASM_subd: - case TOK_ASM_xor: - case TOK_ASM_xori: - case TOK_ASM_or: - case TOK_ASM_ori: - case TOK_ASM_and: - case TOK_ASM_andi: - case TOK_ASM_slt: - case TOK_ASM_slti: - case TOK_ASM_sltu: - case TOK_ASM_sltiu: - asm_data_processing_opcode(s1, token); - return; - - case TOK_ASM_lb: - case TOK_ASM_lh: - case TOK_ASM_lw: - case TOK_ASM_lbu: - case TOK_ASM_lhu: - case TOK_ASM_ld: - case TOK_ASM_lwu: - case TOK_ASM_sb: - case TOK_ASM_sh: - case TOK_ASM_sw: - case TOK_ASM_sd: - asm_data_transfer_opcode(s1, token); - return; - - case TOK_ASM_beq: - case TOK_ASM_bne: - case TOK_ASM_blt: - case TOK_ASM_bge: - case TOK_ASM_bltu: - case TOK_ASM_bgeu: - asm_branch_opcode(s1, token); - return; - - default: - expect("known instruction"); - } -} - -ST_FUNC void subst_asm_operand(CString *add_str, SValue *sv, int modifier) -{ - tcc_error("RISCV64 asm not implemented."); -} - -/* generate prolog and epilog code for asm statement */ -ST_FUNC void asm_gen_code(ASMOperand *operands, int nb_operands, - int nb_outputs, int is_output, - uint8_t *clobber_regs, - int out_reg) -{ -} - -ST_FUNC void asm_compute_constraints(ASMOperand *operands, - int nb_operands, int nb_outputs, - const uint8_t *clobber_regs, - int *pout_reg) -{ -} - -ST_FUNC void asm_clobber(uint8_t *clobber_regs, const char *str) -{ - int reg; - TokenSym *ts; - - if (!strcmp(str, "memory") || - !strcmp(str, "cc") || - !strcmp(str, "flags")) - return; - ts = tok_alloc(str, strlen(str)); - reg = asm_parse_regvar(ts->tok); - if (reg == -1) { - tcc_error("invalid clobber register '%s'", str); - } - clobber_regs[reg] = 1; -} - -ST_FUNC int asm_parse_regvar (int t) -{ - if (t >= TOK_ASM_x0 && t <= TOK_ASM_pc) { /* register name */ - if (t >= TOK_ASM_zero && t <= TOK_ASM_t6) - return t - TOK_ASM_zero; - switch (t) { - case TOK_ASM_s0: - return 8; - case TOK_ASM_pc: - tcc_error("PC register not implemented."); - default: - return t - TOK_ASM_x0; - } - } else - return -1; -} - -/*************************************************************/ -#endif /* ndef TARGET_DEFS_ONLY */ |
