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;