forked from sellicott/tcc-riscv32
-
Notifications
You must be signed in to change notification settings - Fork 5
/
riscv_utils.h
211 lines (175 loc) · 9.69 KB
/
riscv_utils.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
#ifndef _RISCV_UTILS_H_
#define _RISCV_UTILS_H_
/* riscv_utils.h
* Some helper functions and macros that make generating instructions easier
* Implementations of the base instruction types, and macros for each
* instruction to generate the correct opcode from the memeonic.
*/
#include <stdint.h>
// Definitions for various instruction types: implemented in riscv32-asm.c
// Register instructions (math and stuff)
void emit_R(
uint32_t funct7, uint32_t rs2, uint32_t rs1, uint32_t funct3, uint32_t rd, uint32_t opcode );
// immediate instructions
void emit_I( uint32_t imm, uint32_t rs1, uint32_t funct3, uint32_t rd, uint32_t opcode );
// store instructions
void emit_S( uint32_t imm, uint32_t rs2, uint32_t rs1, uint32_t funct3, uint32_t opcode );
// branch instructions
void emit_B( uint32_t imm, uint32_t rs2, uint32_t rs1, uint32_t funct3, uint32_t opcode );
// big immediate instructions (LUI and AUIPC)
void emit_U( uint32_t imm, uint32_t rd, uint32_t opcode );
// jump instructions
void emit_J( uint32_t imm, uint32_t rd, uint32_t opcode );
// Macros for masking values for immediate operations
// mask off the lower 12 bits of a 32-bit value
#define IMM_LOW( imm ) ( (imm)&0x00000FFF )
// mask and shift a 32-bit immediate value to grab the upper 24 bits
#define IMM_HIGH( imm ) ( ( (imm)&0xFFFFF000 ) >> 12 )
// macro to check if the immediate value will be larger than 12 bits
#define LARGE_IMM( imm ) ( (uint32_t)( IMM_HIGH( (imm) + 0x800) ) )
// Now for a big table of opcodes (RV32I) from p130 of ISA documentation
// https://github.com/riscv/riscv-isa-manual/releases/download/Ratified-IMAFDQC/riscv-spec-20191213.pdf
// clang-format off
#define emit_LUI(rd, imm) (emit_U((uint32_t) imm, rd, 0x37))
#define emit_AUIPC(rd, imm) (emit_U((uint32_t) imm, rd, 0x17))
#define emit_JAL(rd, imm) (emit_J((uint32_t) imm, rd, 0x6f))
#define emit_JALR(rd, rs1, imm) (emit_I((uint32_t) imm, rs1, 0x0, rd, 0x67))
#define emit_BEQ(rs1, rs2, imm) (emit_B((uint32_t) imm, rs2, rs1, 0x0, 0x63))
#define emit_BNE(rs1, rs2, imm) (emit_B((uint32_t) imm, rs2, rs1, 0x1, 0x63))
#define emit_BLT(rs1, rs2, imm) (emit_B((uint32_t) imm, rs2, rs1, 0x4, 0x63))
#define emit_BGE(rs1, rs2, imm) (emit_B((uint32_t) imm, rs2, rs1, 0x5, 0x63))
#define emit_BLTU(rs1, rs2, imm) (emit_B((uint32_t) imm, rs2, rs1, 0x6, 0x63))
#define emit_BGEU(rs1, rs2, imm) (emit_B((uint32_t) imm, rs2, rs1, 0x7, 0x63))
#define emit_LB(rd, rs1, imm) (emit_I((uint32_t) imm, rs1, 0x0, rd, 0x03))
#define emit_LH(rd, rs1, imm) (emit_I((uint32_t) imm, rs1, 0x1, rd, 0x03))
#define emit_LW(rd, rs1, imm) (emit_I((uint32_t) imm, rs1, 0x2, rd, 0x03))
#define emit_LBU(rd, rs1, imm) (emit_I((uint32_t) imm, rs1, 0x4, rd, 0x03))
#define emit_LHU(rd, rs1, imm) (emit_I((uint32_t) imm, rs1, 0x5, rd, 0x03))
#define emit_SB(rs1, rs2, imm) (emit_S((uint32_t) imm, rs2, rs1, 0x0, 0x23))
#define emit_SH(rs1, rs2, imm) (emit_S((uint32_t) imm, rs2, rs1, 0x1, 0x23))
#define emit_SW(rs1, rs2, imm) (emit_S((uint32_t) imm, rs2, rs1, 0x2, 0x23))
#define emit_ADDI(rd, rs1, imm) (emit_I((uint32_t) imm, rs1, 0x0, rd, 0x13))
#define emit_SLTI(rd, rs1, imm) (emit_I((uint32_t) imm, rs1, 0x2, rd, 0x13))
#define emit_SLTIU(rd, rs1, imm) (emit_I((uint32_t) imm, rs1, 0x3, rd, 0x13))
#define emit_XORI(rd, rs1, imm) (emit_I((uint32_t) imm, rs1, 0x4, rd, 0x13))
#define emit_ORI(rd, rs1, imm) (emit_I((uint32_t) imm, rs1, 0x6, rd, 0x13))
#define emit_ANDI(rd, rs1, imm) (emit_I((uint32_t) imm, rs1, 0x7, rd, 0x13))
#define emit_SLLI(rd, rs1, shamt) (emit_R(0x00, shamt, rs1, 0x1, rd, 0x13))
#define emit_SRLI(rd, rs1, shamt) (emit_R(0x00, shamt, rs1, 0x5, rd, 0x13))
#define emit_SRAI(rd, rs1, shamt) (emit_R(0x20, shamt, rs1, 0x5, rd, 0x13))
#define emit_ADD(rd, rs1, rs2) (emit_R(0x00, rs2, rs1, 0x0, rd, 0x33))
#define emit_SUB(rd, rs1, rs2) (emit_R(0x20, rs2, rs1, 0x0, rd, 0x33))
#define emit_SLL(rd, rs1, rs2) (emit_R(0x00, rs2, rs1, 0x1, rd, 0x33))
#define emit_SLT(rd, rs1, rs2) (emit_R(0x00, rs2, rs1, 0x2, rd, 0x33))
#define emit_SLTU(rd, rs1, rs2) (emit_R(0x00, rs2, rs1, 0x3, rd, 0x33))
#define emit_XOR(rd, rs1, rs2) (emit_R(0x00, rs2, rs1, 0x4, rd, 0x33))
#define emit_SRL(rd, rs1, rs2) (emit_R(0x00, rs2, rs1, 0x5, rd, 0x33))
#define emit_SRA(rd, rs1, rs2) (emit_R(0x20, rs2, rs1, 0x5, rd, 0x33))
#define emit_OR(rd, rs1, rs2) (emit_R(0x00, rs2, rs1, 0x6, rd, 0x33))
#define emit_AND(rd, rs1, rs2) (emit_R(0x00, rs2, rs1, 0x7, rd, 0x33))
// FM is 4 bits, pred is 4 bits, succ is 4 bits. rs1 and rd are reserved (set to 0)
#define emit_FENCE(succ, pred, fm) (emit_I( \
(0xf & fm << 8) | (0xf & pred << 4) | ( 0xf & succ), 0x0, 0x0, 0x0, 0x0f) \
)
#define emit_ECALL() (emit_I(0x000, 0x00, 0x0, 0x00, 0x73))
#define emit_EBREAK() (emit_I(0x001, 0x00, 0x0, 0x00, 0x73))
// RV32M extension set
#define emit_MUL(rd, rs1, rs2) (emit_R(0x01, rs2, rs1, 0x0, rd, 0x33))
#define emit_MULH(rd, rs1, rs2) (emit_R(0x01, rs2, rs1, 0x1, rd, 0x33))
#define emit_MULHSU(rd, rs1, rs2) (emit_R(0x01, rs2, rs1, 0x2, rd, 0x33))
#define emit_MULHU(rd, rs1, rs2) (emit_R(0x01, rs2, rs1, 0x3, rd, 0x33))
#define emit_DIV(rd, rs1, rs2) (emit_R(0x01, rs2, rs1, 0x4, rd, 0x33))
#define emit_DIVU(rd, rs1, rs2) (emit_R(0x01, rs2, rs1, 0x5, rd, 0x33))
#define emit_REM(rd, rs1, rs2) (emit_R(0x01, rs2, rs1, 0x6, rd, 0x33))
#define emit_REMU(rd, rs1, rs2) (emit_R(0x01, rs2, rs1, 0x7, rd, 0x33))
// clang-format on
// Pseudo instructions (from https://risc-v.guru/instructions/)
// TODO make this switch between emit_LLA and emit_LGA to produce PIC code
#define emit_LA( rd, symbol ) emit_LLA( rd, symbol )
#define emit_NOP() ( emit_ADDI( 0, 0, 0 ) )
#define emit_LI( rd, imm ) \
/* add 1 to the upper 24 bits so that the sign extended*/ \
/* lower bits don't mess up the upper bits*/ \
emit_LUI( rd, IMM_HIGH( ( imm ) + 0x800 ) ); \
emit_ADDI( rd, rd, IMM_LOW( imm ) );
// TODO make emit_LLA and emit_LGA functions so that we can generate realloc symbols without hackery
// load a local symbol address
// example `lga a0, msg + 1
#define emit_LLA( rd, imm ) \
emit_AUIPC( rd, IMM_HIGH( imm ) ); \
emit_ADDI( rd, rd, IMM_LOW( imm ) );
// load global address used to load global symbol addresses
// example `lga a0, msg + 1
// this will need to generate two relocation entries
// R_RISCV_GOT_HI20 for auipc and
// R_RISCV_PCREL_LO12_I
#define emit_LGA( rd, imm ) \
emit_AUIPC( rd, IMM_HIGH( imm ) ); \
emit_LW( rd, IMM_LOW( imm ) );
// ret pseudo instruction
// ret -> jalr x0, x1, 0
#define emit_RET() ( emit_JALR( 0, 1, 0 ) )
#define emit_MV( rd, rs ) ( emit_ADDI( rd, rs, 0 ) )
//#define emit_SEXT_W(rd, rs) (emit_ADDIW(rd, rs, 0))
// branches
// clang-format off
#define emit_BEQZ(rs, offset) (emit_BEQ(rs, 0, offset))
#define emit_BNEZ(rs, offset) (emit_BNE(rs, 0, offset))
#define emit_BLEZ(rs, offset) (emit_BGE(0, rs, offset))
#define emit_BGEZ(rs, offset) (emit_BGE(rs, 0, offset))
#define emit_BLTZ(rs, offset) (emit_BLT(rs, 0, offset))
#define emit_BGTZ(rs, offset) (emit_BLT(0, rs, offset))
#define emit_BGT(rs, rt, offset) (emit_BLT(rt, rs, offset))
#define emit_BLE(rs, rt, offset) (emit_BGE(rs, rt, offset))
#define emit_BGTU(rs, rt, offset) (emit_BLTU(rt, rs, offset))
#define emit_BLEU(rs, rt, offset) (emit_BGEU(rt, rs, offset))
// comparisons
#define emit_SEQZ(rd, rs) (emit_SLTIU(rd, rs, 1))
#define emit_SNEZ(rd, rs) (emit_SLTU(rd, 0, rs))
#define emit_SLTZ(rd, rs) (emit_SLT(rd, rs, 0))
#define emit_SGTZ(rd, rs) (emit_SLT(rd, 0, rs))
// counters
// TODO
// Jump and Link
#define emit_J_inst(offset) (emit_JAL(0, offset))
#define emit_JAL_x1(offset) (emit_JAL(1, offset))
#define emit_JR(rs) (emit_JALR(0, rs, 0))
#define emit_JALR_x1(rs) (emit_JALR(1, rs, 0))
// todo make these functions and put in relocation call directly there
#define emit_CALL(offset) \
emit_AUIPC(6, IMM_HIGH(offset) ); \
emit_JALR(1, 6, IMM_LOW(offset) );
#define emit_TAIL(offset) \
emit_AUIPC(6, IMM_HIGH(offset) ); \
emit_JALR(0, 6, IMM_LOW(offset) );
// Logical
#define emit_NOT(rd, rs) (emit_XORI(rd, rs, -1))
#define emit_NEG(rd, rs) (emit_SUB(rd, 0, rs))
//#define emit_NEGW(rd, rs) (emit_SUBW(rd, 0, rs))
// Sync
#define emit_FENCE_ALL() (emit_FENCE(0xf, 0xf, 0x0))
#define emit_FENCE_DEFAULT() (emit_FENCE(0x0, 0x0, 0x0))
// Zicsr (Control and Status) extension
#define emit_CSRRW(rd, csr, rs1) (emit_I(csr, rs1, 0x1, rd, 0x73))
#define emit_CSRRS(rd, csr, rs1) (emit_I(csr, rs1, 0x2, rd, 0x73))
#define emit_CSRRC(rd, csr, rs1) (emit_I(csr, rs1, 0x3, rd, 0x73))
#define emit_CSRRWI(rd, csr, uimm) (emit_I(csr, uimm, 0x5, rd, 0x73))
#define emit_CSRRSI(rd, csr, uimm) (emit_I(csr, uimm, 0x6, rd, 0x73))
#define emit_CSRRCI(rd, csr, uimm) (emit_I(csr, uimm, 0x7, rd, 0x73))
// control and status pseudoinstructions
#define emit_CSRR(rd, csr) (emit_CSRRS(rd, csr, 0))
#define emit_CSRW(csr, rs) (emit_CSRRW(0, csr, rs))
#define emit_CSRS(csr, rs) (emit_CSRRS(0, csr, rs))
#define emit_CSRC(csr, rs) (emit_CSRRC(0, csr, rs))
#define emit_CSRWI(csr, imm) (emit_CSRRWI(0, csr, imm))
#define emit_CSRSI(csr, imm) (emit_CSRRSI(0, csr, imm))
#define emit_CSRCI(csr, imm) (emit_CSRRCI(0, csr, imm))
// counters
#define emit_RDCYCLE(rd) (emit_CSRRS(rd, 0xc00, 0))
#define emit_RDCYCLEH(rd) (emit_CSRRS(rd, 0xc80, 0))
#define emit_RDTIME(rd) (emit_CSRRS(rd, 0xc01, 0))
#define emit_RDTIMEH(rd) (emit_CSRRS(rd, 0xc81, 0))
#define emit_RDINSTRET(rd) (emit_CSRRS(rd, 0xc02, 0))
#define emit_RDINSTRETH(rd) (emit_CSRRS(rd, 0xc82, 0))
// clang-format on
#endif