libtcc usability improvements
[tinycc.git] / riscv64-asm.c
blob7f61d4cb94039e90e20b586bde2ba4007aa91437
1 /*************************************************************/
2 /*
3 * RISCV64 assembler for TCC
5 */
7 #ifdef TARGET_DEFS_ONLY
9 #define CONFIG_TCC_ASM
10 #define NB_ASM_REGS 32
12 ST_FUNC void g(int c);
13 ST_FUNC void gen_le16(int c);
14 ST_FUNC void gen_le32(int c);
16 /*************************************************************/
17 #else
18 /*************************************************************/
19 #define USING_GLOBALS
20 #include "tcc.h"
22 enum {
23 OPT_REG,
24 OPT_IM12S,
25 OPT_IM32,
27 #define C_ENCODE_RS1(register_index) ((register_index) << 7)
28 #define C_ENCODE_RS2(register_index) ((register_index) << 2)
29 #define ENCODE_RD(register_index) ((register_index) << 7)
30 #define ENCODE_RS1(register_index) ((register_index) << 15)
31 #define ENCODE_RS2(register_index) ((register_index) << 20)
32 #define NTH_BIT(b, n) ((b >> n) & 1)
33 #define OP_IM12S (1 << OPT_IM12S)
34 #define OP_IM32 (1 << OPT_IM32)
35 #define OP_REG (1 << OPT_REG)
37 typedef struct Operand {
38 uint32_t type;
39 union {
40 uint8_t reg;
41 uint16_t regset;
42 ExprValue e;
44 } Operand;
46 static void asm_binary_opcode(TCCState* s1, int token);
47 ST_FUNC void asm_clobber(uint8_t *clobber_regs, const char *str);
48 ST_FUNC void asm_compute_constraints(ASMOperand *operands, int nb_operands, int nb_outputs, const uint8_t *clobber_regs, int *pout_reg);
49 static void asm_emit_b(int token, uint32_t opcode, const Operand *rs1, const Operand *rs2, const Operand *imm);
50 static void asm_emit_i(int token, uint32_t opcode, const Operand *rd, const Operand *rs1, const Operand *rs2);
51 static void asm_emit_j(int token, uint32_t opcode, const Operand *rd, const Operand *rs2);
52 static void asm_emit_opcode(uint32_t opcode);
53 static void asm_emit_r(int token, uint32_t opcode, const Operand *rd, const Operand *rs1, const Operand *rs2);
54 static void asm_emit_s(int token, uint32_t opcode, const Operand *rs1, const Operand *rs2, const Operand *imm);
55 static void asm_emit_u(int token, uint32_t opcode, const Operand *rd, const Operand *rs2);
56 ST_FUNC void asm_gen_code(ASMOperand *operands, int nb_operands, int nb_outputs, int is_output, uint8_t *clobber_regs, int out_reg);
57 static void asm_nullary_opcode(TCCState *s1, int token);
58 ST_FUNC void asm_opcode(TCCState *s1, int token);
59 static int asm_parse_csrvar(int t);
60 ST_FUNC int asm_parse_regvar(int t);
61 static void asm_ternary_opcode(TCCState *s1, int token);
62 static void asm_unary_opcode(TCCState *s1, int token);
63 ST_FUNC void gen_expr32(ExprValue *pe);
64 static void parse_operand(TCCState *s1, Operand *op);
65 ST_FUNC void subst_asm_operand(CString *add_str, SValue *sv, int modifier);
66 /* C extension */
67 static void asm_emit_ca(int token, uint16_t opcode, const Operand *rd, const Operand *rs2);
68 static void asm_emit_cb(int token, uint16_t opcode, const Operand *rs1, const Operand *imm);
69 static void asm_emit_ci(int token, uint16_t opcode, const Operand *rd, const Operand *imm);
70 static void asm_emit_ciw(int token, uint16_t opcode, const Operand *rd, const Operand *imm);
71 static void asm_emit_cj(int token, uint16_t opcode, const Operand *imm);
72 static void asm_emit_cl(int token, uint16_t opcode, const Operand *rd, const Operand *rs1, const Operand *imm);
73 static void asm_emit_cr(int token, uint16_t opcode, const Operand *rd, const Operand *rs2);
74 static void asm_emit_cs(int token, uint16_t opcode, const Operand *rs2, const Operand *rs1, const Operand *imm);
75 static void asm_emit_css(int token, uint16_t opcode, const Operand *rs2, const Operand *imm);
77 /* XXX: make it faster ? */
78 ST_FUNC void g(int c)
80 int ind1;
81 if (nocode_wanted)
82 return;
83 ind1 = ind + 1;
84 if (ind1 > cur_text_section->data_allocated)
85 section_realloc(cur_text_section, ind1);
86 cur_text_section->data[ind] = c;
87 ind = ind1;
90 ST_FUNC void gen_le16 (int i)
92 g(i);
93 g(i>>8);
96 ST_FUNC void gen_le32 (int i)
98 int ind1;
99 if (nocode_wanted)
100 return;
101 ind1 = ind + 4;
102 if (ind1 > cur_text_section->data_allocated)
103 section_realloc(cur_text_section, ind1);
104 cur_text_section->data[ind++] = i & 0xFF;
105 cur_text_section->data[ind++] = (i >> 8) & 0xFF;
106 cur_text_section->data[ind++] = (i >> 16) & 0xFF;
107 cur_text_section->data[ind++] = (i >> 24) & 0xFF;
110 ST_FUNC void gen_expr32(ExprValue *pe)
112 gen_le32(pe->v);
115 static void asm_emit_opcode(uint32_t opcode) {
116 gen_le32(opcode);
119 static void asm_nullary_opcode(TCCState *s1, int token)
121 static const Operand nil = {.type = OP_REG};
122 static const Operand zimm = {.type = OP_IM12S};
124 switch (token) {
125 // Sync instructions
127 case TOK_ASM_fence: // I
128 asm_emit_opcode((0x3 << 2) | 3 | (0 << 12));
129 return;
130 case TOK_ASM_fence_i: // I
131 asm_emit_opcode((0x3 << 2) | 3| (1 << 12));
132 return;
134 // System calls
136 case TOK_ASM_ecall: // I (pseudo)
137 asm_emit_opcode((0x1C << 2) | 3 | (0 << 12));
138 return;
139 case TOK_ASM_ebreak: // I (pseudo)
140 asm_emit_opcode((0x1C << 2) | 3 | (0 << 12) | (1 << 20));
141 return;
143 // Other
145 case TOK_ASM_nop:
146 asm_emit_i(token, (4 << 2) | 3, &nil, &nil, &zimm);
147 return;
149 case TOK_ASM_wfi:
150 asm_emit_opcode((0x1C << 2) | 3 | (0x105 << 20));
151 return;
153 /* C extension */
154 case TOK_ASM_c_ebreak:
155 asm_emit_cr(token, 2 | (9 << 12), &nil, &nil);
156 return;
157 case TOK_ASM_c_nop:
158 asm_emit_ci(token, 1, &nil, &zimm);
159 return;
161 default:
162 expect("nullary instruction");
166 /* Parse a text containing operand and store the result in OP */
167 static void parse_operand(TCCState *s1, Operand *op)
169 ExprValue e = {0};
170 Sym label = {0};
171 int8_t reg;
173 op->type = 0;
175 if ((reg = asm_parse_regvar(tok)) != -1) {
176 next(); // skip register name
177 op->type = OP_REG;
178 op->reg = (uint8_t) reg;
179 return;
180 } else if (tok == '$') {
181 /* constant value */
182 next(); // skip '#' or '$'
183 } else if ((e.v = asm_parse_csrvar(tok)) != -1) {
184 next();
185 } else {
186 asm_expr(s1, &e);
188 op->type = OP_IM32;
189 op->e = e;
190 /* compare against unsigned 12-bit maximum */
191 if (!op->e.sym) {
192 if ((int) op->e.v >= -0x1000 && (int) op->e.v < 0x1000)
193 op->type = OP_IM12S;
194 } else if (op->e.sym->type.t & (VT_EXTERN | VT_STATIC)) {
195 label.type.t = VT_VOID | VT_STATIC;
197 /* use the medium PIC model: GOT, auipc, lw */
198 if (op->e.sym->type.t & VT_STATIC)
199 greloca(cur_text_section, op->e.sym, ind, R_RISCV_PCREL_HI20, 0);
200 else
201 greloca(cur_text_section, op->e.sym, ind, R_RISCV_GOT_HI20, 0);
202 put_extern_sym(&label, cur_text_section, ind, 0);
203 greloca(cur_text_section, &label, ind+4, R_RISCV_PCREL_LO12_I, 0);
205 op->type = OP_IM12S;
206 op->e.v = 0;
207 } else {
208 expect("operand");
212 static void asm_unary_opcode(TCCState *s1, int token)
214 uint32_t opcode = (0x1C << 2) | 3 | (2 << 12);
215 Operand op;
216 static const Operand nil = {.type = OP_REG};
218 parse_operand(s1, &op);
219 /* Note: Those all map to CSR--so they are pseudo-instructions. */
220 opcode |= ENCODE_RD(op.reg);
222 switch (token) {
223 /* pseudoinstructions */
224 case TOK_ASM_rdcycle:
225 asm_emit_opcode(opcode | (0xC00 << 20));
226 return;
227 case TOK_ASM_rdcycleh:
228 asm_emit_opcode(opcode | (0xC80 << 20));
229 return;
230 case TOK_ASM_rdtime:
231 asm_emit_opcode(opcode | (0xC01 << 20) | ENCODE_RD(op.reg));
232 return;
233 case TOK_ASM_rdtimeh:
234 asm_emit_opcode(opcode | (0xC81 << 20) | ENCODE_RD(op.reg));
235 return;
236 case TOK_ASM_rdinstret:
237 asm_emit_opcode(opcode | (0xC02 << 20) | ENCODE_RD(op.reg));
238 return;
239 case TOK_ASM_rdinstreth:
240 asm_emit_opcode(opcode | (0xC82 << 20) | ENCODE_RD(op.reg));
241 return;
242 /* C extension */
243 case TOK_ASM_c_j:
244 asm_emit_cj(token, 1 | (5 << 13), &op);
245 return;
246 case TOK_ASM_c_jal: /* RV32C-only */
247 asm_emit_cj(token, 1 | (1 << 13), &op);
248 return;
249 case TOK_ASM_c_jalr:
250 asm_emit_cr(token, 2 | (9 << 12), &op, &nil);
251 return;
252 case TOK_ASM_c_jr:
253 asm_emit_cr(token, 2 | (8 << 12), &op, &nil);
254 return;
255 default:
256 expect("unary instruction");
260 static void asm_emit_u(int token, uint32_t opcode, const Operand* rd, const Operand* rs2)
262 if (rd->type != OP_REG) {
263 tcc_error("'%s': Expected destination operand that is a register", get_tok_str(token, NULL));
264 return;
266 if (rs2->type != OP_IM12S && rs2->type != OP_IM32) {
267 tcc_error("'%s': Expected second source operand that is an immediate value", get_tok_str(token, NULL));
268 return;
269 } else if (rs2->e.v >= 0x100000) {
270 tcc_error("'%s': Expected second source operand that is an immediate value between 0 and 0xfffff", get_tok_str(token, NULL));
271 return;
273 /* U-type instruction:
274 31...12 imm[31:12]
275 11...7 rd
276 6...0 opcode */
277 gen_le32(opcode | ENCODE_RD(rd->reg) | (rs2->e.v << 12));
280 static void asm_binary_opcode(TCCState* s1, int token)
282 Operand ops[2];
283 parse_operand(s1, &ops[0]);
284 if (tok == ',')
285 next();
286 else
287 expect("','");
288 parse_operand(s1, &ops[1]);
290 switch (token) {
291 case TOK_ASM_lui:
292 asm_emit_u(token, (0xD << 2) | 3, &ops[0], &ops[1]);
293 return;
294 case TOK_ASM_auipc:
295 asm_emit_u(token, (0x05 << 2) | 3, &ops[0], &ops[1]);
296 return;
297 case TOK_ASM_jal:
298 asm_emit_j(token, 0x6f, ops, ops + 1);
299 return;
301 /* C extension */
302 case TOK_ASM_c_add:
303 asm_emit_cr(token, 2 | (9 << 12), ops, ops + 1);
304 return;
305 case TOK_ASM_c_mv:
306 asm_emit_cr(token, 2 | (8 << 12), ops, ops + 1);
307 return;
309 case TOK_ASM_c_addi16sp:
310 asm_emit_ci(token, 1 | (3 << 13), ops, ops + 1);
311 return;
312 case TOK_ASM_c_addi:
313 asm_emit_ci(token, 1, ops, ops + 1);
314 return;
315 case TOK_ASM_c_addiw:
316 asm_emit_ci(token, 1 | (1 << 13), ops, ops + 1);
317 return;
318 case TOK_ASM_c_fldsp:
319 asm_emit_ci(token, 2 | (1 << 13), ops, ops + 1);
320 return;
321 case TOK_ASM_c_flwsp: /* RV32FC-only */
322 asm_emit_ci(token, 2 | (3 << 13), ops, ops + 1);
323 return;
324 case TOK_ASM_c_ldsp:
325 asm_emit_ci(token, 2 | (3 << 13), ops, ops + 1);
326 return;
327 case TOK_ASM_c_li:
328 asm_emit_ci(token, 1 | (2 << 13), ops, ops + 1);
329 return;
330 case TOK_ASM_c_lui:
331 asm_emit_ci(token, 1 | (3 << 13), ops, ops + 1);
332 return;
333 case TOK_ASM_c_lwsp:
334 asm_emit_ci(token, 2 | (2 << 13), ops, ops + 1);
335 return;
336 case TOK_ASM_c_slli:
337 asm_emit_ci(token, 2, ops, ops + 1);
338 return;
340 case TOK_ASM_c_addi4spn:
341 asm_emit_ciw(token, 0, ops, ops + 1);
342 return;
344 #define CA (1 | (3 << 10) | (4 << 13))
345 case TOK_ASM_c_addw:
346 asm_emit_ca(token, CA | (1 << 5) | (1 << 12), ops, ops + 1);
347 return;
348 case TOK_ASM_c_and:
349 asm_emit_ca(token, CA | (3 << 5), ops, ops + 1);
350 return;
351 case TOK_ASM_c_or:
352 asm_emit_ca(token, CA | (2 << 5), ops, ops + 1);
353 return;
354 case TOK_ASM_c_sub:
355 asm_emit_ca(token, CA, ops, ops + 1);
356 return;
357 case TOK_ASM_c_subw:
358 asm_emit_ca(token, CA | (1 << 12), ops, ops + 1);
359 return;
360 case TOK_ASM_c_xor:
361 asm_emit_ca(token, CA | (1 << 5), ops, ops + 1);
362 return;
363 #undef CA
365 case TOK_ASM_c_andi:
366 asm_emit_cb(token, 1 | (2 << 10) | (4 << 13), ops, ops + 1);
367 return;
368 case TOK_ASM_c_beqz:
369 asm_emit_cb(token, 1 | (6 << 13), ops, ops + 1);
370 return;
371 case TOK_ASM_c_bnez:
372 asm_emit_cb(token, 1 | (7 << 13), ops, ops + 1);
373 return;
374 case TOK_ASM_c_srai:
375 asm_emit_cb(token, 1 | (1 << 10) | (4 << 13), ops, ops + 1);
376 return;
377 case TOK_ASM_c_srli:
378 asm_emit_cb(token, 1 | (4 << 13), ops, ops + 1);
379 return;
381 case TOK_ASM_c_sdsp:
382 asm_emit_css(token, 2 | (7 << 13), ops, ops + 1);
383 return;
384 case TOK_ASM_c_swsp:
385 asm_emit_css(token, 2 | (6 << 13), ops, ops + 1);
386 return;
387 case TOK_ASM_c_fswsp: /* RV32FC-only */
388 asm_emit_css(token, 2 | (7 << 13), ops, ops + 1);
389 return;
390 case TOK_ASM_c_fsdsp:
391 asm_emit_css(token, 2 | (5 << 13), ops, ops + 1);
392 return;
394 /* pseudoinstructions */
395 /* rd, sym */
396 case TOK_ASM_la:
397 /* auipc rd, 0 */
398 asm_emit_u(token, 3 | (5 << 2), ops, ops + 1);
399 /* lw rd, rd, 0 */
400 asm_emit_i(token, 3 | (2 << 12), ops, ops, ops + 1);
401 return;
402 case TOK_ASM_lla:
403 /* auipc rd, 0 */
404 asm_emit_u(token, 3 | (5 << 2), ops, ops + 1);
405 /* addi rd, rd, 0 */
406 asm_emit_i(token, 3 | (4 << 2), ops, ops, ops + 1);
407 return;
409 default:
410 expect("binary instruction");
414 /* caller: Add funct3, funct7 into opcode */
415 static void asm_emit_r(int token, uint32_t opcode, const Operand* rd, const Operand* rs1, const Operand* rs2)
417 if (rd->type != OP_REG) {
418 tcc_error("'%s': Expected destination operand that is a register", get_tok_str(token, NULL));
419 return;
421 if (rs1->type != OP_REG) {
422 tcc_error("'%s': Expected first source operand that is a register", get_tok_str(token, NULL));
423 return;
425 if (rs2->type != OP_REG) {
426 tcc_error("'%s': Expected second source operand that is a register or immediate", get_tok_str(token, NULL));
427 return;
429 /* R-type instruction:
430 31...25 funct7
431 24...20 rs2
432 19...15 rs1
433 14...12 funct3
434 11...7 rd
435 6...0 opcode */
436 gen_le32(opcode | ENCODE_RD(rd->reg) | ENCODE_RS1(rs1->reg) | ENCODE_RS2(rs2->reg));
439 /* caller: Add funct3 into opcode */
440 static void asm_emit_i(int token, uint32_t opcode, const Operand* rd, const Operand* rs1, const Operand* rs2)
442 if (rd->type != OP_REG) {
443 tcc_error("'%s': Expected destination operand that is a register", get_tok_str(token, NULL));
444 return;
446 if (rs1->type != OP_REG) {
447 tcc_error("'%s': Expected first source operand that is a register", get_tok_str(token, NULL));
448 return;
450 if (rs2->type != OP_IM12S) {
451 tcc_error("'%s': Expected second source operand that is an immediate value between 0 and 8191", get_tok_str(token, NULL));
452 return;
454 /* I-type instruction:
455 31...20 imm[11:0]
456 19...15 rs1
457 14...12 funct3
458 11...7 rd
459 6...0 opcode */
461 gen_le32(opcode | ENCODE_RD(rd->reg) | ENCODE_RS1(rs1->reg) | (rs2->e.v << 20));
464 static void asm_emit_j(int token, uint32_t opcode, const Operand* rd, const Operand* rs2)
466 uint32_t imm;
468 if (rd->type != OP_REG) {
469 tcc_error("'%s': Expected destination operand that is a register", get_tok_str(token, NULL));
470 return;
472 if (rs2->type != OP_IM12S && rs2->type != OP_IM32) {
473 tcc_error("'%s': Expected second source operand that is an immediate value", get_tok_str(token, NULL));
474 return;
477 imm = rs2->e.v;
479 /* even offsets in a +- 1 MiB range */
480 if (imm > 0x1ffffe) {
481 tcc_error("'%s': Expected second source operand that is an immediate value between 0 and 0x1fffff", get_tok_str(token, NULL));
482 return;
485 if (imm & 1) {
486 tcc_error("'%s': Expected second source operand that is an even immediate value", get_tok_str(token, NULL));
487 return;
489 /* J-type instruction:
490 31 imm[20]
491 30...21 imm[10:1]
492 20 imm[11]
493 19...12 imm[19:12]
494 11...7 rd
495 6...0 opcode */
496 gen_le32(opcode | ENCODE_RD(rd->reg) | (((imm >> 20) & 1) << 31) | (((imm >> 1) & 0x3ff) << 21) | (((imm >> 11) & 1) << 20) | (((imm >> 12) & 0xff) << 12));
499 static void asm_ternary_opcode(TCCState *s1, int token)
501 Operand ops[3];
502 parse_operand(s1, &ops[0]);
503 if (tok == ',')
504 next();
505 else
506 expect("','");
507 parse_operand(s1, &ops[1]);
508 if (tok == ',')
509 next();
510 else
511 expect("','");
512 parse_operand(s1, &ops[2]);
514 switch (token) {
515 case TOK_ASM_sll:
516 asm_emit_r(token, (0xC << 2) | 3 | (1 << 12), &ops[0], &ops[1], &ops[2]);
517 return;
518 case TOK_ASM_slli:
519 asm_emit_i(token, (4 << 2) | 3 | (1 << 12), &ops[0], &ops[1], &ops[2]);
520 return;
521 case TOK_ASM_srl:
522 asm_emit_r(token, (0xC << 2) | 3 | (4 << 12), &ops[0], &ops[1], &ops[2]);
523 return;
524 case TOK_ASM_srli:
525 asm_emit_i(token, (0x4 << 2) | 3 | (5 << 12), &ops[0], &ops[1], &ops[2]);
526 return;
527 case TOK_ASM_sra:
528 asm_emit_r(token, (0xC << 2) | 3 | (5 << 12) | (32 << 25), &ops[0], &ops[1], &ops[2]);
529 return;
530 case TOK_ASM_srai:
531 asm_emit_i(token, (0x4 << 2) | 3 | (5 << 12) | (16 << 26), &ops[0], &ops[1], &ops[2]);
532 return;
533 case TOK_ASM_sllw:
534 asm_emit_r(token, (0xE << 2) | 3 | (1 << 12), &ops[0], &ops[1], &ops[2]);
535 return;
536 case TOK_ASM_slliw:
537 asm_emit_i(token, (6 << 2) | 3 | (1 << 12), &ops[0], &ops[1], &ops[2]);
538 return;
539 case TOK_ASM_srlw:
540 asm_emit_r(token, (0xE << 2) | 3 | (5 << 12), &ops[0], &ops[1], &ops[2]);
541 return;
542 case TOK_ASM_srliw:
543 asm_emit_i(token, (0x6 << 2) | 3 | (5 << 12), &ops[0], &ops[1], &ops[2]);
544 return;
545 case TOK_ASM_sraw:
546 asm_emit_r(token, (0xE << 2) | 3 | (5 << 12), &ops[0], &ops[1], &ops[2]);
547 return;
548 case TOK_ASM_sraiw:
549 asm_emit_i(token, (0x6 << 2) | 3 | (5 << 12), &ops[0], &ops[1], &ops[2]);
550 return;
552 // Arithmetic (RD,RS1,(RS2|IMM)); R-format, I-format or U-format
554 case TOK_ASM_add:
555 asm_emit_r(token, (0xC << 2) | 3, &ops[0], &ops[1], &ops[2]);
556 return;
557 case TOK_ASM_addi:
558 asm_emit_i(token, (4 << 2) | 3, &ops[0], &ops[1], &ops[2]);
559 return;
560 case TOK_ASM_sub:
561 asm_emit_r(token, (0xC << 2) | 3 | (32 << 25), &ops[0], &ops[1], &ops[2]);
562 return;
563 case TOK_ASM_addw:
564 asm_emit_r(token, (0xE << 2) | 3 | (0 << 12), &ops[0], &ops[1], &ops[2]);
565 return;
566 case TOK_ASM_addiw: // 64 bit
567 asm_emit_i(token, (0x6 << 2) | 3 | (0 << 12), &ops[0], &ops[1], &ops[2]);
568 return;
569 case TOK_ASM_subw:
570 asm_emit_r(token, (0xE << 2) | 3 | (0 << 12) | (32 << 25), &ops[0], &ops[1], &ops[2]);
571 return;
573 // Logical (RD,RS1,(RS2|IMM)); R-format or I-format
575 case TOK_ASM_xor:
576 asm_emit_r(token, (0xC << 2) | 3 | (4 << 12), &ops[0], &ops[1], &ops[2]);
577 return;
578 case TOK_ASM_xori:
579 asm_emit_i(token, (0x4 << 2) | 3 | (4 << 12), &ops[0], &ops[1], &ops[2]);
580 return;
581 case TOK_ASM_or:
582 asm_emit_r(token, (0xC << 2) | 3 | (6 << 12), &ops[0], &ops[1], &ops[2]);
583 return;
584 case TOK_ASM_ori:
585 asm_emit_i(token, (0x4 << 2) | 3 | (6 << 12), &ops[0], &ops[1], &ops[2]);
586 return;
587 case TOK_ASM_and:
588 asm_emit_r(token, (0xC << 2) | 3 | (7 << 12), &ops[0], &ops[1], &ops[2]);
589 return;
590 case TOK_ASM_andi:
591 asm_emit_i(token, (0x4 << 2) | 3 | (7 << 12), &ops[0], &ops[1], &ops[2]);
592 return;
594 // Compare (RD,RS1,(RS2|IMM)); R-format or I-format
596 case TOK_ASM_slt:
597 asm_emit_r(token, (0xC << 2) | 3 | (2 << 12), &ops[0], &ops[1], &ops[2]);
598 return;
599 case TOK_ASM_slti:
600 asm_emit_i(token, (0x4 << 2) | 3 | (2 << 12), &ops[0], &ops[1], &ops[2]);
601 return;
602 case TOK_ASM_sltu:
603 asm_emit_r(token, (0xC << 2) | 3 | (3 << 12), &ops[0], &ops[1], &ops[2]);
604 return;
605 case TOK_ASM_sltiu:
606 asm_emit_i(token, (0x4 << 2) | 3 | (3 << 12), &ops[0], &ops[1], &ops[2]);
607 return;
609 /* indirect jump (RD, RS1, IMM); I-format */
610 case TOK_ASM_jalr:
611 asm_emit_i(token, 0x67 | (0 << 12), ops, ops + 1, ops + 2);
612 return;
614 /* branch (RS1, RS2, IMM); B-format */
615 case TOK_ASM_beq:
616 asm_emit_b(token, 0x63 | (0 << 12), ops, ops + 1, ops + 2);
617 return;
618 case TOK_ASM_bne:
619 asm_emit_b(token, 0x63 | (1 << 12), ops, ops + 1, ops + 2);
620 return;
621 case TOK_ASM_blt:
622 asm_emit_b(token, 0x63 | (4 << 12), ops, ops + 1, ops + 2);
623 return;
624 case TOK_ASM_bge:
625 asm_emit_b(token, 0x63 | (5 << 12), ops, ops + 1, ops + 2);
626 return;
627 case TOK_ASM_bltu:
628 asm_emit_b(token, 0x63 | (6 << 12), ops, ops + 1, ops + 2);
629 return;
630 case TOK_ASM_bgeu:
631 asm_emit_b(token, 0x63 | (7 << 12), ops, ops + 1, ops + 2);
632 return;
634 // Loads (RD,RS1,I); I-format
636 case TOK_ASM_lb:
637 asm_emit_i(token, (0x0 << 2) | 3, &ops[0], &ops[1], &ops[2]);
638 return;
639 case TOK_ASM_lh:
640 asm_emit_i(token, (0x0 << 2) | 3 | (1 << 12), &ops[0], &ops[1], &ops[2]);
641 return;
642 case TOK_ASM_lw:
643 asm_emit_i(token, (0x0 << 2) | 3 | (2 << 12), &ops[0], &ops[1], &ops[2]);
644 return;
645 case TOK_ASM_lbu:
646 asm_emit_i(token, (0x0 << 2) | 3 | (4 << 12), &ops[0], &ops[1], &ops[2]);
647 return;
648 case TOK_ASM_lhu:
649 asm_emit_i(token, (0x0 << 2) | 3 | (5 << 12), &ops[0], &ops[1], &ops[2]);
650 return;
651 // 64 bit
652 case TOK_ASM_ld:
653 asm_emit_i(token, (0x0 << 2) | 3 | (3 << 12), &ops[0], &ops[1], &ops[2]);
654 return;
655 case TOK_ASM_lwu:
656 asm_emit_i(token, (0x0 << 2) | 3 | (6 << 12), &ops[0], &ops[1], &ops[2]);
657 return;
659 // Stores (RS1,RS2,I); S-format
661 case TOK_ASM_sb:
662 asm_emit_s(token, (0x8 << 2) | 3 | (0 << 12), &ops[0], &ops[1], &ops[2]);
663 return;
664 case TOK_ASM_sh:
665 asm_emit_s(token, (0x8 << 2) | 3 | (1 << 12), &ops[0], &ops[1], &ops[2]);
666 return;
667 case TOK_ASM_sw:
668 asm_emit_s(token, (0x8 << 2) | 3 | (2 << 12), &ops[0], &ops[1], &ops[2]);
669 return;
670 case TOK_ASM_sd:
671 asm_emit_s(token, (0x8 << 2) | 3 | (3 << 12), &ops[0], &ops[1], &ops[2]);
672 return;
674 /* M extension */
675 case TOK_ASM_div:
676 asm_emit_r(token, 0x33 | (4 << 12) | (1 << 25), ops, ops + 1, ops + 2);
677 return;
678 case TOK_ASM_divu:
679 asm_emit_r(token, 0x33 | (5 << 12) | (1 << 25), ops, ops + 1, ops + 2);
680 return;
681 case TOK_ASM_divuw:
682 asm_emit_r(token, 0x3b | (5 << 12) | (1 << 25), ops, ops + 1, ops + 2);
683 return;
684 case TOK_ASM_divw:
685 asm_emit_r(token, 0x3b | (4 << 12) | (1 << 25), ops, ops + 1, ops + 2);
686 return;
687 case TOK_ASM_mul:
688 asm_emit_r(token, 0x33 | (1 << 25), ops, ops + 1, ops + 2);
689 return;
690 case TOK_ASM_mulh:
691 asm_emit_r(token, 0x33 | (1 << 12) | (1 << 25), ops, ops + 1, ops + 2);
692 return;
693 case TOK_ASM_mulhsu:
694 asm_emit_r(token, 0x33 | (2 << 12) | (1 << 25), ops, ops + 1, ops + 2);
695 return;
696 case TOK_ASM_mulhu:
697 asm_emit_r(token, 0x33 | (3 << 12) | (1 << 25), ops, ops + 1, ops + 2);
698 return;
699 case TOK_ASM_mulw:
700 asm_emit_r(token, 0x3b | (1 << 25), ops, ops + 1, ops + 2);
701 return;
702 case TOK_ASM_rem:
703 asm_emit_r(token, 0x33 | (6 << 12) | (1 << 25), ops, ops + 1, ops + 2);
704 return;
705 case TOK_ASM_remu:
706 asm_emit_r(token, 0x33 | (7 << 12) | (1 << 25), ops, ops + 1, ops + 2);
707 return;
708 case TOK_ASM_remuw:
709 asm_emit_r(token, 0x3b | (7 << 12) | (1 << 25), ops, ops + 1, ops + 2);
710 return;
711 case TOK_ASM_remw:
712 asm_emit_r(token, 0x3b | (6 << 12) | (1 << 25), ops, ops + 1, ops + 2);
713 return;
715 /* Zicsr extension; (rd, csr, rs/uimm) */
716 case TOK_ASM_csrrc:
717 asm_emit_i(token, 0x73 | (3 << 12), ops, ops + 2, ops + 1);
718 return;
719 case TOK_ASM_csrrci:
720 /* using rs1 field for uimmm */
721 ops[2].type = OP_REG;
722 asm_emit_i(token, 0x73 | (7 << 12), ops, ops + 2, ops + 1);
723 return;
724 case TOK_ASM_csrrs:
725 asm_emit_i(token, 0x73 | (2 << 12), ops, ops + 2, ops + 1);
726 return;
727 case TOK_ASM_csrrsi:
728 ops[2].type = OP_REG;
729 asm_emit_i(token, 0x73 | (6 << 12), ops, ops + 2, ops + 1);
730 return;
731 case TOK_ASM_csrrw:
732 asm_emit_i(token, 0x73 | (1 << 12), ops, ops + 2, ops + 1);
733 return;
734 case TOK_ASM_csrrwi:
735 ops[2].type = OP_REG;
736 asm_emit_i(token, 0x73 | (5 << 12), ops, ops + 2, ops + 1);
737 return;
739 /* C extension */
740 /* register-based loads and stores (RD, RS1, IMM); CL-format */
741 case TOK_ASM_c_fld:
742 asm_emit_cl(token, 1 << 13, ops, ops + 1, ops + 2);
743 return;
744 case TOK_ASM_c_flw: /* RV32FC-only */
745 asm_emit_cl(token, 3 << 13, ops, ops + 1, ops + 2);
746 return;
747 case TOK_ASM_c_fsd:
748 asm_emit_cs(token, 5 << 13, ops, ops + 1, ops + 2);
749 return;
750 case TOK_ASM_c_fsw: /* RV32FC-only */
751 asm_emit_cs(token, 7 << 13, ops, ops + 1, ops + 2);
752 return;
753 case TOK_ASM_c_ld:
754 asm_emit_cl(token, 3 << 13, ops, ops + 1, ops + 2);
755 return;
756 case TOK_ASM_c_lw:
757 asm_emit_cl(token, 2 << 13, ops, ops + 1, ops + 2);
758 return;
759 case TOK_ASM_c_sd:
760 asm_emit_cs(token, 7 << 13, ops, ops + 1, ops + 2);
761 return;
762 case TOK_ASM_c_sw:
763 asm_emit_cs(token, 6 << 13, ops, ops + 1, ops + 2);
764 return;
766 default:
767 expect("ternary instruction");
771 /* caller: Add funct3 to opcode */
772 static void asm_emit_s(int token, uint32_t opcode, const Operand* rs1, const Operand* rs2, const Operand* imm)
774 if (rs1->type != OP_REG) {
775 tcc_error("'%s': Expected first source operand that is a register", get_tok_str(token, NULL));
776 return;
778 if (rs2->type != OP_REG) {
779 tcc_error("'%s': Expected second source operand that is a register", get_tok_str(token, NULL));
780 return;
782 if (imm->type != OP_IM12S) {
783 tcc_error("'%s': Expected third operand that is an immediate value between 0 and 8191", get_tok_str(token, NULL));
784 return;
787 uint16_t v = imm->e.v;
788 /* S-type instruction:
789 31...25 imm[11:5]
790 24...20 rs2
791 19...15 rs1
792 14...12 funct3
793 11...7 imm[4:0]
794 6...0 opcode
795 opcode always fixed pos. */
796 gen_le32(opcode | ENCODE_RS1(rs1->reg) | ENCODE_RS2(rs2->reg) | ((v & 0x1F) << 7) | ((v >> 5) << 25));
800 static void asm_emit_b(int token, uint32_t opcode, const Operand *rs1, const Operand *rs2, const Operand *imm)
802 uint32_t offset;
804 if (rs1->type != OP_REG) {
805 tcc_error("'%s': Expected first source operand that is a register", get_tok_str(token, NULL));
806 return;
808 if (rs2->type != OP_REG) {
809 tcc_error("'%s': Expected destination operand that is a register", get_tok_str(token, NULL));
810 return;
812 if (imm->type != OP_IM12S) {
813 tcc_error("'%s': Expected second source operand that is an immediate value between 0 and 8191", get_tok_str(token, NULL));
814 return;
817 offset = imm->e.v;
819 /* B-type instruction:
820 31 imm[12]
821 30...25 imm[10:5]
822 24...20 rs2
823 19...15 rs1
824 14...12 funct3
825 8...11 imm[4:1]
826 7 imm[11]
827 6...0 opcode */
828 asm_emit_opcode(opcode | ENCODE_RS1(rs1->reg) | ENCODE_RS2(rs2->reg) | (((offset >> 1) & 0xF) << 8) | (((offset >> 5) & 0x1f) << 25) | (((offset >> 11) & 1) << 7) | (((offset >> 12) & 1) << 31));
831 ST_FUNC void asm_opcode(TCCState *s1, int token)
833 switch (token) {
834 case TOK_ASM_ebreak:
835 case TOK_ASM_ecall:
836 case TOK_ASM_fence:
837 case TOK_ASM_fence_i:
838 case TOK_ASM_hrts:
839 case TOK_ASM_mrth:
840 case TOK_ASM_mrts:
841 case TOK_ASM_wfi:
842 asm_nullary_opcode(s1, token);
843 return;
845 case TOK_ASM_rdcycle:
846 case TOK_ASM_rdcycleh:
847 case TOK_ASM_rdtime:
848 case TOK_ASM_rdtimeh:
849 case TOK_ASM_rdinstret:
850 case TOK_ASM_rdinstreth:
851 asm_unary_opcode(s1, token);
852 return;
854 case TOK_ASM_lui:
855 case TOK_ASM_auipc:
856 case TOK_ASM_jal:
857 asm_binary_opcode(s1, token);
858 return;
860 case TOK_ASM_add:
861 case TOK_ASM_addi:
862 case TOK_ASM_addiw:
863 case TOK_ASM_addw:
864 case TOK_ASM_and:
865 case TOK_ASM_andi:
866 case TOK_ASM_beq:
867 case TOK_ASM_bge:
868 case TOK_ASM_bgeu:
869 case TOK_ASM_blt:
870 case TOK_ASM_bltu:
871 case TOK_ASM_bne:
872 case TOK_ASM_jalr:
873 case TOK_ASM_lb:
874 case TOK_ASM_lbu:
875 case TOK_ASM_ld:
876 case TOK_ASM_lh:
877 case TOK_ASM_lhu:
878 case TOK_ASM_lw:
879 case TOK_ASM_lwu:
880 case TOK_ASM_or:
881 case TOK_ASM_ori:
882 case TOK_ASM_sb:
883 case TOK_ASM_sd:
884 case TOK_ASM_sh:
885 case TOK_ASM_sll:
886 case TOK_ASM_slli:
887 case TOK_ASM_slliw:
888 case TOK_ASM_sllw:
889 case TOK_ASM_slt:
890 case TOK_ASM_slti:
891 case TOK_ASM_sltiu:
892 case TOK_ASM_sltu:
893 case TOK_ASM_sra:
894 case TOK_ASM_srai:
895 case TOK_ASM_sraiw:
896 case TOK_ASM_sraw:
897 case TOK_ASM_srl:
898 case TOK_ASM_srli:
899 case TOK_ASM_srliw:
900 case TOK_ASM_srlw:
901 case TOK_ASM_sub:
902 case TOK_ASM_subw:
903 case TOK_ASM_sw:
904 case TOK_ASM_xor:
905 case TOK_ASM_xori:
906 /* M extension */
907 case TOK_ASM_div:
908 case TOK_ASM_divu:
909 case TOK_ASM_divuw:
910 case TOK_ASM_divw:
911 case TOK_ASM_mul:
912 case TOK_ASM_mulh:
913 case TOK_ASM_mulhsu:
914 case TOK_ASM_mulhu:
915 case TOK_ASM_mulw:
916 case TOK_ASM_rem:
917 case TOK_ASM_remu:
918 case TOK_ASM_remuw:
919 case TOK_ASM_remw:
920 /* Zicsr extension */
921 case TOK_ASM_csrrc:
922 case TOK_ASM_csrrci:
923 case TOK_ASM_csrrs:
924 case TOK_ASM_csrrsi:
925 case TOK_ASM_csrrw:
926 case TOK_ASM_csrrwi:
927 asm_ternary_opcode(s1, token);
928 return;
930 /* C extension */
931 case TOK_ASM_c_ebreak:
932 case TOK_ASM_c_nop:
933 asm_nullary_opcode(s1, token);
934 return;
936 case TOK_ASM_c_j:
937 case TOK_ASM_c_jal:
938 case TOK_ASM_c_jalr:
939 case TOK_ASM_c_jr:
940 asm_unary_opcode(s1, token);
941 return;
943 case TOK_ASM_c_add:
944 case TOK_ASM_c_addi16sp:
945 case TOK_ASM_c_addi4spn:
946 case TOK_ASM_c_addi:
947 case TOK_ASM_c_addiw:
948 case TOK_ASM_c_addw:
949 case TOK_ASM_c_and:
950 case TOK_ASM_c_andi:
951 case TOK_ASM_c_beqz:
952 case TOK_ASM_c_bnez:
953 case TOK_ASM_c_fldsp:
954 case TOK_ASM_c_flwsp:
955 case TOK_ASM_c_fsdsp:
956 case TOK_ASM_c_fswsp:
957 case TOK_ASM_c_ldsp:
958 case TOK_ASM_c_li:
959 case TOK_ASM_c_lui:
960 case TOK_ASM_c_lwsp:
961 case TOK_ASM_c_mv:
962 case TOK_ASM_c_or:
963 case TOK_ASM_c_sdsp:
964 case TOK_ASM_c_slli:
965 case TOK_ASM_c_srai:
966 case TOK_ASM_c_srli:
967 case TOK_ASM_c_sub:
968 case TOK_ASM_c_subw:
969 case TOK_ASM_c_swsp:
970 case TOK_ASM_c_xor:
971 asm_binary_opcode(s1, token);
972 return;
974 case TOK_ASM_c_fld:
975 case TOK_ASM_c_flw:
976 case TOK_ASM_c_fsd:
977 case TOK_ASM_c_fsw:
978 case TOK_ASM_c_ld:
979 case TOK_ASM_c_lw:
980 case TOK_ASM_c_sd:
981 case TOK_ASM_c_sw:
982 asm_ternary_opcode(s1, token);
983 return;
985 /* pseudoinstructions */
986 case TOK_ASM_nop:
987 asm_nullary_opcode(s1, token);
988 return;
990 case TOK_ASM_la:
991 case TOK_ASM_lla:
992 asm_binary_opcode(s1, token);
993 return;
995 default:
996 expect("known instruction");
1000 static int asm_parse_csrvar(int t)
1002 switch (t) {
1003 case TOK_ASM_cycle:
1004 return 0xc00;
1005 case TOK_ASM_fcsr:
1006 return 3;
1007 case TOK_ASM_fflags:
1008 return 1;
1009 case TOK_ASM_frm:
1010 return 2;
1011 case TOK_ASM_instret:
1012 return 0xc02;
1013 case TOK_ASM_time:
1014 return 0xc01;
1015 case TOK_ASM_cycleh:
1016 return 0xc80;
1017 case TOK_ASM_instreth:
1018 return 0xc82;
1019 case TOK_ASM_timeh:
1020 return 0xc81;
1021 default:
1022 return -1;
1026 ST_FUNC void subst_asm_operand(CString *add_str, SValue *sv, int modifier)
1028 tcc_error("RISCV64 asm not implemented.");
1031 /* generate prolog and epilog code for asm statement */
1032 ST_FUNC void asm_gen_code(ASMOperand *operands, int nb_operands,
1033 int nb_outputs, int is_output,
1034 uint8_t *clobber_regs,
1035 int out_reg)
1039 ST_FUNC void asm_compute_constraints(ASMOperand *operands,
1040 int nb_operands, int nb_outputs,
1041 const uint8_t *clobber_regs,
1042 int *pout_reg)
1046 ST_FUNC void asm_clobber(uint8_t *clobber_regs, const char *str)
1048 int reg;
1049 TokenSym *ts;
1051 if (!strcmp(str, "memory") ||
1052 !strcmp(str, "cc") ||
1053 !strcmp(str, "flags"))
1054 return;
1055 ts = tok_alloc(str, strlen(str));
1056 reg = asm_parse_regvar(ts->tok);
1057 if (reg == -1) {
1058 tcc_error("invalid clobber register '%s'", str);
1060 clobber_regs[reg] = 1;
1063 ST_FUNC int asm_parse_regvar (int t)
1065 /* PC register not implemented */
1066 if (t >= TOK_ASM_pc || t < TOK_ASM_x0)
1067 return -1;
1069 if (t < TOK_ASM_f0)
1070 return t - TOK_ASM_x0;
1072 if (t < TOK_ASM_zero)
1073 return t - TOK_ASM_f0;
1075 /* ABI mnemonic */
1076 if (t < TOK_ASM_ft0)
1077 return t - TOK_ASM_zero;
1079 return t - TOK_ASM_ft0;
1082 /*************************************************************/
1083 /* C extension */
1085 /* caller: Add funct6, funct2 into opcode */
1086 static void asm_emit_ca(int token, uint16_t opcode, const Operand *rd, const Operand *rs2)
1088 uint8_t dst, src;
1090 if (rd->type != OP_REG) {
1091 tcc_error("'%s': Expected destination operand that is a register", get_tok_str(token, NULL));
1092 return;
1095 if (rs2->type != OP_REG) {
1096 tcc_error("'%s': Expected source operand that is a register", get_tok_str(token, NULL));
1097 return;
1100 /* subtract index of x8 */
1101 dst = rd->reg - 8;
1102 src = rs2->reg - 8;
1104 /* only registers {x,f}8 to {x,f}15 are valid (3-bit) */
1105 if (dst > 7) {
1106 tcc_error("'%s': Expected destination operand that is a valid C-extension register", get_tok_str(token, NULL));
1107 return;
1110 if (src > 7) {
1111 tcc_error("'%s': Expected source operand that is a valid C-extension register", get_tok_str(token, NULL));
1112 return;
1115 /* CA-type instruction:
1116 15...10 funct6
1117 9...7 rd'/rs1'
1118 6..5 funct2
1119 4...2 rs2'
1120 1...0 opcode */
1122 gen_le16(opcode | C_ENCODE_RS2(src) | C_ENCODE_RS1(dst));
1125 static void asm_emit_cb(int token, uint16_t opcode, const Operand *rs1, const Operand *imm)
1127 uint32_t offset;
1128 uint8_t src;
1130 if (rs1->type != OP_REG) {
1131 tcc_error("'%s': Expected source operand that is a register", get_tok_str(token, NULL));
1132 return;
1135 if (imm->type != OP_IM12S && imm->type != OP_IM32) {
1136 tcc_error("'%s': Expected source operand that is an immediate value", get_tok_str(token, NULL));
1137 return;
1140 offset = imm->e.v;
1142 if (offset & 1) {
1143 tcc_error("'%s': Expected source operand that is an even immediate value", get_tok_str(token, NULL));
1144 return;
1147 src = rs1->reg - 8;
1149 if (src > 7) {
1150 tcc_error("'%s': Expected source operand that is a valid C-extension register", get_tok_str(token, NULL));
1151 return;
1154 /* CB-type instruction:
1155 15...13 funct3
1156 12...10 offset
1157 9..7 rs1'
1158 6...2 offset
1159 1...0 opcode */
1161 /* non-branch also using CB:
1162 15...13 funct3
1163 12 imm
1164 11..10 funct2
1165 9...7 rd'/rs1'
1166 6..2 imm
1167 1...0 opcode */
1169 switch (token) {
1170 case TOK_ASM_c_beqz:
1171 case TOK_ASM_c_bnez:
1172 gen_le16(opcode | C_ENCODE_RS1(src) | ((NTH_BIT(offset, 5) | (((offset >> 1) & 3) << 1) | (((offset >> 6) & 3) << 3)) << 2) | ((((offset >> 3) & 3) | NTH_BIT(offset, 8)) << 10));
1173 return;
1174 default:
1175 gen_le16(opcode | C_ENCODE_RS1(src) | ((offset & 0x1f) << 2) | (NTH_BIT(offset, 5) << 12));
1176 return;
1180 static void asm_emit_ci(int token, uint16_t opcode, const Operand *rd, const Operand *imm)
1182 uint32_t immediate;
1184 if (rd->type != OP_REG) {
1185 tcc_error("'%s': Expected destination operand that is a register", get_tok_str(token, NULL));
1186 return;
1189 if (imm->type != OP_IM12S && imm->type != OP_IM32) {
1190 tcc_error("'%s': Expected source operand that is an immediate value", get_tok_str(token, NULL));
1191 return;
1194 immediate = imm->e.v;
1196 /* CI-type instruction:
1197 15...13 funct3
1198 12 imm
1199 11...7 rd/rs1
1200 6...2 imm
1201 1...0 opcode */
1203 switch (token) {
1204 case TOK_ASM_c_addi:
1205 case TOK_ASM_c_addiw:
1206 case TOK_ASM_c_li:
1207 case TOK_ASM_c_slli:
1208 gen_le16(opcode | ((immediate & 0x1f) << 2) | ENCODE_RD(rd->reg) | (NTH_BIT(immediate, 5) << 12));
1209 return;
1210 case TOK_ASM_c_addi16sp:
1211 gen_le16(opcode | NTH_BIT(immediate, 5) << 2 | (((immediate >> 7) & 3) << 3) | NTH_BIT(immediate, 6) << 5 | NTH_BIT(immediate, 4) << 6 | ENCODE_RD(rd->reg) | (NTH_BIT(immediate, 9) << 12));
1212 return;
1213 case TOK_ASM_c_lui:
1214 gen_le16(opcode | (((immediate >> 12) & 0x1f) << 2) | ENCODE_RD(rd->reg) | (NTH_BIT(immediate, 17) << 12));
1215 return;
1216 case TOK_ASM_c_fldsp:
1217 case TOK_ASM_c_ldsp:
1218 gen_le16(opcode | (((immediate >> 6) & 7) << 2) | (((immediate >> 3) & 2) << 5) | ENCODE_RD(rd->reg) | (NTH_BIT(immediate, 5) << 12));
1219 return;
1220 case TOK_ASM_c_flwsp:
1221 case TOK_ASM_c_lwsp:
1222 gen_le16(opcode | (((immediate >> 6) & 3) << 2) | (((immediate >> 2) & 7) << 4) | ENCODE_RD(rd->reg) | (NTH_BIT(immediate, 5) << 12));
1223 return;
1224 case TOK_ASM_c_nop:
1225 gen_le16(opcode);
1226 return;
1227 default:
1228 expect("known instruction");
1232 /* caller: Add funct3 into opcode */
1233 static void asm_emit_ciw(int token, uint16_t opcode, const Operand *rd, const Operand *imm)
1235 uint32_t nzuimm;
1236 uint8_t dst;
1238 if (rd->type != OP_REG) {
1239 tcc_error("'%s': Expected destination operand that is a register", get_tok_str(token, NULL));
1240 return;
1243 if (imm->type != OP_IM12S && imm->type != OP_IM32) {
1244 tcc_error("'%s': Expected source operand that is an immediate value", get_tok_str(token, NULL));
1245 return;
1248 dst = rd->reg - 8;
1250 if (dst > 7) {
1251 tcc_error("'%s': Expected destination operand that is a valid C-extension register", get_tok_str(token, NULL));
1252 return;
1255 nzuimm = imm->e.v;
1257 if (nzuimm > 0x3fc) {
1258 tcc_error("'%s': Expected source operand that is an immediate value between 0 and 0x3ff", get_tok_str(token, NULL));
1259 return;
1262 if (nzuimm & 3) {
1263 tcc_error("'%s': Expected source operand that is a non-zero immediate value divisible by 4", get_tok_str(token, NULL));
1264 return;
1267 /* CIW-type instruction:
1268 15...13 funct3
1269 12...5 imm
1270 4...2 rd'
1271 1...0 opcode */
1273 gen_le16(opcode | ENCODE_RS2(rd->reg) | ((NTH_BIT(nzuimm, 3) | (NTH_BIT(nzuimm, 2) << 1) | (((nzuimm >> 6) & 0xf) << 2) | (((nzuimm >> 4) & 3) << 6)) << 5));
1276 /* caller: Add funct3 into opcode */
1277 static void asm_emit_cj(int token, uint16_t opcode, const Operand *imm)
1279 uint32_t offset;
1281 /* +-2 KiB range */
1282 if (imm->type != OP_IM12S) {
1283 tcc_error("'%s': Expected source operand that is a 12-bit immediate value", get_tok_str(token, NULL));
1284 return;
1287 offset = imm->e.v;
1289 if (offset & 1) {
1290 tcc_error("'%s': Expected source operand that is an even immediate value", get_tok_str(token, NULL));
1291 return;
1294 /* CJ-type instruction:
1295 15...13 funct3
1296 12...2 offset[11|4|9:8|10|6|7|3:1|5]
1297 1...0 opcode */
1299 gen_le16(opcode | (NTH_BIT(offset, 5) << 2) | (((offset >> 1) & 7) << 3) | (NTH_BIT(offset, 7) << 6) | (NTH_BIT(offset, 6) << 7) | (NTH_BIT(offset, 10) << 8) | (((offset >> 8) & 3) << 9) | (NTH_BIT(offset, 4) << 11) | (NTH_BIT(offset, 11) << 12));
1302 /* caller: Add funct3 into opcode */
1303 static void asm_emit_cl(int token, uint16_t opcode, const Operand *rd, const Operand *rs1, const Operand *imm)
1305 uint32_t offset;
1306 uint8_t dst, src;
1308 if (rd->type != OP_REG) {
1309 tcc_error("'%s': Expected destination operand that is a register", get_tok_str(token, NULL));
1310 return;
1313 if (rs1->type != OP_REG) {
1314 tcc_error("'%s': Expected source operand that is a register", get_tok_str(token, NULL));
1315 return;
1318 if (imm->type != OP_IM12S && imm->type != OP_IM32) {
1319 tcc_error("'%s': Expected source operand that is an immediate value", get_tok_str(token, NULL));
1320 return;
1323 dst = rd->reg - 8;
1324 src = rs1->reg - 8;
1326 if (dst > 7) {
1327 tcc_error("'%s': Expected destination operand that is a valid C-extension register", get_tok_str(token, NULL));
1328 return;
1331 if (src > 7) {
1332 tcc_error("'%s': Expected source operand that is a valid C-extension register", get_tok_str(token, NULL));
1333 return;
1336 offset = imm->e.v;
1338 if (offset > 0xff) {
1339 tcc_error("'%s': Expected source operand that is an immediate value between 0 and 0xff", get_tok_str(token, NULL));
1340 return;
1343 if (offset & 3) {
1344 tcc_error("'%s': Expected source operand that is an immediate value divisible by 4", get_tok_str(token, NULL));
1345 return;
1348 /* CL-type instruction:
1349 15...13 funct3
1350 12...10 imm
1351 9...7 rs1'
1352 6...5 imm
1353 4...2 rd'
1354 1...0 opcode */
1356 switch (token) {
1357 /* imm variant 1 */
1358 case TOK_ASM_c_flw:
1359 case TOK_ASM_c_lw:
1360 gen_le16(opcode | C_ENCODE_RS2(dst) | C_ENCODE_RS1(src) | (NTH_BIT(offset, 6) << 5) | (NTH_BIT(offset, 2) << 6) | (((offset >> 3) & 7) << 10));
1361 return;
1362 /* imm variant 2 */
1363 case TOK_ASM_c_fld:
1364 case TOK_ASM_c_ld:
1365 gen_le16(opcode | C_ENCODE_RS2(dst) | C_ENCODE_RS1(src) | (((offset >> 6) & 3) << 5) | (((offset >> 3) & 7) << 10));
1366 return;
1367 default:
1368 expect("known instruction");
1372 /* caller: Add funct4 into opcode */
1373 static void asm_emit_cr(int token, uint16_t opcode, const Operand *rd, const Operand *rs2)
1375 if (rd->type != OP_REG) {
1376 tcc_error("'%s': Expected destination operand that is a register", get_tok_str(token, NULL));
1377 return;
1380 if (rs2->type != OP_REG) {
1381 tcc_error("'%s': Expected source operand that is a register", get_tok_str(token, NULL));
1382 return;
1385 /* CR-type instruction:
1386 15...12 funct4
1387 11..7 rd/rs1
1388 6...2 rs2
1389 1...0 opcode */
1391 gen_le16(opcode | C_ENCODE_RS1(rd->reg) | C_ENCODE_RS2(rs2->reg));
1394 /* caller: Add funct3 into opcode */
1395 static void asm_emit_cs(int token, uint16_t opcode, const Operand *rs2, const Operand *rs1, const Operand *imm)
1397 uint32_t offset;
1398 uint8_t base, src;
1400 if (rs2->type != OP_REG) {
1401 tcc_error("'%s': Expected destination operand that is a register", get_tok_str(token, NULL));
1402 return;
1405 if (rs1->type != OP_REG) {
1406 tcc_error("'%s': Expected source operand that is a register", get_tok_str(token, NULL));
1407 return;
1410 if (imm->type != OP_IM12S && imm->type != OP_IM32) {
1411 tcc_error("'%s': Expected source operand that is an immediate value", get_tok_str(token, NULL));
1412 return;
1415 base = rs1->reg - 8;
1416 src = rs2->reg - 8;
1418 if (base > 7) {
1419 tcc_error("'%s': Expected destination operand that is a valid C-extension register", get_tok_str(token, NULL));
1420 return;
1423 if (src > 7) {
1424 tcc_error("'%s': Expected source operand that is a valid C-extension register", get_tok_str(token, NULL));
1425 return;
1428 offset = imm->e.v;
1430 if (offset > 0xff) {
1431 tcc_error("'%s': Expected source operand that is an immediate value between 0 and 0xff", get_tok_str(token, NULL));
1432 return;
1435 if (offset & 3) {
1436 tcc_error("'%s': Expected source operand that is an immediate value divisible by 4", get_tok_str(token, NULL));
1437 return;
1440 /* CS-type instruction:
1441 15...13 funct3
1442 12...10 imm
1443 9...7 rs1'
1444 6...5 imm
1445 4...2 rs2'
1446 1...0 opcode */
1447 switch (token) {
1448 /* imm variant 1 */
1449 case TOK_ASM_c_fsw:
1450 case TOK_ASM_c_sw:
1451 gen_le16(opcode | C_ENCODE_RS2(base) | C_ENCODE_RS1(src) | (NTH_BIT(offset, 6) << 5) | (NTH_BIT(offset, 2) << 6) | (((offset >> 3) & 7) << 10));
1452 return;
1453 /* imm variant 2 */
1454 case TOK_ASM_c_fsd:
1455 case TOK_ASM_c_sd:
1456 gen_le16(opcode | C_ENCODE_RS2(base) | C_ENCODE_RS1(src) | (((offset >> 6) & 3) << 5) | (((offset >> 3) & 7) << 10));
1457 return;
1458 default:
1459 expect("known instruction");
1463 /* caller: Add funct3 into opcode */
1464 static void asm_emit_css(int token, uint16_t opcode, const Operand *rs2, const Operand *imm)
1466 uint32_t offset;
1468 if (rs2->type != OP_REG) {
1469 tcc_error("'%s': Expected destination operand that is a register", get_tok_str(token, NULL));
1470 return;
1473 if (imm->type != OP_IM12S && imm->type != OP_IM32) {
1474 tcc_error("'%s': Expected source operand that is an immediate value", get_tok_str(token, NULL));
1475 return;
1478 offset = imm->e.v;
1480 if (offset > 0xff) {
1481 tcc_error("'%s': Expected source operand that is an immediate value between 0 and 0xff", get_tok_str(token, NULL));
1482 return;
1485 if (offset & 3) {
1486 tcc_error("'%s': Expected source operand that is an immediate value divisible by 4", get_tok_str(token, NULL));
1487 return;
1490 /* CSS-type instruction:
1491 15...13 funct3
1492 12...7 imm
1493 6...2 rs2
1494 1...0 opcode */
1496 switch (token) {
1497 /* imm variant 1 */
1498 case TOK_ASM_c_fswsp:
1499 case TOK_ASM_c_swsp:
1500 gen_le16(opcode | ENCODE_RS2(rs2->reg) | (((offset >> 6) & 3) << 7) | (((offset >> 2) & 0xf) << 9));
1501 return;
1502 /* imm variant 2 */
1503 case TOK_ASM_c_fsdsp:
1504 case TOK_ASM_c_sdsp:
1505 gen_le16(opcode | ENCODE_RS2(rs2->reg) | (((offset >> 6) & 7) << 7) | (((offset >> 3) & 7) << 10));
1506 return;
1507 default:
1508 expect("known instruction");
1512 /*************************************************************/
1513 #endif /* ndef TARGET_DEFS_ONLY */