master
1//! This file is responsible for going from MIR, which is emitted by CodeGen
2//! and converting it into Instructions, which can be used as needed.
3//!
4//! Here we encode how mnemonics relate to opcodes and where their operands go.
5
6/// Lower Instruction Representation
7///
8/// This format encodes a specific instruction, however it's still abstracted
9/// away from the true encoding it'll be in. It's meant to make the process of
10/// indicating unique encoding data easier.
11pub const Lir = struct {
12 opcode: OpCode,
13 format: Format,
14 data: Data,
15
16 pub const Format = enum {
17 R,
18 I,
19 S,
20 B,
21 U,
22 J,
23 extra,
24 };
25
26 const Data = union(enum) {
27 none,
28 f: struct { funct3: u3 },
29 ff: struct {
30 funct3: u3,
31 funct7: u7,
32 },
33 sh: struct {
34 typ: u6,
35 funct3: u3,
36 has_5: bool,
37 },
38
39 fmt: struct {
40 funct5: u5,
41 rm: u3,
42 fmt: FpFmt,
43 },
44 fcvt: struct {
45 funct5: u5,
46 rm: u3,
47 fmt: FpFmt,
48 width: Mir.FcvtOp,
49 },
50
51 vecls: struct {
52 width: VecWidth,
53 umop: Umop,
54 vm: bool,
55 mop: Mop,
56 mew: bool,
57 nf: u3,
58 },
59 vecmath: struct {
60 vm: bool,
61 funct6: u6,
62 funct3: VecType,
63 },
64
65 amo: struct {
66 funct5: u5,
67 width: AmoWidth,
68 },
69 fence: struct {
70 funct3: u3,
71 fm: FenceMode,
72 },
73
74 /// the mnemonic has some special properities that can't be handled in a generic fashion
75 extra: Mnemonic,
76 };
77
78 const OpCode = enum(u7) {
79 LOAD = 0b0000011,
80 LOAD_FP = 0b0000111,
81 MISC_MEM = 0b0001111,
82 OP_IMM = 0b0010011,
83 AUIPC = 0b0010111,
84 OP_IMM_32 = 0b0011011,
85 STORE = 0b0100011,
86 STORE_FP = 0b0100111,
87 AMO = 0b0101111,
88 OP_V = 0b1010111,
89 OP = 0b0110011,
90 OP_32 = 0b0111011,
91 LUI = 0b0110111,
92 MADD = 0b1000011,
93 MSUB = 0b1000111,
94 NMSUB = 0b1001011,
95 NMADD = 0b1001111,
96 OP_FP = 0b1010011,
97 OP_IMM_64 = 0b1011011,
98 BRANCH = 0b1100011,
99 JALR = 0b1100111,
100 JAL = 0b1101111,
101 SYSTEM = 0b1110011,
102 OP_64 = 0b1111011,
103 NONE = 0b00000000,
104 };
105
106 const FpFmt = enum(u2) {
107 /// 32-bit single-precision
108 S = 0b00,
109 /// 64-bit double-precision
110 D = 0b01,
111
112 // H = 0b10, unused in the G extension
113
114 /// 128-bit quad-precision
115 Q = 0b11,
116 };
117
118 const AmoWidth = enum(u3) {
119 W = 0b010,
120 D = 0b011,
121 };
122
123 const FenceMode = enum(u4) {
124 none = 0b0000,
125 tso = 0b1000,
126 };
127
128 const Mop = enum(u2) {
129 // zig fmt: off
130 unit = 0b00,
131 unord = 0b01,
132 stride = 0b10,
133 ord = 0b11,
134 // zig fmt: on
135 };
136
137 const Umop = enum(u5) {
138 // zig fmt: off
139 unit = 0b00000,
140 whole = 0b01000,
141 mask = 0b01011,
142 fault = 0b10000,
143 // zig fmt: on
144 };
145
146 const VecWidth = enum(u3) {
147 // zig fmt: off
148 @"8" = 0b000,
149 @"16" = 0b101,
150 @"32" = 0b110,
151 @"64" = 0b111,
152 // zig fmt: on
153 };
154
155 const VecType = enum(u3) {
156 OPIVV = 0b000,
157 OPFVV = 0b001,
158 OPMVV = 0b010,
159 OPIVI = 0b011,
160 OPIVX = 0b100,
161 OPFVF = 0b101,
162 OPMVX = 0b110,
163 };
164
165 pub fn fromMnem(mnem: Mnemonic) Lir {
166 return switch (mnem) {
167 // zig fmt: off
168
169 // OP
170 .add => .{ .opcode = .OP, .format = .R, .data = .{ .ff = .{ .funct3 = 0b000, .funct7 = 0b0000000 } } },
171 .sub => .{ .opcode = .OP, .format = .R, .data = .{ .ff = .{ .funct3 = 0b000, .funct7 = 0b0100000 } } },
172
173 .@"and" => .{ .opcode = .OP, .format = .R, .data = .{ .ff = .{ .funct3 = 0b111, .funct7 = 0b0000000 } } },
174 .@"or" => .{ .opcode = .OP, .format = .R, .data = .{ .ff = .{ .funct3 = 0b110, .funct7 = 0b0000000 } } },
175 .xor => .{ .opcode = .OP, .format = .R, .data = .{ .ff = .{ .funct3 = 0b100, .funct7 = 0b0000000 } } },
176
177 .sltu => .{ .opcode = .OP, .format = .R, .data = .{ .ff = .{ .funct3 = 0b011, .funct7 = 0b0000000 } } },
178 .slt => .{ .opcode = .OP, .format = .R, .data = .{ .ff = .{ .funct3 = 0b010, .funct7 = 0b0000000 } } },
179
180 .mul => .{ .opcode = .OP, .format = .R, .data = .{ .ff = .{ .funct3 = 0b000, .funct7 = 0b0000001 } } },
181 .mulh => .{ .opcode = .OP, .format = .R, .data = .{ .ff = .{ .funct3 = 0b001, .funct7 = 0b0000001 } } },
182 .mulhsu => .{ .opcode = .OP, .format = .R, .data = .{ .ff = .{ .funct3 = 0b010, .funct7 = 0b0000001 } } },
183 .mulhu => .{ .opcode = .OP, .format = .R, .data = .{ .ff = .{ .funct3 = 0b011, .funct7 = 0b0000001 } } },
184
185 .div => .{ .opcode = .OP, .format = .R, .data = .{ .ff = .{ .funct3 = 0b100, .funct7 = 0b0000001 } } },
186 .divu => .{ .opcode = .OP, .format = .R, .data = .{ .ff = .{ .funct3 = 0b101, .funct7 = 0b0000001 } } },
187
188 .rem => .{ .opcode = .OP, .format = .R, .data = .{ .ff = .{ .funct3 = 0b110, .funct7 = 0b0000001 } } },
189 .remu => .{ .opcode = .OP, .format = .R, .data = .{ .ff = .{ .funct3 = 0b111, .funct7 = 0b0000001 } } },
190
191 .sll => .{ .opcode = .OP, .format = .R, .data = .{ .ff = .{ .funct3 = 0b001, .funct7 = 0b0000000 } } },
192 .srl => .{ .opcode = .OP, .format = .R, .data = .{ .ff = .{ .funct3 = 0b101, .funct7 = 0b0000000 } } },
193 .sra => .{ .opcode = .OP, .format = .R, .data = .{ .ff = .{ .funct3 = 0b101, .funct7 = 0b0100000 } } },
194
195
196 // OP_IMM
197
198 .addi => .{ .opcode = .OP_IMM, .format = .I, .data = .{ .f = .{ .funct3 = 0b000 } } },
199 .andi => .{ .opcode = .OP_IMM, .format = .I, .data = .{ .f = .{ .funct3 = 0b111 } } },
200 .xori => .{ .opcode = .OP_IMM, .format = .I, .data = .{ .f = .{ .funct3 = 0b100 } } },
201
202 .sltiu => .{ .opcode = .OP_IMM, .format = .I, .data = .{ .f = .{ .funct3 = 0b011 } } },
203
204 .slli => .{ .opcode = .OP_IMM, .format = .I, .data = .{ .sh = .{ .typ = 0b000000, .funct3 = 0b001, .has_5 = true } } },
205 .srli => .{ .opcode = .OP_IMM, .format = .I, .data = .{ .sh = .{ .typ = 0b000000, .funct3 = 0b101, .has_5 = true } } },
206 .srai => .{ .opcode = .OP_IMM, .format = .I, .data = .{ .sh = .{ .typ = 0b010000, .funct3 = 0b101, .has_5 = true } } },
207
208 .clz => .{ .opcode = .OP_IMM, .format = .R, .data = .{ .ff = .{ .funct3 = 0b001, .funct7 = 0b0110000 } } },
209 .cpop => .{ .opcode = .OP_IMM, .format = .R, .data = .{ .ff = .{ .funct3 = 0b001, .funct7 = 0b0110000 } } },
210
211 // OP_IMM_32
212
213 .slliw => .{ .opcode = .OP_IMM_32, .format = .I, .data = .{ .sh = .{ .typ = 0b000000, .funct3 = 0b001, .has_5 = false } } },
214 .srliw => .{ .opcode = .OP_IMM_32, .format = .I, .data = .{ .sh = .{ .typ = 0b000000, .funct3 = 0b101, .has_5 = false } } },
215 .sraiw => .{ .opcode = .OP_IMM_32, .format = .I, .data = .{ .sh = .{ .typ = 0b010000, .funct3 = 0b101, .has_5 = false } } },
216
217 .clzw => .{ .opcode = .OP_IMM_32, .format = .R, .data = .{ .ff = .{ .funct3 = 0b001, .funct7 = 0b0110000 } } },
218 .cpopw => .{ .opcode = .OP_IMM_32, .format = .R, .data = .{ .ff = .{ .funct3 = 0b001, .funct7 = 0b0110000 } } },
219
220 // OP_32
221
222 .addw => .{ .opcode = .OP_32, .format = .R, .data = .{ .ff = .{ .funct3 = 0b000, .funct7 = 0b0000000 } } },
223 .subw => .{ .opcode = .OP_32, .format = .R, .data = .{ .ff = .{ .funct3 = 0b000, .funct7 = 0b0100000 } } },
224 .mulw => .{ .opcode = .OP_32, .format = .R, .data = .{ .ff = .{ .funct3 = 0b000, .funct7 = 0b0000001 } } },
225
226 .divw => .{ .opcode = .OP_32, .format = .R, .data = .{ .ff = .{ .funct3 = 0b100, .funct7 = 0b0000001 } } },
227 .divuw => .{ .opcode = .OP_32, .format = .R, .data = .{ .ff = .{ .funct3 = 0b101, .funct7 = 0b0000001 } } },
228
229 .remw => .{ .opcode = .OP_32, .format = .R, .data = .{ .ff = .{ .funct3 = 0b110, .funct7 = 0b0000001 } } },
230 .remuw => .{ .opcode = .OP_32, .format = .R, .data = .{ .ff = .{ .funct3 = 0b111, .funct7 = 0b0000001 } } },
231
232 .sllw => .{ .opcode = .OP_32, .format = .R, .data = .{ .ff = .{ .funct3 = 0b001, .funct7 = 0b0000000 } } },
233 .srlw => .{ .opcode = .OP_32, .format = .R, .data = .{ .ff = .{ .funct3 = 0b101, .funct7 = 0b0000000 } } },
234 .sraw => .{ .opcode = .OP_32, .format = .R, .data = .{ .ff = .{ .funct3 = 0b101, .funct7 = 0b0100000 } } },
235
236
237 // OP_FP
238
239 .fadds => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b00000, .fmt = .S, .rm = 0b111 } } },
240 .faddd => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b00000, .fmt = .D, .rm = 0b111 } } },
241
242 .fsubs => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b00001, .fmt = .S, .rm = 0b111 } } },
243 .fsubd => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b00001, .fmt = .D, .rm = 0b111 } } },
244
245 .fmuls => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b00010, .fmt = .S, .rm = 0b111 } } },
246 .fmuld => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b00010, .fmt = .D, .rm = 0b111 } } },
247
248 .fdivs => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b00011, .fmt = .S, .rm = 0b111 } } },
249 .fdivd => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b00011, .fmt = .D, .rm = 0b111 } } },
250
251 .fmins => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b00101, .fmt = .S, .rm = 0b000 } } },
252 .fmind => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b00101, .fmt = .D, .rm = 0b000 } } },
253
254 .fmaxs => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b00101, .fmt = .S, .rm = 0b001 } } },
255 .fmaxd => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b00101, .fmt = .D, .rm = 0b001 } } },
256
257 .fsqrts => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b01011, .fmt = .S, .rm = 0b111 } } },
258 .fsqrtd => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b01011, .fmt = .D, .rm = 0b111 } } },
259
260 .fles => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b10100, .fmt = .S, .rm = 0b000 } } },
261 .fled => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b10100, .fmt = .D, .rm = 0b000 } } },
262
263 .flts => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b10100, .fmt = .S, .rm = 0b001 } } },
264 .fltd => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b10100, .fmt = .D, .rm = 0b001 } } },
265
266 .feqs => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b10100, .fmt = .S, .rm = 0b010 } } },
267 .feqd => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b10100, .fmt = .D, .rm = 0b010 } } },
268
269 .fsgnjns => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b00100, .fmt = .S, .rm = 0b000 } } },
270 .fsgnjnd => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b00100, .fmt = .D, .rm = 0b000 } } },
271
272 .fsgnjxs => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b00100, .fmt = .S, .rm = 0b010 } } },
273 .fsgnjxd => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b00100, .fmt = .D, .rm = 0b010 } } },
274
275 .fcvtws => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fcvt = .{ .funct5 = 0b11000, .fmt = .S, .rm = 0b111, .width = .w } } },
276 .fcvtwus => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fcvt = .{ .funct5 = 0b11000, .fmt = .S, .rm = 0b111, .width = .wu } } },
277 .fcvtls => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fcvt = .{ .funct5 = 0b11000, .fmt = .S, .rm = 0b111, .width = .l } } },
278 .fcvtlus => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fcvt = .{ .funct5 = 0b11000, .fmt = .S, .rm = 0b111, .width = .lu } } },
279
280 .fcvtwd => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fcvt = .{ .funct5 = 0b11000, .fmt = .D, .rm = 0b111, .width = .w } } },
281 .fcvtwud => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fcvt = .{ .funct5 = 0b11000, .fmt = .D, .rm = 0b111, .width = .wu } } },
282 .fcvtld => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fcvt = .{ .funct5 = 0b11000, .fmt = .D, .rm = 0b111, .width = .l } } },
283 .fcvtlud => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fcvt = .{ .funct5 = 0b11000, .fmt = .D, .rm = 0b111, .width = .lu } } },
284
285 .fcvtsw => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fcvt = .{ .funct5 = 0b11010, .fmt = .S, .rm = 0b111, .width = .w } } },
286 .fcvtswu => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fcvt = .{ .funct5 = 0b11010, .fmt = .S, .rm = 0b111, .width = .wu } } },
287 .fcvtsl => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fcvt = .{ .funct5 = 0b11010, .fmt = .S, .rm = 0b111, .width = .l } } },
288 .fcvtslu => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fcvt = .{ .funct5 = 0b11010, .fmt = .S, .rm = 0b111, .width = .lu } } },
289
290 .fcvtdw => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fcvt = .{ .funct5 = 0b11010, .fmt = .D, .rm = 0b111, .width = .w } } },
291 .fcvtdwu => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fcvt = .{ .funct5 = 0b11010, .fmt = .D, .rm = 0b111, .width = .wu } } },
292 .fcvtdl => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fcvt = .{ .funct5 = 0b11010, .fmt = .D, .rm = 0b111, .width = .l } } },
293 .fcvtdlu => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fcvt = .{ .funct5 = 0b11010, .fmt = .D, .rm = 0b111, .width = .lu } } },
294
295 // LOAD
296
297 .lb => .{ .opcode = .LOAD, .format = .I, .data = .{ .f = .{ .funct3 = 0b000 } } },
298 .lh => .{ .opcode = .LOAD, .format = .I, .data = .{ .f = .{ .funct3 = 0b001 } } },
299 .lw => .{ .opcode = .LOAD, .format = .I, .data = .{ .f = .{ .funct3 = 0b010 } } },
300 .ld => .{ .opcode = .LOAD, .format = .I, .data = .{ .f = .{ .funct3 = 0b011 } } },
301 .lbu => .{ .opcode = .LOAD, .format = .I, .data = .{ .f = .{ .funct3 = 0b100 } } },
302 .lhu => .{ .opcode = .LOAD, .format = .I, .data = .{ .f = .{ .funct3 = 0b101 } } },
303 .lwu => .{ .opcode = .LOAD, .format = .I, .data = .{ .f = .{ .funct3 = 0b110 } } },
304
305
306 // STORE
307
308 .sb => .{ .opcode = .STORE, .format = .S, .data = .{ .f = .{ .funct3 = 0b000 } } },
309 .sh => .{ .opcode = .STORE, .format = .S, .data = .{ .f = .{ .funct3 = 0b001 } } },
310 .sw => .{ .opcode = .STORE, .format = .S, .data = .{ .f = .{ .funct3 = 0b010 } } },
311 .sd => .{ .opcode = .STORE, .format = .S, .data = .{ .f = .{ .funct3 = 0b011 } } },
312
313
314 // LOAD_FP
315
316 .flw => .{ .opcode = .LOAD_FP, .format = .I, .data = .{ .f = .{ .funct3 = 0b010 } } },
317 .fld => .{ .opcode = .LOAD_FP, .format = .I, .data = .{ .f = .{ .funct3 = 0b011 } } },
318
319 .vle8v => .{ .opcode = .LOAD_FP, .format = .R, .data = .{ .vecls = .{ .width = .@"8", .umop = .unit, .vm = true, .mop = .unit, .mew = false, .nf = 0b000 } } },
320 .vle16v => .{ .opcode = .LOAD_FP, .format = .R, .data = .{ .vecls = .{ .width = .@"16", .umop = .unit, .vm = true, .mop = .unit, .mew = false, .nf = 0b000 } } },
321 .vle32v => .{ .opcode = .LOAD_FP, .format = .R, .data = .{ .vecls = .{ .width = .@"32", .umop = .unit, .vm = true, .mop = .unit, .mew = false, .nf = 0b000 } } },
322 .vle64v => .{ .opcode = .LOAD_FP, .format = .R, .data = .{ .vecls = .{ .width = .@"64", .umop = .unit, .vm = true, .mop = .unit, .mew = false, .nf = 0b000 } } },
323
324
325 // STORE_FP
326
327 .fsw => .{ .opcode = .STORE_FP, .format = .S, .data = .{ .f = .{ .funct3 = 0b010 } } },
328 .fsd => .{ .opcode = .STORE_FP, .format = .S, .data = .{ .f = .{ .funct3 = 0b011 } } },
329
330 .vse8v => .{ .opcode = .STORE_FP, .format = .R, .data = .{ .vecls = .{ .width = .@"8", .umop = .unit, .vm = true, .mop = .unit, .mew = false, .nf = 0b000 } } },
331 .vse16v => .{ .opcode = .STORE_FP, .format = .R, .data = .{ .vecls = .{ .width = .@"16", .umop = .unit, .vm = true, .mop = .unit, .mew = false, .nf = 0b000 } } },
332 .vse32v => .{ .opcode = .STORE_FP, .format = .R, .data = .{ .vecls = .{ .width = .@"32", .umop = .unit, .vm = true, .mop = .unit, .mew = false, .nf = 0b000 } } },
333 .vse64v => .{ .opcode = .STORE_FP, .format = .R, .data = .{ .vecls = .{ .width = .@"64", .umop = .unit, .vm = true, .mop = .unit, .mew = false, .nf = 0b000 } } },
334
335 // JALR
336
337 .jalr => .{ .opcode = .JALR, .format = .I, .data = .{ .f = .{ .funct3 = 0b000 } } },
338
339
340 // LUI
341
342 .lui => .{ .opcode = .LUI, .format = .U, .data = .{ .none = {} } },
343
344
345 // AUIPC
346
347 .auipc => .{ .opcode = .AUIPC, .format = .U, .data = .{ .none = {} } },
348
349
350 // JAL
351
352 .jal => .{ .opcode = .JAL, .format = .J, .data = .{ .none = {} } },
353
354
355 // BRANCH
356
357 .beq => .{ .opcode = .BRANCH, .format = .B, .data = .{ .f = .{ .funct3 = 0b000 } } },
358 .bne => .{ .opcode = .BRANCH, .format = .B, .data = .{ .f = .{ .funct3 = 0b001 } } },
359
360
361 // SYSTEM
362
363 .ecall => .{ .opcode = .SYSTEM, .format = .extra, .data = .{ .extra = .ecall } },
364 .ebreak => .{ .opcode = .SYSTEM, .format = .extra, .data = .{ .extra = .ebreak } },
365
366 .csrrs => .{ .opcode = .SYSTEM, .format = .I, .data = .{ .f = .{ .funct3 = 0b010 } } },
367
368
369 // NONE
370
371 .unimp => .{ .opcode = .NONE, .format = .extra, .data = .{ .extra = .unimp } },
372
373
374 // MISC_MEM
375
376 .fence => .{ .opcode = .MISC_MEM, .format = .I, .data = .{ .fence = .{ .funct3 = 0b000, .fm = .none } } },
377 .fencetso => .{ .opcode = .MISC_MEM, .format = .I, .data = .{ .fence = .{ .funct3 = 0b000, .fm = .tso } } },
378
379
380 // AMO
381
382 .amoaddw => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .W, .funct5 = 0b00000 } } },
383 .amoswapw => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .W, .funct5 = 0b00001 } } },
384 .lrw => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .W, .funct5 = 0b00010 } } },
385 .scw => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .W, .funct5 = 0b00011 } } },
386 .amoxorw => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .W, .funct5 = 0b00100 } } },
387 .amoandw => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .W, .funct5 = 0b01100 } } },
388 .amoorw => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .W, .funct5 = 0b01000 } } },
389 .amominw => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .W, .funct5 = 0b10000 } } },
390 .amomaxw => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .W, .funct5 = 0b10100 } } },
391 .amominuw => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .W, .funct5 = 0b11000 } } },
392 .amomaxuw => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .W, .funct5 = 0b11100 } } },
393
394
395 .amoaddd => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .D, .funct5 = 0b00000 } } },
396 .amoswapd => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .D, .funct5 = 0b00001 } } },
397 .lrd => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .D, .funct5 = 0b00010 } } },
398 .scd => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .D, .funct5 = 0b00011 } } },
399 .amoxord => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .D, .funct5 = 0b00100 } } },
400 .amoandd => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .D, .funct5 = 0b01100 } } },
401 .amoord => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .D, .funct5 = 0b01000 } } },
402 .amomind => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .D, .funct5 = 0b10000 } } },
403 .amomaxd => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .D, .funct5 = 0b10100 } } },
404 .amominud => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .D, .funct5 = 0b11000 } } },
405 .amomaxud => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .D, .funct5 = 0b11100 } } },
406
407 // OP_V
408 .vsetivli => .{ .opcode = .OP_V, .format = .I, .data = .{ .f = .{ .funct3 = 0b111 } } },
409 .vsetvli => .{ .opcode = .OP_V, .format = .I, .data = .{ .f = .{ .funct3 = 0b111 } } },
410 .vaddvv => .{ .opcode = .OP_V, .format = .R, .data = .{ .vecmath = .{ .vm = true, .funct6 = 0b000000, .funct3 = .OPIVV } } },
411 .vsubvv => .{ .opcode = .OP_V, .format = .R, .data = .{ .vecmath = .{ .vm = true, .funct6 = 0b000010, .funct3 = .OPIVV } } },
412 .vmulvv => .{ .opcode = .OP_V, .format = .R, .data = .{ .vecmath = .{ .vm = true, .funct6 = 0b100101, .funct3 = .OPIVV } } },
413
414 .vfaddvv => .{ .opcode = .OP_V, .format = .R, .data = .{ .vecmath = .{ .vm = true, .funct6 = 0b000000, .funct3 = .OPFVV } } },
415 .vfsubvv => .{ .opcode = .OP_V, .format = .R, .data = .{ .vecmath = .{ .vm = true, .funct6 = 0b000010, .funct3 = .OPFVV } } },
416 .vfmulvv => .{ .opcode = .OP_V, .format = .R, .data = .{ .vecmath = .{ .vm = true, .funct6 = 0b100100, .funct3 = .OPFVV } } },
417
418 .vadcvv => .{ .opcode = .OP_V, .format = .R, .data = .{ .vecmath = .{ .vm = true, .funct6 = 0b010000, .funct3 = .OPMVV } } },
419 .vmvvx => .{ .opcode = .OP_V, .format = .R, .data = .{ .vecmath = .{ .vm = true, .funct6 = 0b010111, .funct3 = .OPIVX } } },
420
421 .vslidedownvx => .{ .opcode = .OP_V, .format = .R, .data = .{ .vecmath = .{ .vm = true, .funct6 = 0b001111, .funct3 = .OPIVX } } },
422
423
424 .pseudo_prologue,
425 .pseudo_epilogue,
426 .pseudo_dbg_prologue_end,
427 .pseudo_dbg_epilogue_begin,
428 .pseudo_dbg_line_column,
429 .pseudo_load_rm,
430 .pseudo_store_rm,
431 .pseudo_lea_rm,
432 .pseudo_j,
433 .pseudo_dead,
434 .pseudo_load_symbol,
435 .pseudo_load_tlv,
436 .pseudo_mv,
437 .pseudo_restore_regs,
438 .pseudo_spill_regs,
439 .pseudo_compare,
440 .pseudo_not,
441 .pseudo_extern_fn_reloc,
442 .nop,
443 => std.debug.panic("lir: didn't catch pseudo {s}", .{@tagName(mnem)}),
444 // zig fmt: on
445 };
446 }
447};
448
449/// This is the final form of the instruction. Lir is transformed into
450/// this, which is then bitcast into a u32.
451pub const Instruction = union(Lir.Format) {
452 R: packed struct(u32) {
453 opcode: u7,
454 rd: u5,
455 funct3: u3,
456 rs1: u5,
457 rs2: u5,
458 funct7: u7,
459 },
460 I: packed struct(u32) {
461 opcode: u7,
462 rd: u5,
463 funct3: u3,
464 rs1: u5,
465 imm0_11: u12,
466 },
467 S: packed struct(u32) {
468 opcode: u7,
469 imm0_4: u5,
470 funct3: u3,
471 rs1: u5,
472 rs2: u5,
473 imm5_11: u7,
474 },
475 B: packed struct(u32) {
476 opcode: u7,
477 imm11: u1,
478 imm1_4: u4,
479 funct3: u3,
480 rs1: u5,
481 rs2: u5,
482 imm5_10: u6,
483 imm12: u1,
484 },
485 U: packed struct(u32) {
486 opcode: u7,
487 rd: u5,
488 imm12_31: u20,
489 },
490 J: packed struct(u32) {
491 opcode: u7,
492 rd: u5,
493 imm12_19: u8,
494 imm11: u1,
495 imm1_10: u10,
496 imm20: u1,
497 },
498 extra: u32,
499
500 comptime {
501 for (std.meta.fields(Instruction)) |field| {
502 assert(@bitSizeOf(field.type) == 32);
503 }
504 }
505
506 pub const Operand = union(enum) {
507 none,
508 reg: Register,
509 csr: CSR,
510 mem: Memory,
511 imm: Immediate,
512 barrier: Mir.Barrier,
513 };
514
515 pub fn toU32(inst: Instruction) u32 {
516 return switch (inst) {
517 inline else => |v| @bitCast(v),
518 };
519 }
520
521 pub fn encode(inst: Instruction, writer: *std.Io.Writer) !void {
522 try writer.writeInt(u32, inst.toU32(), .little);
523 }
524
525 pub fn fromLir(lir: Lir, ops: []const Operand) Instruction {
526 const opcode: u7 = @intFromEnum(lir.opcode);
527
528 switch (lir.format) {
529 .R => {
530 return .{
531 .R = switch (lir.data) {
532 .ff => |ff| .{
533 .rd = ops[0].reg.encodeId(),
534 .rs1 = ops[1].reg.encodeId(),
535 .rs2 = ops[2].reg.encodeId(),
536
537 .opcode = opcode,
538 .funct3 = ff.funct3,
539 .funct7 = ff.funct7,
540 },
541 .fmt => |fmt| .{
542 .rd = ops[0].reg.encodeId(),
543 .rs1 = ops[1].reg.encodeId(),
544 .rs2 = ops[2].reg.encodeId(),
545
546 .opcode = opcode,
547 .funct3 = fmt.rm,
548 .funct7 = (@as(u7, fmt.funct5) << 2) | @intFromEnum(fmt.fmt),
549 },
550 .fcvt => |fcvt| .{
551 .rd = ops[0].reg.encodeId(),
552 .rs1 = ops[1].reg.encodeId(),
553 .rs2 = @intFromEnum(fcvt.width),
554
555 .opcode = opcode,
556 .funct3 = fcvt.rm,
557 .funct7 = (@as(u7, fcvt.funct5) << 2) | @intFromEnum(fcvt.fmt),
558 },
559 .vecls => |vec| .{
560 .rd = ops[0].reg.encodeId(),
561 .rs1 = ops[1].reg.encodeId(),
562
563 .rs2 = @intFromEnum(vec.umop),
564
565 .opcode = opcode,
566 .funct3 = @intFromEnum(vec.width),
567 .funct7 = (@as(u7, vec.nf) << 4) | (@as(u7, @intFromBool(vec.mew)) << 3) | (@as(u7, @intFromEnum(vec.mop)) << 1) | @intFromBool(vec.vm),
568 },
569 .vecmath => |vec| .{
570 .rd = ops[0].reg.encodeId(),
571 .rs1 = ops[1].reg.encodeId(),
572 .rs2 = ops[2].reg.encodeId(),
573
574 .opcode = opcode,
575 .funct3 = @intFromEnum(vec.funct3),
576 .funct7 = (@as(u7, vec.funct6) << 1) | @intFromBool(vec.vm),
577 },
578 .amo => |amo| .{
579 .rd = ops[0].reg.encodeId(),
580 .rs1 = ops[1].reg.encodeId(),
581 .rs2 = ops[2].reg.encodeId(),
582
583 .opcode = opcode,
584 .funct3 = @intFromEnum(amo.width),
585 .funct7 = @as(u7, amo.funct5) << 2 |
586 @as(u7, @intFromBool(ops[3].barrier == .rl)) << 1 |
587 @as(u7, @intFromBool(ops[4].barrier == .aq)),
588 },
589 else => unreachable,
590 },
591 };
592 },
593 .S => {
594 assert(ops.len == 3);
595 const umm = ops[2].imm.asBits(u12);
596 return .{
597 .S = .{
598 .imm0_4 = @truncate(umm),
599 .rs1 = ops[0].reg.encodeId(),
600 .rs2 = ops[1].reg.encodeId(),
601 .imm5_11 = @truncate(umm >> 5),
602
603 .opcode = opcode,
604 .funct3 = lir.data.f.funct3,
605 },
606 };
607 },
608 .I => {
609 return .{
610 .I = switch (lir.data) {
611 .f => |f| .{
612 .rd = ops[0].reg.encodeId(),
613 .rs1 = ops[1].reg.encodeId(),
614 .imm0_11 = ops[2].imm.asBits(u12),
615
616 .opcode = opcode,
617 .funct3 = f.funct3,
618 },
619 .sh => |sh| .{
620 .rd = ops[0].reg.encodeId(),
621 .rs1 = ops[1].reg.encodeId(),
622 .imm0_11 = (@as(u12, sh.typ) << 6) |
623 if (sh.has_5) ops[2].imm.asBits(u6) else (@as(u6, 0) | ops[2].imm.asBits(u5)),
624
625 .opcode = opcode,
626 .funct3 = sh.funct3,
627 },
628 .fence => |fence| .{
629 .rd = 0,
630 .rs1 = 0,
631 .funct3 = 0,
632 .imm0_11 = (@as(u12, @intFromEnum(fence.fm)) << 8) |
633 (@as(u12, @intFromEnum(ops[1].barrier)) << 4) |
634 @as(u12, @intFromEnum(ops[0].barrier)),
635 .opcode = opcode,
636 },
637 else => unreachable,
638 },
639 };
640 },
641 .U => {
642 assert(ops.len == 2);
643 return .{
644 .U = .{
645 .rd = ops[0].reg.encodeId(),
646 .imm12_31 = ops[1].imm.asBits(u20),
647
648 .opcode = opcode,
649 },
650 };
651 },
652 .J => {
653 assert(ops.len == 2);
654
655 const umm = ops[1].imm.asBits(u21);
656 // the RISC-V spec says the target index of a jump
657 // must be a multiple of 2
658 assert(umm % 2 == 0);
659
660 return .{
661 .J = .{
662 .rd = ops[0].reg.encodeId(),
663 .imm1_10 = @truncate(umm >> 1),
664 .imm11 = @truncate(umm >> 11),
665 .imm12_19 = @truncate(umm >> 12),
666 .imm20 = @truncate(umm >> 20),
667
668 .opcode = opcode,
669 },
670 };
671 },
672 .B => {
673 assert(ops.len == 3);
674
675 const umm = ops[2].imm.asBits(u13);
676 // the RISC-V spec says the target index of a branch
677 // must be a multiple of 2
678 assert(umm % 2 == 0);
679
680 return .{
681 .B = .{
682 .rs1 = ops[0].reg.encodeId(),
683 .rs2 = ops[1].reg.encodeId(),
684 .imm1_4 = @truncate(umm >> 1),
685 .imm5_10 = @truncate(umm >> 5),
686 .imm11 = @truncate(umm >> 11),
687 .imm12 = @truncate(umm >> 12),
688
689 .opcode = opcode,
690 .funct3 = lir.data.f.funct3,
691 },
692 };
693 },
694 .extra => {
695 assert(ops.len == 0);
696
697 return .{
698 .I = .{
699 .rd = Register.zero.encodeId(),
700 .rs1 = Register.zero.encodeId(),
701 .imm0_11 = switch (lir.data.extra) {
702 .ecall => 0x000,
703 .ebreak => 0x001,
704 .unimp => 0x000,
705 else => unreachable,
706 },
707
708 .opcode = opcode,
709 .funct3 = 0b000,
710 },
711 };
712 },
713 }
714 }
715};
716
717const std = @import("std");
718const assert = std.debug.assert;
719const log = std.log.scoped(.format);
720
721const bits = @import("bits.zig");
722const Mir = @import("Mir.zig");
723const Mnemonic = @import("mnem.zig").Mnemonic;
724const Lower = @import("Lower.zig");
725
726const Register = bits.Register;
727const CSR = bits.CSR;
728const Memory = bits.Memory;
729const Immediate = bits.Immediate;