Commit 571aa694fd

David Rubin <daviru007@icloud.com>
2024-06-03 04:25:53
riscv: vectors part 1
1 parent f2301ba
src/arch/riscv64/abi.zig
@@ -193,6 +193,15 @@ pub fn classifySystem(ty: Type, pt: Zcu.PerThread) [8]SystemClass {
             }
             return memory_class;
         },
+        .Vector => {
+            // we pass vectors through integer registers if they are small enough to fit.
+            const vec_bits = ty.totalVectorBits(pt);
+            if (vec_bits <= 64) {
+                result[0] = .integer;
+                return result;
+            }
+            return memory_class;
+        },
         else => |bad_ty| std.debug.panic("classifySystem {s}", .{@tagName(bad_ty)}),
     }
 }
@@ -254,15 +263,15 @@ fn classifyStruct(
     }
 }
 
-const allocatable_registers = Registers.Integer.all_regs ++ Registers.Float.all_regs;
+const allocatable_registers = Registers.Integer.all_regs ++ Registers.Float.all_regs ++ Registers.Vector.all_regs;
 pub const RegisterManager = RegisterManagerFn(@import("CodeGen.zig"), Register, &allocatable_registers);
 
-// Register classes
 const RegisterBitSet = RegisterManager.RegisterBitSet;
 
 pub const RegisterClass = enum {
     int,
     float,
+    vector,
 };
 
 pub const Registers = struct {
@@ -322,6 +331,19 @@ pub const Registers = struct {
 
         pub const all_regs = callee_preserved_regs ++ function_arg_regs ++ temporary_regs;
     };
+
+    pub const Vector = struct {
+        pub const general_purpose = initRegBitSet(Integer.all_regs.len + Float.all_regs.len, all_regs.len);
+
+        // zig fmt: off
+        pub const all_regs = [_]Register{
+            .v0,  .v1,  .v2,  .v3,  .v4,  .v5,  .v6,  .v7,
+            .v8,  .v9,  .v10, .v11, .v12, .v13, .v14, .v15,
+            .v16, .v17, .v18, .v19, .v20, .v21, .v22, .v23,
+            .v24, .v25, .v26, .v27, .v28, .v29, .v30, .v31,
+        };
+        // zig fmt: on
+    };
 };
 
 fn initRegBitSet(start: usize, length: usize) RegisterBitSet {
src/arch/riscv64/bits.zig
@@ -128,6 +128,12 @@ pub const Immediate = union(enum) {
     }
 };
 
