1 /*************************************************************/
3 * RISCV64 assembler for TCC
7 #ifdef TARGET_DEFS_ONLY
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 /*************************************************************/
18 /*************************************************************/
22 /* XXX: make it faster ? */
29 if (ind1
> cur_text_section
->data_allocated
)
30 section_realloc(cur_text_section
, ind1
);
31 cur_text_section
->data
[ind
] = c
;
35 ST_FUNC
void gen_le16 (int i
)
41 ST_FUNC
void gen_le32 (int i
)
47 if (ind1
> cur_text_section
->data_allocated
)
48 section_realloc(cur_text_section
, ind1
);
49 cur_text_section
->data
[ind
++] = i
& 0xFF;
50 cur_text_section
->data
[ind
++] = (i
>> 8) & 0xFF;
51 cur_text_section
->data
[ind
++] = (i
>> 16) & 0xFF;
52 cur_text_section
->data
[ind
++] = (i
>> 24) & 0xFF;
55 ST_FUNC
void gen_expr32(ExprValue
*pe
)
60 static void asm_emit_opcode(uint32_t opcode
) {
64 static void asm_nullary_opcode(TCCState
*s1
, int token
)
69 case TOK_ASM_fence
: // I
70 asm_emit_opcode((0x3 << 2) | 3 | (0 << 12));
72 case TOK_ASM_fence_i
: // I
73 asm_emit_opcode((0x3 << 2) | 3| (1 << 12));
78 case TOK_ASM_scall
: // I (pseudo)
79 asm_emit_opcode((0x1C << 2) | 3 | (0 << 12));
81 case TOK_ASM_sbreak
: // I (pseudo)
82 asm_emit_opcode((0x1C << 2) | 3 | (0 << 12) | (1 << 20));
85 // Privileged Instructions
88 asm_emit_opcode((0x1C << 2) | 3 | (0 << 20));
91 asm_emit_opcode((0x1C << 2) | 3 | (1 << 20));
97 asm_emit_opcode((0x1C << 2) | 3 | (0x105 << 20));
101 expect("nullary instruction");
110 #define OP_REG (1 << OPT_REG)
111 #define OP_IM32 (1 << OPT_IM32)
112 #define OP_IM12S (1 << OPT_IM12S)
114 typedef struct Operand
{
123 /* Parse a text containing operand and store the result in OP */
124 static void parse_operand(TCCState
*s1
, Operand
*op
)
131 if ((reg
= asm_parse_regvar(tok
)) != -1) {
132 next(); // skip register name
134 op
->reg
= (uint8_t) reg
;
136 } else if (tok
== '$') {
138 next(); // skip '#' or '$'
144 if ((int) op
->e
.v
>= -2048 && (int) op
->e
.v
< 2048)
150 #define ENCODE_RS1(register_index) ((register_index) << 15)
151 #define ENCODE_RS2(register_index) ((register_index) << 20)
152 #define ENCODE_RD(register_index) ((register_index) << 7)
154 // Note: Those all map to CSR--so they are pseudo-instructions.
155 static void asm_unary_opcode(TCCState
*s1
, int token
)
157 uint32_t opcode
= (0x1C << 2) | 3 | (2 << 12);
159 parse_operand(s1
, &op
);
160 if (op
.type
!= OP_REG
) {
164 opcode
|= ENCODE_RD(op
.reg
);
167 case TOK_ASM_rdcycle
:
168 asm_emit_opcode(opcode
| (0xC00 << 20));
170 case TOK_ASM_rdcycleh
:
171 asm_emit_opcode(opcode
| (0xC80 << 20));
174 asm_emit_opcode(opcode
| (0xC01 << 20) | ENCODE_RD(op
.reg
));
176 case TOK_ASM_rdtimeh
:
177 asm_emit_opcode(opcode
| (0xC81 << 20) | ENCODE_RD(op
.reg
));
179 case TOK_ASM_rdinstret
:
180 asm_emit_opcode(opcode
| (0xC02 << 20) | ENCODE_RD(op
.reg
));
182 case TOK_ASM_rdinstreth
:
183 asm_emit_opcode(opcode
| (0xC82 << 20) | ENCODE_RD(op
.reg
));
186 expect("unary instruction");
190 static void asm_emit_u(int token
, uint32_t opcode
, const Operand
* rd
, const Operand
* rs2
)
192 if (rd
->type
!= OP_REG
) {
193 tcc_error("'%s': Expected destination operand that is a register", get_tok_str(token
, NULL
));
196 if (rs2
->type
!= OP_IM12S
&& rs2
->type
!= OP_IM32
) {
197 tcc_error("'%s': Expected second source operand that is an immediate value", get_tok_str(token
, NULL
));
199 } else if (rs2
->e
.v
>= 0x100000) {
200 tcc_error("'%s': Expected second source operand that is an immediate value between 0 and 0xfffff", get_tok_str(token
, NULL
));
203 /* U-type instruction:
207 gen_le32(opcode
| ENCODE_RD(rd
->reg
) | (rs2
->e
.v
<< 12));
210 static void asm_binary_opcode(TCCState
* s1
, int token
)
213 parse_operand(s1
, &ops
[0]);
218 parse_operand(s1
, &ops
[1]);
222 asm_emit_u(token
, (0xD << 2) | 3, &ops
[0], &ops
[1]);
225 asm_emit_u(token
, (0x05 << 2) | 3, &ops
[0], &ops
[1]);
228 expect("binary instruction");
232 /* caller: Add funct3, funct7 into opcode */
233 static void asm_emit_r(int token
, uint32_t opcode
, const Operand
* rd
, const Operand
* rs1
, const Operand
* rs2
)
235 if (rd
->type
!= OP_REG
) {
236 tcc_error("'%s': Expected destination operand that is a register", get_tok_str(token
, NULL
));
239 if (rs1
->type
!= OP_REG
) {
240 tcc_error("'%s': Expected first source operand that is a register", get_tok_str(token
, NULL
));
243 if (rs2
->type
!= OP_REG
) {
244 tcc_error("'%s': Expected second source operand that is a register or immediate", get_tok_str(token
, NULL
));
247 /* R-type instruction:
254 gen_le32(opcode
| ENCODE_RD(rd
->reg
) | ENCODE_RS1(rs1
->reg
) | ENCODE_RS2(rs2
->reg
));
257 /* caller: Add funct3 into opcode */
258 static void asm_emit_i(int token
, uint32_t opcode
, const Operand
* rd
, const Operand
* rs1
, const Operand
* rs2
)
260 if (rd
->type
!= OP_REG
) {
261 tcc_error("'%s': Expected destination operand that is a register", get_tok_str(token
, NULL
));
264 if (rs1
->type
!= OP_REG
) {
265 tcc_error("'%s': Expected first source operand that is a register", get_tok_str(token
, NULL
));
268 if (rs2
->type
!= OP_IM12S
) {
269 tcc_error("'%s': Expected second source operand that is an immediate value between 0 and 4095", get_tok_str(token
, NULL
));
272 /* I-type instruction:
279 gen_le32(opcode
| ENCODE_RD(rd
->reg
) | ENCODE_RS1(rs1
->reg
) | (rs2
->e
.v
<< 20));
282 static void asm_shift_opcode(TCCState
*s1
, int token
)
285 parse_operand(s1
, &ops
[0]);
290 parse_operand(s1
, &ops
[1]);
295 parse_operand(s1
, &ops
[2]);
299 asm_emit_r(token
, (0xC << 2) | 3 | (1 << 12), &ops
[0], &ops
[1], &ops
[2]);
302 asm_emit_i(token
, (4 << 2) | 3 | (1 << 12), &ops
[0], &ops
[1], &ops
[2]);
305 asm_emit_r(token
, (0xC << 2) | 3 | (4 << 12), &ops
[0], &ops
[1], &ops
[2]);
308 asm_emit_i(token
, (0x4 << 2) | 3 | (5 << 12), &ops
[0], &ops
[1], &ops
[2]);
311 asm_emit_r(token
, (0xC << 2) | 3 | (5 << 12) | (32 << 25), &ops
[0], &ops
[1], &ops
[2]);
314 asm_emit_i(token
, (0x4 << 2) | 3 | (5 << 12) | (16 << 26), &ops
[0], &ops
[1], &ops
[2]);
317 asm_emit_r(token
, (0xE << 2) | 3 | (1 << 12), &ops
[0], &ops
[1], &ops
[2]);
320 asm_emit_i(token
, (6 << 2) | 3 | (1 << 12), &ops
[0], &ops
[1], &ops
[2]);
323 asm_emit_r(token
, (0xE << 2) | 3 | (5 << 12), &ops
[0], &ops
[1], &ops
[2]);
326 asm_emit_i(token
, (0x6 << 2) | 3 | (5 << 12), &ops
[0], &ops
[1], &ops
[2]);
329 asm_emit_r(token
, (0xE << 2) | 3 | (5 << 12), &ops
[0], &ops
[1], &ops
[2]);
332 asm_emit_i(token
, (0x6 << 2) | 3 | (5 << 12), &ops
[0], &ops
[1], &ops
[2]);
335 expect("shift instruction");
339 static void asm_data_processing_opcode(TCCState
* s1
, int token
)
342 parse_operand(s1
, &ops
[0]);
347 parse_operand(s1
, &ops
[1]);
352 parse_operand(s1
, &ops
[2]);
355 // Arithmetic (RD,RS1,(RS2|IMM)); R-format, I-format or U-format
358 asm_emit_r(token
, (0xC << 2) | 3, &ops
[0], &ops
[1], &ops
[2]);
361 asm_emit_i(token
, (4 << 2) | 3, &ops
[0], &ops
[1], &ops
[2]);
364 asm_emit_r(token
, (0xC << 2) | 3 | (32 << 25), &ops
[0], &ops
[1], &ops
[2]);
367 asm_emit_r(token
, (0xE << 2) | 3 | (0 << 12), &ops
[0], &ops
[1], &ops
[2]);
369 case TOK_ASM_addiw
: // 64 bit
370 asm_emit_i(token
, (0x6 << 2) | 3 | (0 << 12), &ops
[0], &ops
[1], &ops
[2]);
373 asm_emit_r(token
, (0xE << 2) | 3 | (0 << 12) | (32 << 25), &ops
[0], &ops
[1], &ops
[2]);
376 // Logical (RD,RS1,(RS2|IMM)); R-format or I-format
379 asm_emit_r(token
, (0xC << 2) | 3 | (4 << 12), &ops
[0], &ops
[1], &ops
[2]);
382 asm_emit_i(token
, (0x4 << 2) | 3 | (4 << 12), &ops
[0], &ops
[1], &ops
[2]);
385 asm_emit_r(token
, (0xC << 2) | 3 | (6 << 12), &ops
[0], &ops
[1], &ops
[2]);
388 asm_emit_i(token
, (0x4 << 2) | 3 | (6 << 12), &ops
[0], &ops
[1], &ops
[2]);
391 asm_emit_r(token
, (0xC << 2) | 3 | (7 << 12), &ops
[0], &ops
[1], &ops
[2]);
394 asm_emit_i(token
, (0x4 << 2) | 3 | (7 << 12), &ops
[0], &ops
[1], &ops
[2]);
397 // Compare (RD,RS1,(RS2|IMM)); R-format or I-format
400 asm_emit_r(token
, (0xC << 2) | 3 | (2 << 12), &ops
[0], &ops
[1], &ops
[2]);
403 asm_emit_i(token
, (0x4 << 2) | 3 | (2 << 12), &ops
[0], &ops
[1], &ops
[2]);
406 asm_emit_r(token
, (0xC << 2) | 3 | (3 << 12), &ops
[0], &ops
[1], &ops
[2]);
409 asm_emit_i(token
, (0x4 << 2) | 3 | (3 << 12), &ops
[0], &ops
[1], &ops
[2]);
412 expect("known data processing instruction");
416 /* caller: Add funct3 to opcode */
417 static void asm_emit_s(int token
, uint32_t opcode
, const Operand
* rs1
, const Operand
* rs2
, const Operand
* imm
)
419 if (rs1
->type
!= OP_REG
) {
420 tcc_error("'%s': Expected first source operand that is a register", get_tok_str(token
, NULL
));
423 if (rs2
->type
!= OP_REG
) {
424 tcc_error("'%s': Expected second source operand that is a register", get_tok_str(token
, NULL
));
427 if (imm
->type
!= OP_IM12S
) {
428 tcc_error("'%s': Expected third operand that is an immediate value between 0 and 0xfff", get_tok_str(token
, NULL
));
432 uint16_t v
= imm
->e
.v
;
433 /* S-type instruction:
440 opcode always fixed pos. */
441 gen_le32(opcode
| ENCODE_RS1(rs1
->reg
) | ENCODE_RS2(rs2
->reg
) | ((v
& 0x1F) << 7) | ((v
>> 5) << 25));
445 static void asm_data_transfer_opcode(TCCState
* s1
, int token
)
448 parse_operand(s1
, &ops
[0]);
449 if (ops
[0].type
!= OP_REG
) {
457 parse_operand(s1
, &ops
[1]);
458 if (ops
[1].type
!= OP_REG
) {
466 parse_operand(s1
, &ops
[2]);
469 // Loads (RD,RS1,I); I-format
472 asm_emit_i(token
, (0x0 << 2) | 3, &ops
[0], &ops
[1], &ops
[2]);
475 asm_emit_i(token
, (0x0 << 2) | 3 | (1 << 12), &ops
[0], &ops
[1], &ops
[2]);
478 asm_emit_i(token
, (0x0 << 2) | 3 | (2 << 12), &ops
[0], &ops
[1], &ops
[2]);
481 asm_emit_i(token
, (0x0 << 2) | 3 | (4 << 12), &ops
[0], &ops
[1], &ops
[2]);
484 asm_emit_i(token
, (0x0 << 2) | 3 | (5 << 12), &ops
[0], &ops
[1], &ops
[2]);
488 asm_emit_i(token
, (0x0 << 2) | 3 | (3 << 12), &ops
[0], &ops
[1], &ops
[2]);
491 asm_emit_i(token
, (0x0 << 2) | 3 | (6 << 12), &ops
[0], &ops
[1], &ops
[2]);
494 // Stores (RS1,RS2,I); S-format
497 asm_emit_s(token
, (0x8 << 2) | 3 | (0 << 12), &ops
[0], &ops
[1], &ops
[2]);
500 asm_emit_s(token
, (0x8 << 2) | 3 | (1 << 12), &ops
[0], &ops
[1], &ops
[2]);
503 asm_emit_s(token
, (0x8 << 2) | 3 | (2 << 12), &ops
[0], &ops
[1], &ops
[2]);
506 asm_emit_s(token
, (0x8 << 2) | 3 | (3 << 12), &ops
[0], &ops
[1], &ops
[2]);
510 expect("known data transfer instruction");
514 static void asm_branch_opcode(TCCState
* s1
, int token
)
516 // Branch (RS1,RS2,IMM); SB-format
517 uint32_t opcode
= (0x18 << 2) | 3;
520 parse_operand(s1
, &ops
[0]);
521 if (ops
[0].type
!= OP_REG
) {
529 parse_operand(s1
, &ops
[1]);
530 if (ops
[1].type
!= OP_REG
) {
538 parse_operand(s1
, &ops
[2]);
540 if (ops
[2].type
!= OP_IM12S
) {
541 tcc_error("'%s': Expected third operand that is an immediate value between 0 and 0xfff", get_tok_str(token
, NULL
));
546 tcc_error("'%s': Expected third operand that is an even immediate value", get_tok_str(token
, NULL
));
570 expect("known branch instruction");
572 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));
575 ST_FUNC
void asm_opcode(TCCState
*s1
, int token
)
579 case TOK_ASM_fence_i
:
588 asm_nullary_opcode(s1
, token
);
591 case TOK_ASM_rdcycle
:
592 case TOK_ASM_rdcycleh
:
594 case TOK_ASM_rdtimeh
:
595 case TOK_ASM_rdinstret
:
596 case TOK_ASM_rdinstreth
:
597 asm_unary_opcode(s1
, token
);
602 asm_binary_opcode(s1
, token
);
623 asm_shift_opcode(s1
, token
);
645 asm_data_processing_opcode(s1
, token
);
658 asm_data_transfer_opcode(s1
, token
);
667 asm_branch_opcode(s1
, token
);
671 expect("known instruction");
675 ST_FUNC
void subst_asm_operand(CString
*add_str
, SValue
*sv
, int modifier
)
677 tcc_error("RISCV64 asm not implemented.");
680 /* generate prolog and epilog code for asm statement */
681 ST_FUNC
void asm_gen_code(ASMOperand
*operands
, int nb_operands
,
682 int nb_outputs
, int is_output
,
683 uint8_t *clobber_regs
,
688 ST_FUNC
void asm_compute_constraints(ASMOperand
*operands
,
689 int nb_operands
, int nb_outputs
,
690 const uint8_t *clobber_regs
,
695 ST_FUNC
void asm_clobber(uint8_t *clobber_regs
, const char *str
)
700 if (!strcmp(str
, "memory") ||
701 !strcmp(str
, "cc") ||
702 !strcmp(str
, "flags"))
704 ts
= tok_alloc(str
, strlen(str
));
705 reg
= asm_parse_regvar(ts
->tok
);
707 tcc_error("invalid clobber register '%s'", str
);
709 clobber_regs
[reg
] = 1;
712 ST_FUNC
int asm_parse_regvar (int t
)
714 if (t
>= TOK_ASM_x0
&& t
<= TOK_ASM_pc
) { /* register name */
717 return -1; // TODO: Figure out where it can be used after all
719 return t
- TOK_ASM_x0
;
725 /*************************************************************/
726 #endif /* ndef TARGET_DEFS_ONLY */