+pub const CSR = enum(u12) {
+    vl = 0xC20,
+    vtype = 0xC21,
+    vlenb = 0xC22,
+};
+
 pub const Register = enum(u8) {
     // zig fmt: off
 
@@ -169,6 +175,13 @@ pub const Register = enum(u8) {
     f16, f17, f18, f19, f20, f21, f22, f23, 
     f24, f25, f26, f27, f28, f29, f30, f31, 
 
+
+    // V extension registers
+    v0,  v1,  v2,  v3,  v4,  v5,  v6,  v7,
+    v8,  v9,  v10, v11, v12, v13, v14, v15,
+    v16, v17, v18, v19, v20, v21, v22, v23,
+    v24, v25, v26, v27, v28, v29, v30, v31,
+
     // zig fmt: on
 
     /// in RISC-V registers are stored as 5 bit IDs and a register can have
@@ -180,11 +193,12 @@ pub const Register = enum(u8) {
     /// The goal of this function is to return the same ID for `zero` and `x0` but two
     /// seperate IDs for `x0` and `f0`. We will assume that each register set has 32 registers
     /// and is repeated twice, once for the named version, once for the number version.
-    pub fn id(reg: Register) u7 {
+    pub fn id(reg: Register) u8 {
         const base = switch (@intFromEnum(reg)) {
             // zig fmt: off
             @intFromEnum(Register.zero) ... @intFromEnum(Register.x31) => @intFromEnum(Register.zero),
             @intFromEnum(Register.ft0)  ... @intFromEnum(Register.f31) => @intFromEnum(Register.ft0),
+            @intFromEnum(Register.v0)   ... @intFromEnum(Register.v31) => @intFromEnum(Register.v0),
             else => unreachable,
             // zig fmt: on
         };
@@ -207,6 +221,7 @@ pub const Register = enum(u8) {
             // zig fmt: off
             @intFromEnum(Register.zero) ... @intFromEnum(Register.x31) => 64,
             @intFromEnum(Register.ft0)  ... @intFromEnum(Register.f31) => if (Target.riscv.featureSetHas(features, .d)) 64 else 32,
+            @intFromEnum(Register.v0)   ... @intFromEnum(Register.v31) => 1024, // TODO: look at suggestVectorSize
             else => unreachable,
             // zig fmt: on
         };
@@ -217,6 +232,7 @@ pub const Register = enum(u8) {
             // zig fmt: off
             @intFromEnum(Register.zero) ... @intFromEnum(Register.x31) => .int,
             @intFromEnum(Register.ft0)  ... @intFromEnum(Register.f31) => .float,
+            @intFromEnum(Register.v0)   ... @intFromEnum(Register.v31) => .vector,
             else => unreachable,
             // zig fmt: on
         };
@@ -272,3 +288,27 @@ pub const Symbol = struct {
     /// Index into the linker's symbol table.
     sym_index: u32,
 };
+
+pub const VType = packed struct(u8) {
+    vlmul: VlMul,
+    vsew: VSew,
+    vta: bool,
+    vma: bool,
+};
+
+const VSew = enum(u3) {
+    @"8" = 0b000,
+    @"16" = 0b001,
+    @"32" = 0b010,
+    @"64" = 0b011,
+};
+
+const VlMul = enum(u3) {
+    mf8 = 0b101,
+    mf4 = 0b110,
+    mf2 = 0b111,
+    m1 = 0b000,
+    m2 = 0b001,
+    m4 = 0b010,
+    m8 = 0b011,
+};
src/arch/riscv64/CodeGen.zig
@@ -37,6 +37,7 @@ const abi = @import("abi.zig");
 const Lower = @import("Lower.zig");
 
 const Register = bits.Register;
+const CSR = bits.CSR;
 const Immediate = bits.Immediate;
 const Memory = bits.Memory;
 const FrameIndex = bits.FrameIndex;
@@ -87,6 +88,9 @@ exitlude_jump_relocs: std.ArrayListUnmanaged(usize) = .{},
 /// across each runtime branch upon joining.
 branch_stack: *std.ArrayList(Branch),
 
+// The current bit length of vector registers.
+vec_len: u32,
+
 // Key is the block instruction
 blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{},
 register_manager: RegisterManager = .{},
@@ -747,6 +751,7 @@ pub fn generate(
         .end_di_line = func.rbrace_line,
         .end_di_column = func.rbrace_column,
         .scope_generation = 0,
+        .vec_len = 16 * 8, // TODO: set this per cpu
     };
     defer {
         function.frame_allocs.deinit(gpa);
@@ -1040,10 +1045,60 @@ pub fn addExtraAssumeCapacity(func: *Func, extra: anytype) u32 {
     return result;
 }
 
+/// Returns a temporary register that contains the value of the `reg` csr.
+///
+/// Caller's duty to lock the return register is needed.
+fn getCsr(func: *Func, csr: CSR) !Register {
+    assert(func.hasFeature(.zicsr));
+    const dst_reg = try func.register_manager.allocReg(null, func.regTempClassForType(Type.usize));
+    _ = try func.addInst(.{
+        .tag = .csrrs,
+        .ops = .csr,
+        .data = .{
+            .csr = .{
+                .csr = csr,
+                .rd = dst_reg,
+                .rs1 = .x0,
+            },
+        },
+    });
+    return dst_reg;
+}
+
+fn setVl(func: *Func, dst_reg: Register, avl: u5, options: bits.VType) !void {
+    if (avl == 0) {
+        const options_int: u12 = @as(u12, 0) | @as(u8, @bitCast(options));
+        _ = try func.addInst(.{
+            .tag = .vsetvli,
+            .ops = .rri,
+            .data = .{ .i_type = .{
+                .rd = dst_reg,
+                .rs1 = .zero,
+                .imm12 = Immediate.u(options_int),
+            } },
+        });
+    } else {
+        const options_int: u12 = (~@as(u12, 0) << 10) | @as(u8, @bitCast(options));
+        _ = try func.addInst(.{
+            .tag = .vsetivli,
+            .ops = .rri,
+            .data = .{
+                .i_type = .{
+                    .rd = dst_reg,
+                    .rs1 = @enumFromInt(avl),
+                    .imm12 = Immediate.u(options_int),
+                },
+            },
+        });
+    }
+}
+
 const required_features = [_]Target.riscv.Feature{
     .d,
     .m,
     .a,
+    .zicsr,
+    .v,
 };
 
 fn gen(func: *Func) !void {
@@ -1101,7 +1156,19 @@ fn gen(func: *Func) !void {
         const backpatch_ra_restore = try func.addPseudo(.pseudo_dead);
         const backpatch_fp_restore = try func.addPseudo(.pseudo_dead);
         const backpatch_stack_alloc_restore = try func.addPseudo(.pseudo_dead);
-        try func.addPseudoNone(.pseudo_ret);
+
+        // ret
+        _ = try func.addInst(.{
+            .tag = .jalr,
+            .ops = .rri,
+            .data = .{
+                .i_type = .{
+                    .rd = .zero,
+                    .rs1 = .ra,
+                    .imm12 = Immediate.s(0),
+                },
+            },
+        });
 
         const frame_layout = try func.computeFrameLayout();
         const need_save_reg = frame_layout.save_reg_list.count() > 0;
@@ -1842,8 +1909,8 @@ fn typeRegClass(func: *Func, ty: Type) abi.RegisterClass {
     const zcu = pt.zcu;
     return switch (ty.zigTypeTag(zcu)) {
         .Float => .float,
-        .Vector => @panic("TODO: typeRegClass for Vectors"),
-        inline else => .int,
+        .Vector => .vector,
+        else => .int,
     };
 }
 
@@ -1852,7 +1919,7 @@ fn regGeneralClassForType(func: *Func, ty: Type) RegisterManager.RegisterBitSet
     const zcu = pt.zcu;
     return switch (ty.zigTypeTag(zcu)) {
         .Float => abi.Registers.Float.general_purpose,
-        .Vector => @panic("TODO: regGeneralClassForType for Vectors"),
+        .Vector => abi.Registers.Vector.general_purpose,
         else => abi.Registers.Integer.general_purpose,
     };
 }
@@ -1862,7 +1929,7 @@ fn regTempClassForType(func: *Func, ty: Type) RegisterManager.RegisterBitSet {
     const zcu = pt.zcu;
     return switch (ty.zigTypeTag(zcu)) {
         .Float => abi.Registers.Float.temporary,
-        .Vector => @panic("TODO: regTempClassForType for Vectors"),
+        .Vector => abi.Registers.Vector.general_purpose, // there are no temporary vector registers
         else => abi.Registers.Integer.temporary,
     };
 }
@@ -1870,20 +1937,19 @@ fn regTempClassForType(func: *Func, ty: Type) RegisterManager.RegisterBitSet {
 fn allocRegOrMem(func: *Func, elem_ty: Type, inst: ?Air.Inst.Index, reg_ok: bool) !MCValue {
     const pt = func.pt;
 
-    const abi_size = math.cast(u32, elem_ty.abiSize(pt)) orelse {
-        return func.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(pt)});
+    const bit_size = elem_ty.bitSize(pt);
+    const min_size: u64 = switch (elem_ty.zigTypeTag(pt.zcu)) {
+        .Float => if (func.hasFeature(.d)) 64 else 32,
+        .Vector => func.vec_len,
+        else => 64,
     };
 
-    const min_size: u32 = switch (elem_ty.zigTypeTag(pt.zcu)) {
-        .Float => 4,
-        .Vector => @panic("allocRegOrMem Vector"),
-        else => 8,
-    };
-
-    if (reg_ok and abi_size <= min_size) {
+    if (reg_ok and bit_size <= min_size) {
         if (func.register_manager.tryAllocReg(inst, func.regGeneralClassForType(elem_ty))) |reg| {
             return .{ .register = reg };
         }
+    } else if (reg_ok and elem_ty.zigTypeTag(pt.zcu) == .Vector) {
+        return func.fail("did you forget to extend vector registers before allocating", .{});
     }
 
     const frame_index = try func.allocFrameIndex(FrameAlloc.initSpill(elem_ty, pt));
@@ -1896,10 +1962,13 @@ fn allocRegOrMem(func: *Func, elem_ty: Type, inst: ?Air.Inst.Index, reg_ok: bool
 fn allocReg(func: *Func, reg_class: abi.RegisterClass) !struct { Register, RegisterLock } {
     if (reg_class == .float and !func.hasFeature(.f))
         std.debug.panic("allocReg class == float where F isn't enabled", .{});
+    if (reg_class == .vector and !func.hasFeature(.v))
+        std.debug.panic("allocReg class == vector where V isn't enabled", .{});
 
     const class = switch (reg_class) {
         .int => abi.Registers.Integer.general_purpose,
         .float => abi.Registers.Float.general_purpose,
+        .vector => abi.Registers.Vector.general_purpose,
     };
 
     const reg = try func.register_manager.allocReg(null, class);
@@ -1915,7 +1984,8 @@ fn promoteReg(func: *Func, ty: Type, operand: MCValue) !struct { Register, ?Regi
         return .{ op_reg, func.register_manager.lockReg(operand.register) };
     }
 
-    const reg, const lock = try func.allocReg(func.typeRegClass(ty));
+    const class = func.typeRegClass(ty);
+    const reg, const lock = try func.allocReg(class);
     try func.genSetReg(ty, reg, operand);
     return .{ reg, lock };
 }
@@ -2372,6 +2442,42 @@ fn genBinOp(
                         },
                     });
                 },
+                .Vector => {
+                    const mir_tag: Mir.Inst.Tag = switch (tag) {
+                        .add => .vaddvv,
+                        else => return func.fail("TODO: genBinOp {s} Vector", .{@tagName(tag)}),
+                    };
+
+                    const num_elem: u5 = math.cast(u5, lhs_ty.vectorLen(zcu)) orelse {
+                        return func.fail("TODO: genBinOp use vsetvli for larger avl sizes", .{});
+                    };
+                    const elem_size = lhs_ty.childType(zcu).bitSize(pt);
+
+                    try func.setVl(.zero, num_elem, .{
+                        .vlmul = .mf2,
+                        .vsew = switch (elem_size) {
+                            8 => .@"8",
+                            16 => .@"16",
+                            32 => .@"32",
+                            64 => .@"64",
+                            else => unreachable,
+                        },
+                        .vma = true,
+                        .vta = true,
+                    });
+
+                    _ = try func.addInst(.{
+                        .tag = mir_tag,
+                        .ops = .rrr,
+                        .data = .{
+                            .r_type = .{
+                                .rd = dst_reg,
+                                .rs1 = lhs_reg,
+                                .rs2 = rhs_reg,
+                            },
+                        },
+                    });
+                },
                 else => unreachable,
             }
         },
@@ -3560,13 +3666,12 @@ fn airSetUnionTag(func: *Func, inst: Air.Inst.Index) !void {
 }
 
 fn airGetUnionTag(func: *Func, inst: Air.Inst.Index) !void {
-    const zcu = func.bin_file.comp.module.?;
-    const mod = func.bin_file.comp.module.?;
+    const pt = func.pt;
     const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
 
     const tag_ty = func.typeOfIndex(inst);
     const union_ty = func.typeOf(ty_op.operand);
-    const layout = union_ty.unionGetLayout(mod);
+    const layout = union_ty.unionGetLayout(pt);
 
     if (layout.tag_size == 0) {
         return func.finishAir(inst, .none, .{ ty_op.operand, .none, .none });
@@ -3577,7 +3682,7 @@ fn airGetUnionTag(func: *Func, inst: Air.Inst.Index) !void {
     const frame_mcv = try func.allocRegOrMem(union_ty, null, false);
     try func.genCopy(union_ty, frame_mcv, operand);
 
-    const tag_abi_size = tag_ty.abiSize(mod);
+    const tag_abi_size = tag_ty.abiSize(pt);
     const result_reg, const result_lock = try func.allocReg(.int);
     defer func.register_manager.unlockReg(result_lock);
 
@@ -3597,7 +3702,7 @@ fn airGetUnionTag(func: *Func, inst: Air.Inst.Index) !void {
             } else {
                 return func.fail(
                     "TODO implement get_union_tag for ABI larger than 8 bytes and operand {}, tag {}",
-                    .{ frame_mcv, tag_ty.fmt(zcu) },
+                    .{ frame_mcv, tag_ty.fmt(pt) },
                 );
             }
         },
@@ -3781,13 +3886,13 @@ fn airBitReverse(func: *Func, inst: Air.Inst.Index) !void {
 }
 
 fn airUnaryMath(func: *Func, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {
-    const zcu = func.bin_file.comp.module.?;
+    const pt = func.pt;
     const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
     const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else result: {
         const ty = func.typeOf(un_op);
 
         const operand = try func.resolveInst(un_op);
-        const operand_bit_size = ty.bitSize(zcu);
+        const operand_bit_size = ty.bitSize(pt);
 
         if (!math.isPowerOfTwo(operand_bit_size))
             return func.fail("TODO: airUnaryMath non-pow 2", .{});
@@ -3799,7 +3904,7 @@ fn airUnaryMath(func: *Func, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {
         const dst_reg, const dst_lock = try func.allocReg(dst_class);
         defer func.register_manager.unlockReg(dst_lock);
 
-        switch (ty.zigTypeTag(zcu)) {
+        switch (ty.zigTypeTag(pt.zcu)) {
             .Float => {
                 assert(dst_class == .float);
 
@@ -3833,7 +3938,7 @@ fn airUnaryMath(func: *Func, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {
                     else => return func.fail("TODO: airUnaryMath Float {s}", .{@tagName(tag)}),
                 }
             },
-            else => return func.fail("TODO: airUnaryMath ty: {}", .{ty.fmt(zcu)}),
+            else => return func.fail("TODO: airUnaryMath ty: {}", .{ty.fmt(pt)}),
         }
 
         break :result MCValue{ .register = dst_reg };
@@ -4510,7 +4615,27 @@ fn airRet(func: *Func, inst: Air.Inst.Index, safety: bool) !void {
         .none => {},
         .register,
         .register_pair,
-        => try func.genCopy(ret_ty, func.ret_mcv.short, .{ .air_ref = un_op }),
+        => {
+            if (ret_ty.isVector(zcu)) {
+                const bit_size = ret_ty.totalVectorBits(pt);
+
+                // set the vtype to hold the entire vector's contents in a single element
+                try func.setVl(.zero, 0, .{
+                    .vsew = switch (bit_size) {
+                        8 => .@"8",
+                        16 => .@"16",
+                        32 => .@"32",
+                        64 => .@"64",
+                        else => unreachable,
+                    },
+                    .vlmul = .m1,
+                    .vma = true,
+                    .vta = true,
+                });
+            }
+
+            try func.genCopy(ret_ty, func.ret_mcv.short, .{ .air_ref = un_op });
+        },
         .indirect => |reg_off| {
             try func.register_manager.getReg(reg_off.reg, null);
             const lock = func.register_manager.lockRegAssumeUnused(reg_off.reg);
@@ -5735,7 +5860,12 @@ fn genSetReg(func: *Func, ty: Type, reg: Register, src_mcv: MCValue) InnerError!
     const zcu = pt.zcu;
     const abi_size: u32 = @intCast(ty.abiSize(pt));
 
-    if (abi_size > 8) return std.debug.panic("tried to set reg with size {}", .{abi_size});
+    const max_size: u32 = switch (reg.class()) {
+        .int => 64,
+        .float => if (func.hasFeature(.d)) 64 else 32,
+        .vector => func.vec_len,
+    };
+    if (abi_size > max_size) return std.debug.panic("tried to set reg with size {}", .{abi_size});
     const dst_reg_class = reg.class();
 
     switch (src_mcv) {
@@ -5835,13 +5965,6 @@ fn genSetReg(func: *Func, ty: Type, reg: Register, src_mcv: MCValue) InnerError!
             if (src_reg.id() == reg.id())
                 return;
 
-            const src_reg_class = src_reg.class();
-
-            if (src_reg_class == .float and dst_reg_class == .int) {
-                // to move from float -> int, we use FMV.X.W
-                return func.fail("TODO: genSetReg float -> int", .{});
-            }
-
             // mv reg, src_reg
             _ = try func.addInst(.{
                 .tag = .pseudo,
@@ -5854,6 +5977,43 @@ fn genSetReg(func: *Func, ty: Type, reg: Register, src_mcv: MCValue) InnerError!
         },
         .register_pair => return func.fail("genSetReg should we allow reg -> reg_pair?", .{}),
         .load_frame => |frame| {
+            if (reg.class() == .vector) {
+                if (abi_size > 8)
+                    return func.fail("TODO: genSetReg vectors > 8", .{});
+
+                const temp_reg = try func.register_manager.allocReg(null, abi.Registers.Integer.temporary);
+                const temp_lock = func.register_manager.lockRegAssumeUnused(temp_reg);
+                defer func.register_manager.unlockReg(temp_lock);
+
+                try func.setVl(.zero, 1, .{
+                    .vsew = switch (abi_size) {
+                        1 => .@"8",
+                        2 => .@"16",
+                        4 => .@"32",
+                        8 => .@"64",
+                        else => unreachable,
+                    },
+                    .vlmul = .m1,
+                    .vma = true,
+                    .vta = true,
+                });
+
+                try func.genCopy(ty, .{ .register = temp_reg }, .{ .load_frame = frame });
+
+                _ = try func.addInst(.{
+                    .tag = .pseudo,
+                    .ops = .pseudo_mv,
+                    .data = .{
+                        .rr = .{
+                            .rd = reg,
+                            .rs = temp_reg,
+                        },
+                    },
+                });
+
+                return;
+            }
+
             _ = try func.addInst(.{
                 .tag = .pseudo,
                 .ops = .pseudo_load_rm,
@@ -6195,7 +6355,7 @@ fn airCmpxchg(func: *Func, inst: Air.Inst.Index) !void {
 }
 
 fn airAtomicRmw(func: *Func, inst: Air.Inst.Index) !void {
-    const zcu = func.pt.zcu;
+    const pt = func.pt;
     const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
     const extra = func.air.extraData(Air.AtomicRmw, pl_op.payload).data;
 
@@ -6206,13 +6366,13 @@ fn airAtomicRmw(func: *Func, inst: Air.Inst.Index) !void {
     const ptr_mcv = try func.resolveInst(pl_op.operand);
 
     const val_ty = func.typeOf(extra.operand);
-    const val_size = val_ty.abiSize(func.pt);
+    const val_size = val_ty.abiSize(pt);
     const val_mcv = try func.resolveInst(extra.operand);
 
     if (!math.isPowerOfTwo(val_size))
         return func.fail("TODO: airAtomicRmw non-pow 2", .{});
 
-    switch (val_ty.zigTypeTag(zcu)) {
+    switch (val_ty.zigTypeTag(pt.zcu)) {
         .Int => {},
         inline .Bool, .Float, .Enum, .Pointer => |ty| return func.fail("TODO: airAtomicRmw {s}", .{@tagName(ty)}),
         else => unreachable,
@@ -6735,15 +6895,15 @@ fn getResolvedInstValue(func: *Func, inst: Air.Inst.Index) *InstTracking {
 }
 
 fn genTypedValue(func: *Func, val: Value) InnerError!MCValue {
-    const zcu = func.pt.zcu;
+    const pt = func.pt;
     const gpa = func.gpa;
 
-    const owner_decl_index = zcu.funcOwnerDeclIndex(func.func_index);
+    const owner_decl_index = pt.zcu.funcOwnerDeclIndex(func.func_index);
     const lf = func.bin_file;
     const src_loc = func.src_loc;
 
-    if (val.isUndef(zcu)) {
-        const local_sym_index = lf.lowerUnnamedConst(func.pt, val, owner_decl_index) catch |err| {
+    if (val.isUndef(pt.zcu)) {
+        const local_sym_index = lf.lowerUnnamedConst(pt, val, owner_decl_index) catch |err| {
             const msg = try ErrorMsg.create(gpa, src_loc, "lowering unnamed undefined constant failed: {s}", .{@errorName(err)});
             func.err_msg = msg;
             return error.CodegenFail;
@@ -6760,7 +6920,7 @@ fn genTypedValue(func: *Func, val: Value) InnerError!MCValue {
 
     const result = try codegen.genTypedValue(
         lf,
-        func.pt,
+        pt,
         src_loc,
         val,
         owner_decl_index,
src/arch/riscv64/encoder.zig
@@ -5,6 +5,7 @@ pub const Instruction = struct {
     pub const Operand = union(enum) {
         none,
         reg: Register,
+        csr: CSR,
         mem: Memory,
         imm: Immediate,
         barrier: Mir.Barrier,
@@ -58,6 +59,7 @@ pub const Instruction = struct {
                 .imm => |imm| try writer.print("{d}", .{imm.asSigned(64)}),
                 .mem => try writer.writeAll("mem"),
                 .barrier => |barrier| try writer.writeAll(@tagName(barrier)),
+                .csr => |csr| try writer.writeAll(@tagName(csr)),
             }
         }
     }
@@ -71,6 +73,7 @@ const bits = @import("bits.zig");
 const Encoding = @import("Encoding.zig");
 
 const Register = bits.Register;
+const CSR = bits.CSR;
 const Memory = bits.Memory;
 const Immediate = bits.Immediate;
 
src/arch/riscv64/Encoding.zig
@@ -11,6 +11,7 @@ const OpCode = enum(u7) {
     STORE = 0b0100011,
     STORE_FP = 0b0100111,
     AMO = 0b0101111,
+    OP_V = 0b1010111,
     OP = 0b0110011,
     OP_32 = 0b0111011,
     LUI = 0b0110111,
@@ -83,9 +84,51 @@ const Enc = struct {
             funct3: u3,
             has_5: bool,
         },
+        vecls: struct {
+            width: VecWidth,
+            umop: Umop,
+            vm: bool,
+            mop: Mop,
+            mew: bool,
+            nf: u3,
+        },
+        vecmath: struct {
+            vm: bool,
+            funct6: u6,
+            funct3: VecType,
+        },
         /// U-type
         none,
     },
+
+    const Mop = enum(u2) {
+        unit = 0b00,
+        unord = 0b01,
+        stride = 0b10,
+        ord = 0b11,
+    };
+
+    const Umop = enum(u5) {
+        unit = 0b00000,
+        whole = 0b01000,
+        mask = 0b01011,
+        fault = 0b10000,
+    };
+
+    const VecWidth = enum(u3) {
+        @"32" = 0b110,
+        @"64" = 0b111,
+    };
+
+    const VecType = enum(u3) {
+        OPIVV = 0b000,
+        OPFVV = 0b001,
+        OPMVV = 0b010,
+        OPIVI = 0b011,
+        OPIVX = 0b100,
+        OPFVF = 0b101,
+        OPMVX = 0b110,
+    };
 };
 
 pub const Mnemonic = enum {
@@ -115,6 +158,9 @@ pub const Mnemonic = enum {
     addi,
     jalr,
 
+    vsetivli,
+    vsetvli,
+
     // U Type
     lui,
     auipc,
@@ -155,6 +201,8 @@ pub const Mnemonic = enum {
     ebreak,
     unimp,
 
+    csrrs,
+
     // M extension
     mul,
     mulw,
@@ -217,6 +265,17 @@ pub const Mnemonic = enum {
     fsgnjnd,
     fsgnjxd,
 
+    // V Extension
+    vle32v,
+    vle64v,
+
+    vse32v,
+    vse64v,
+
+    vaddvv,
+    vadcxv,
+    vadcvx,
+
     // MISC
     fence,
     fencetso,
@@ -373,6 +432,9 @@ pub const Mnemonic = enum {
 
             .flw     => .{ .opcode = .LOAD_FP, .data = .{ .f = .{ .funct3 = 0b010 } } },
             .fld     => .{ .opcode = .LOAD_FP, .data = .{ .f = .{ .funct3 = 0b011 } } },
+
+            .vle32v  => .{ .opcode = .LOAD_FP, .data = .{ .vecls = .{ .width = .@"32", .umop = .unit, .vm = true, .mop = .unit, .mew = true, .nf = 0b000 } } },
+            .vle64v  => .{ .opcode = .LOAD_FP, .data = .{ .vecls = .{ .width = .@"64", .umop = .unit, .vm = true, .mop = .unit, .mew = true, .nf = 0b000 } } },
             
 
             // STORE_FP
@@ -380,6 +442,8 @@ pub const Mnemonic = enum {
             .fsw     => .{ .opcode = .STORE_FP, .data = .{ .f = .{ .funct3 = 0b010 } } },
             .fsd     => .{ .opcode = .STORE_FP, .data = .{ .f = .{ .funct3 = 0b011 } } },
 
+            .vse32v  => .{ .opcode = .STORE_FP, .data = .{ .vecls = .{ .width = .@"32", .umop = .unit, .vm = true, .mop = .unit, .mew = true, .nf = 0b000 } } },
+            .vse64v  => .{ .opcode = .STORE_FP, .data = .{ .vecls = .{ .width = .@"64", .umop = .unit, .vm = true, .mop = .unit, .mew = true, .nf = 0b000 } } },
 
             // JALR
 
@@ -410,6 +474,8 @@ pub const Mnemonic = enum {
 
             .ecall   => .{ .opcode = .SYSTEM, .data = .{ .f = .{ .funct3 = 0b000 } } },
             .ebreak  => .{ .opcode = .SYSTEM, .data = .{ .f = .{ .funct3 = 0b000 } } },
+
+            .csrrs   => .{ .opcode = .SYSTEM, .data = .{ .f = .{ .funct3 = 0b010 } } },
            
 
             // NONE
@@ -449,7 +515,13 @@ pub const Mnemonic = enum {
             .amominud  => .{ .opcode = .AMO, .data = .{ .amo = .{ .width = .D, .funct5 = 0b11000 } } },
             .amomaxud  => .{ .opcode = .AMO, .data = .{ .amo = .{ .width = .D, .funct5 = 0b11100 } } },
 
-                    
+            // OP_V
+            .vsetivli  => .{ .opcode = .OP_V, .data = .{ .f = .{ .funct3 = 0b111 } } },
+            .vsetvli   => .{ .opcode = .OP_V, .data = .{ .f = .{ .funct3 = 0b111 } } },
+            .vaddvv    => .{ .opcode = .OP_V, .data = .{ .vecmath = .{ .vm = true, .funct6 = 0b000000, .funct3 = .OPIVV } } },
+            .vadcxv    => .{ .opcode = .OP_V, .data = .{ .vecmath = .{ .vm = true,  .funct6 = 0b010000, .funct3 = .OPMVX } } },
+            .vadcvx    => .{ .opcode = .OP_V, .data = .{ .vecmath = .{ .vm = true,  .funct6 = 0b010000, .funct3 = .OPMVV } } },
+            
             // zig fmt: on
         };
     }
@@ -465,7 +537,6 @@ pub const InstEnc = enum {
     J,
     fence,
     amo,
-    /// extras that have unusual op counts
     system,
 
     pub fn fromMnemonic(mnem: Mnemonic) InstEnc {
@@ -494,6 +565,10 @@ pub const InstEnc = enum {
 
             .flw,
             .fld,
+
+            .csrrs,
+            .vsetivli,
+            .vsetvli,
             => .I,
 
             .lui,
@@ -587,6 +662,14 @@ pub const InstEnc = enum {
 
             .fsgnjxs,
             .fsgnjxd,
+
+            .vle32v,
+            .vle64v,
+            .vse32v,
+            .vse64v,
+            .vaddvv,
+            .vadcxv,
+            .vadcvx,
             => .R,
 
             .ecall,
@@ -757,6 +840,25 @@ pub const Data = union(InstEnc) {
                     },
                 };
             },
+            .csrrs => {
+                assert(ops.len == 3);
+
+                const csr = ops[0].csr;
+                const rs1 = ops[1].reg;
+                const rd = ops[2].reg;
+
+                return .{
+                    .I = .{
+                        .rd = rd.encodeId(),
+                        .rs1 = rs1.encodeId(),
+
+                        .imm0_11 = @intFromEnum(csr),
+
+                        .opcode = @intFromEnum(enc.opcode),
+                        .funct3 = enc.data.f.funct3,
+                    },
+                };
+            },
             else => {},
         }
 
@@ -783,6 +885,25 @@ pub const Data = union(InstEnc) {
                             .funct3 = fmt.rm,
                             .funct7 = (@as(u7, fmt.funct5) << 2) | @intFromEnum(fmt.fmt),
                         },
+                        .vecls => |vec| .{
+                            .rd = ops[0].reg.encodeId(),
+                            .rs1 = ops[1].reg.encodeId(),
+
+                            .rs2 = @intFromEnum(vec.umop),
+
+                            .opcode = @intFromEnum(enc.opcode),
+                            .funct3 = @intFromEnum(vec.width),
+                            .funct7 = (@as(u7, vec.nf) << 4) | (@as(u7, @intFromBool(vec.mew)) << 3) | (@as(u7, @intFromEnum(vec.mop)) << 1) | @intFromBool(vec.vm),
+                        },
+                        .vecmath => |vec| .{
+                            .rd = ops[0].reg.encodeId(),
+                            .rs1 = ops[1].reg.encodeId(),
+                            .rs2 = ops[2].reg.encodeId(),
+
+                            .opcode = @intFromEnum(enc.opcode),
+                            .funct3 = @intFromEnum(vec.funct3),
+                            .funct7 = (@as(u7, vec.funct6) << 1) | @intFromBool(vec.vm),
+                        },
                         else => unreachable,
                     },
                 };
@@ -897,21 +1018,21 @@ pub const Data = union(InstEnc) {
             .amo => {
                 assert(ops.len == 5);
 
-                const rd = ops[0];
-                const rs1 = ops[1];
-                const rs2 = ops[2];
-                const rl = ops[3];
-                const aq = ops[4];
+                const rd = ops[0].reg;
+                const rs1 = ops[1].reg;
+                const rs2 = ops[2].reg;
+                const rl = ops[3].barrier;
+                const aq = ops[4].barrier;
 
                 return .{
                     .amo = .{
-                        .rd = rd.reg.encodeId(),
-                        .rs1 = rs1.reg.encodeId(),
-                        .rs2 = rs2.reg.encodeId(),
+                        .rd = rd.encodeId(),
+                        .rs1 = rs1.encodeId(),
+                        .rs2 = rs2.encodeId(),
 
                         // TODO: https://github.com/ziglang/zig/issues/20113
-                        .rl = if (rl.barrier == .rl) true else false,
-                        .aq = if (aq.barrier == .aq) true else false,
+                        .rl = if (rl == .rl) true else false,
+                        .aq = if (aq == .aq) true else false,
 
                         .opcode = @intFromEnum(enc.opcode),
                         .funct3 = @intFromEnum(enc.data.amo.width),
@@ -919,7 +1040,6 @@ pub const Data = union(InstEnc) {
                     },
                 };
             },
-
             else => std.debug.panic("TODO: construct {s}", .{@tagName(inst_enc)}),
         }
     }
src/arch/riscv64/Lower.zig
@@ -80,58 +80,99 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index, options: struct {
                     .pseudo_load_rm => {
                         const dest_reg = rm.r;
                         const dest_reg_class = dest_reg.class();
-                        const float = dest_reg_class == .float;
 
                         const src_size = rm.m.mod.size;
                         const unsigned = rm.m.mod.unsigned;
 
-                        const tag: Encoding.Mnemonic = if (!float)
-                            switch (src_size) {
+                        const tag: Encoding.Mnemonic = switch (dest_reg_class) {
+                            .int => switch (src_size) {
                                 .byte => if (unsigned) .lbu else .lb,
                                 .hword => if (unsigned) .lhu else .lh,
                                 .word => if (unsigned) .lwu else .lw,
                                 .dword => .ld,
-                            }
-                        else switch (src_size) {
-                            .byte => unreachable, // Zig does not support 8-bit floats
-                            .hword => return lower.fail("TODO: lowerMir pseudo_load_rm support 16-bit floats", .{}),
-                            .word => .flw,
-                            .dword => .fld,
+                            },
+                            .float => switch (src_size) {
+                                .byte => unreachable, // Zig does not support 8-bit floats
+                                .hword => return lower.fail("TODO: lowerMir pseudo_load_rm support 16-bit floats", .{}),
+                                .word => .flw,
+                                .dword => .fld,
+                            },
+                            .vector => switch (src_size) {
+                                .byte,
+                                .hword,
+                                => return lower.fail(
+                                    "TODO: lowerMir pseudo_load_rm support {s} vector",
+                                    .{@tagName(src_size)},
+                                ),
+                                .word => .vle32v,
+                                .dword => .vle64v,
+                            },
                         };
 
-                        try lower.emit(tag, &.{
-                            .{ .reg = rm.r },
-                            .{ .reg = frame_loc.base },
-                            .{ .imm = Immediate.s(frame_loc.disp) },
-                        });
+                        switch (dest_reg_class) {
+                            .int, .float => {
+                                try lower.emit(tag, &.{
+                                    .{ .reg = rm.r },
+                                    .{ .reg = frame_loc.base },
+                                    .{ .imm = Immediate.s(frame_loc.disp) },
+                                });
+                            },
+                            .vector => {
+                                try lower.emit(tag, &.{
+                                    .{ .reg = rm.r },
+                                    .{ .reg = frame_loc.base },
+                                    .{ .reg = .x0 },
+                                });
+                            },
+                        }
                     },
                     .pseudo_store_rm => {
                         const src_reg = rm.r;
                         const src_reg_class = src_reg.class();
-                        const float = src_reg_class == .float;
 
-                        // TODO: do we actually need this? are all stores not usize?
                         const dest_size = rm.m.mod.size;
 
-                        const tag: Encoding.Mnemonic = if (!float)
-                            switch (dest_size) {
+                        const tag: Encoding.Mnemonic = switch (src_reg_class) {
+                            .int => switch (dest_size) {
                                 .byte => .sb,
                                 .hword => .sh,
                                 .word => .sw,
                                 .dword => .sd,
-                            }
-                        else switch (dest_size) {
-                            .byte => unreachable, // Zig does not support 8-bit floats
-                            .hword => return lower.fail("TODO: lowerMir pseudo_load_rm support 16-bit floats", .{}),
-                            .word => .fsw,
-                            .dword => .fsd,
+                            },
+                            .float => switch (dest_size) {
+                                .byte => unreachable, // Zig does not support 8-bit floats
+                                .hword => return lower.fail("TODO: lowerMir pseudo_store_rm support 16-bit floats", .{}),
+                                .word => .fsw,
+                                .dword => .fsd,
+                            },
+                            .vector => switch (dest_size) {
+                                .byte,
+                                .hword,
+                                => return lower.fail(
+                                    "TODO: lowerMir pseudo_load_rm support {s} vector",
+                                    .{@tagName(dest_size)},
+                                ),
+                                .word => .vse32v,
+                                .dword => .vse64v,
+                            },
                         };
 
-                        try lower.emit(tag, &.{
-                            .{ .reg = frame_loc.base },
-                            .{ .reg = rm.r },
-                            .{ .imm = Immediate.s(frame_loc.disp) },
-                        });
+                        switch (src_reg_class) {
+                            .int, .float => {
+                                try lower.emit(tag, &.{
+                                    .{ .reg = frame_loc.base },
+                                    .{ .reg = rm.r },
+                                    .{ .imm = Immediate.s(frame_loc.disp) },
+                                });
+                            },
+                            .vector => {
+                                try lower.emit(tag, &.{
+                                    .{ .reg = frame_loc.base },
+                                    .{ .reg = rm.r },
+                                    .{ .reg = .x0 },
+                                });
+                            },
+                        }
                     },
                     else => unreachable,
                 }
@@ -143,34 +184,47 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index, options: struct {
                 const dst_class = rr.rd.class();
                 const src_class = rr.rs.class();
 
-                assert(dst_class == src_class);
-
-                switch (dst_class) {
-                    .float => {
-                        try lower.emit(if (lower.hasFeature(.d)) .fsgnjnd else .fsgnjns, &.{
-                            .{ .reg = rr.rd },
-                            .{ .reg = rr.rs },
-                            .{ .reg = rr.rs },
-                        });
+                switch (src_class) {
+                    .float => switch (dst_class) {
+                        .float => {
+                            try lower.emit(if (lower.hasFeature(.d)) .fsgnjnd else .fsgnjns, &.{
+                                .{ .reg = rr.rd },
+                                .{ .reg = rr.rs },
+                                .{ .reg = rr.rs },
+                            });
+                        },
+                        .int, .vector => return lower.fail("TODO: lowerMir pseudo_mv float -> {s}", .{@tagName(dst_class)}),
+                    },
+                    .int => switch (dst_class) {
+                        .int => {
+                            try lower.emit(.addi, &.{
+                                .{ .reg = rr.rd },
+                                .{ .reg = rr.rs },
+                                .{ .imm = Immediate.s(0) },
+                            });
+                        },
+                        .vector => {
+                            try lower.emit(.vadcxv, &.{
+                                .{ .reg = rr.rd },
+                                .{ .reg = rr.rs },
+                                .{ .reg = .zero },
+                            });
+                        },
+                        .float => return lower.fail("TODO: lowerMir pseudo_mv int -> {s}", .{@tagName(dst_class)}),
                     },
-                    .int => {
-                        try lower.emit(.addi, &.{
-                            .{ .reg = rr.rd },
-                            .{ .reg = rr.rs },
-                            .{ .imm = Immediate.s(0) },
-                        });
+                    .vector => switch (dst_class) {
+                        .int => {
+                            try lower.emit(.vadcvx, &.{
+                                .{ .reg = rr.rd },
+                                .{ .reg = rr.rs },
+                                .{ .reg = .zero },
+                            });
+                        },
+                        .float, .vector => return lower.fail("TODO: lowerMir pseudo_mv vector -> {s}", .{@tagName(dst_class)}),
                     },
                 }
             },
 
-            .pseudo_ret => {
-                try lower.emit(.jalr, &.{
-                    .{ .reg = .zero },
-                    .{ .reg = .ra },
-                    .{ .imm = Immediate.s(0) },
-                });
-            },
-
             .pseudo_j => {
                 try lower.emit(.jal, &.{
                     .{ .reg = .zero },
@@ -209,7 +263,10 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index, options: struct {
                 const rm = inst.data.rm;
                 assert(rm.r.class() == .int);
 
-                const frame = rm.m.toFrameLoc(lower.mir);
+                const frame: Mir.FrameLoc = if (options.allow_frame_locs)
+                    rm.m.toFrameLoc(lower.mir)
+                else
+                    .{ .base = .s0, .disp = 0 };
 
                 try lower.emit(.addi, &.{
                     .{ .reg = rm.r },
@@ -376,6 +433,7 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index, options: struct {
                             });
                         },
                     },
+                    .vector => return lower.fail("TODO: lowerMir pseudo_cmp vector", .{}),
                 }
             },
 
@@ -497,6 +555,11 @@ fn generic(lower: *Lower, inst: Mir.Inst) Error!void {
             .{ .reg = inst.data.r_type.rs1 },
             .{ .reg = inst.data.r_type.rs2 },
         },
+        .csr => &.{
+            .{ .csr = inst.data.csr.csr },
+            .{ .reg = inst.data.csr.rs1 },
+            .{ .reg = inst.data.csr.rd },
+        },
         else => return lower.fail("TODO: generic lower ops {s}", .{@tagName(inst.ops)}),
     });
 }
@@ -523,17 +586,22 @@ fn pushPopRegList(lower: *Lower, comptime spilling: bool, reg_list: Mir.Register
     while (it.next()) |i| {
         const frame = lower.mir.frame_locs.get(@intFromEnum(bits.FrameIndex.spill_frame));
         const reg = abi.Registers.all_preserved[i];
+
         const reg_class = reg.class();
-        const is_float_reg = reg_class == .float;
+        const load_inst: Encoding.Mnemonic, const store_inst: Encoding.Mnemonic = switch (reg_class) {
+            .int => .{ .ld, .sd },
+            .float => .{ .fld, .fsd },
+            .vector => unreachable,
+        };
 
         if (spilling) {
-            try lower.emit(if (is_float_reg) .fsd else .sd, &.{
+            try lower.emit(store_inst, &.{
                 .{ .reg = frame.base },
                 .{ .reg = abi.Registers.all_preserved[i] },
                 .{ .imm = Immediate.s(frame.disp + reg_i) },
             });
         } else {
-            try lower.emit(if (is_float_reg) .fld else .ld, &.{
+            try lower.emit(load_inst, &.{
                 .{ .reg = abi.Registers.all_preserved[i] },
                 .{ .reg = frame.base },
                 .{ .imm = Immediate.s(frame.disp + reg_i) },
src/arch/riscv64/Mir.zig
@@ -134,7 +134,16 @@ pub const Inst = struct {
         fltd,
         fled,
 
-        /// A Extension Instructions
+        // Zicsr Extension Instructions
+        csrrs,
+
+        // V Extension Instructions
+        vsetvli,
+        vsetivli,
+        vsetvl,
+        vaddvv,
+
+        // A Extension Instructions
         amo,
 
         /// A pseudo-instruction. Used for anything that isn't 1:1 with an
@@ -146,91 +155,57 @@ pub const Inst = struct {
     /// this union. `Ops` determines which union field is active, as well as
     /// how to interpret the data within.
     pub const Data = union {
-        /// No additional data
-        ///
-        /// Used by e.g. ebreak
         nop: void,
-        /// Another instruction.
-        ///
-        /// Used by e.g. b
         inst: Index,
-        /// Index into `extra`. Meaning of what can be found there is context-dependent.
-        ///
-        /// Used by e.g. load_memory
         payload: u32,
-
         r_type: struct {
             rd: Register,
             rs1: Register,
             rs2: Register,
         },
-
         i_type: struct {
             rd: Register,
             rs1: Register,
             imm12: Immediate,
         },
-
         s_type: struct {
             rs1: Register,
             rs2: Register,
             imm5: Immediate,
             imm7: Immediate,
         },
-
         b_type: struct {
             rs1: Register,
             rs2: Register,
             inst: Inst.Index,
         },
-
         u_type: struct {
             rd: Register,
             imm20: Immediate,
         },
-
         j_type: struct {
             rd: Register,
             inst: Inst.Index,
         },
-
-        /// Debug info: line and column
-        ///
-        /// Used by e.g. pseudo_dbg_line
         pseudo_dbg_line_column: struct {
             line: u32,
             column: u32,
         },
-
-        // Custom types to be lowered
-
-        /// Register + Memory
         rm: struct {
             r: Register,
             m: Memory,
         },
-
         reg_list: Mir.RegisterList,
-
-        /// A register
-        ///
-        /// Used by e.g. blr
         reg: Register,
-
-        /// Two registers
-        ///
-        /// Used by e.g. mv
         rr: struct {
             rd: Register,
             rs: Register,
         },
-
         fabs: struct {
             rd: Register,
             rs: Register,
             bits: u16,
         },
-
         compare: struct {
             rd: Register,
             rs1: Register,
@@ -245,12 +220,10 @@ pub const Inst = struct {
             },
             ty: Type,
         },
-
         reloc: struct {
             atom_index: u32,
             sym_index: u32,
         },
-
         fence: struct {
             pred: Barrier,
             succ: Barrier,
@@ -259,7 +232,6 @@ pub const Inst = struct {
                 tso,
             },
         },
-
         amo: struct {
             rd: Register,
             rs1: Register,
@@ -269,6 +241,11 @@ pub const Inst = struct {
             op: AmoOp,
             ty: Type,
         },
+        csr: struct {
+            csr: CSR,
+            rs1: Register,
+            rd: Register,
+        },
     };
 
     pub const Ops = enum {
@@ -293,6 +270,9 @@ pub const Inst = struct {
         /// Another instruction.
         inst,
 
+        /// Control and Status Register Instruction.
+        csr,
+
         /// Pseudo-instruction that will generate a backpatched
         /// function prologue.
         pseudo_prologue,
@@ -321,11 +301,6 @@ pub const Inst = struct {
         /// Uses `rm` payload.
         pseudo_lea_rm,
 
-        /// Shorthand for returning, aka jumping to ra register.
-        ///
-        /// Uses nop payload.
-        pseudo_ret,
-
         /// Jumps. Uses `inst` payload.
         pseudo_j,
 
@@ -363,14 +338,6 @@ pub const Inst = struct {
         pseudo_amo,
     };
 
-    // Make sure we don't accidentally make instructions bigger than expected.
-    // Note that in Debug builds, Zig is allowed to insert a secret field for safety checks.
-    // comptime {
-    //     if (builtin.mode != .Debug) {
-    //         assert(@sizeOf(Inst) == 8);
-    //     }
-    // }
-
     pub fn format(
         inst: Inst,
         comptime fmt: []const u8,
@@ -490,6 +457,7 @@ const assert = std.debug.assert;
 
 const bits = @import("bits.zig");
 const Register = bits.Register;
+const CSR = bits.CSR;
 const Immediate = bits.Immediate;
 const Memory = bits.Memory;
 const FrameIndex = bits.FrameIndex;
test/tests.zig
@@ -436,11 +436,12 @@ const test_targets = blk: {
         //},
 
         .{
-            .target = .{
-                .cpu_arch = .riscv64,
-                .os_tag = .linux,
-                .abi = .musl,
-            },
+            .target = std.Target.Query.parse(
+                .{
+                    .arch_os_abi = "riscv64-linux-musl",
+                    .cpu_features = "baseline+v",
+                },
+            ) catch @panic("OOM"),
             .use_llvm = false,
             .use_lld = false,
         },