authorgravatar for david@vortan.devDavid Rubin <david@vortan.dev> 2024-06-02 19:25:53-07:00
committergravatar for david@vortan.devDavid Rubin <david@vortan.dev> 2024-07-14 23:02:34-07:00
log571aa694fd150b9f76d78a6ac96c25163f46091a
tree46944915fe868cfb566fab2304c6f88a9995d6e1
parentf2301ba896bd522483e987ecebe72bdcd86705ba
signaturelock-open Commit is signed but in an unrecognized format.

riscv: vectors part 1


8 files changed, 551 insertions(+), 169 deletions(-)

src/arch/riscv64/CodeGen.zig+201-41
...@@ -37,6 +37,7 @@ const abi = @import("abi.zig");...@@ -37,6 +37,7 @@ const abi = @import("abi.zig");
37const Lower = @import("Lower.zig");37const Lower = @import("Lower.zig");
3838
39const Register = bits.Register;39const Register = bits.Register;
40const CSR = bits.CSR;
40const Immediate = bits.Immediate;41const Immediate = bits.Immediate;
41const Memory = bits.Memory;42const Memory = bits.Memory;
42const FrameIndex = bits.FrameIndex;43const FrameIndex = bits.FrameIndex;
...@@ -87,6 +88,9 @@ exitlude_jump_relocs: std.ArrayListUnmanaged(usize) = .{},...@@ -87,6 +88,9 @@ exitlude_jump_relocs: std.ArrayListUnmanaged(usize) = .{},
87/// across each runtime branch upon joining.88/// across each runtime branch upon joining.
88branch_stack: *std.ArrayList(Branch),89branch_stack: *std.ArrayList(Branch),
8990
91// The current bit length of vector registers.
92vec_len: u32,
93
90// Key is the block instruction94// Key is the block instruction
91blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{},95blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{},
92register_manager: RegisterManager = .{},96register_manager: RegisterManager = .{},
...@@ -747,6 +751,7 @@ pub fn generate(...@@ -747,6 +751,7 @@ pub fn generate(
747 .end_di_line = func.rbrace_line,751 .end_di_line = func.rbrace_line,
748 .end_di_column = func.rbrace_column,752 .end_di_column = func.rbrace_column,
749 .scope_generation = 0,753 .scope_generation = 0,
754 .vec_len = 16 * 8, // TODO: set this per cpu
750 };755 };
751 defer {756 defer {
752 function.frame_allocs.deinit(gpa);757 function.frame_allocs.deinit(gpa);
...@@ -1040,10 +1045,60 @@ pub fn addExtraAssumeCapacity(func: *Func, extra: anytype) u32 {...@@ -1040,10 +1045,60 @@ pub fn addExtraAssumeCapacity(func: *Func, extra: anytype) u32 {
1040 return result;1045 return result;
1041}1046}
10421047
1048/// Returns a temporary register that contains the value of the `reg` csr.
1049///
1050/// Caller's duty to lock the return register is needed.
1051fn getCsr(func: *Func, csr: CSR) !Register {
1052 assert(func.hasFeature(.zicsr));
1053 const dst_reg = try func.register_manager.allocReg(null, func.regTempClassForType(Type.usize));
1054 _ = try func.addInst(.{
1055 .tag = .csrrs,
1056 .ops = .csr,
1057 .data = .{
1058 .csr = .{
1059 .csr = csr,
1060 .rd = dst_reg,
1061 .rs1 = .x0,
1062 },
1063 },
1064 });
1065 return dst_reg;
1066}
1067
1068fn setVl(func: *Func, dst_reg: Register, avl: u5, options: bits.VType) !void {
1069 if (avl == 0) {
1070 const options_int: u12 = @as(u12, 0) | @as(u8, @bitCast(options));
1071 _ = try func.addInst(.{
1072 .tag = .vsetvli,
1073 .ops = .rri,
1074 .data = .{ .i_type = .{
1075 .rd = dst_reg,
1076 .rs1 = .zero,
1077 .imm12 = Immediate.u(options_int),
1078 } },
1079 });
1080 } else {
1081 const options_int: u12 = (~@as(u12, 0) << 10) | @as(u8, @bitCast(options));
1082 _ = try func.addInst(.{
1083 .tag = .vsetivli,
1084 .ops = .rri,
1085 .data = .{
1086 .i_type = .{
1087 .rd = dst_reg,
1088 .rs1 = @enumFromInt(avl),
1089 .imm12 = Immediate.u(options_int),
1090 },
1091 },
1092 });
1093 }
1094}
1095
1043const required_features = [_]Target.riscv.Feature{1096const required_features = [_]Target.riscv.Feature{
1044 .d,1097 .d,
1045 .m,1098 .m,
1046 .a,1099 .a,
1100 .zicsr,
1101 .v,
1047};1102};
10481103
1049fn gen(func: *Func) !void {1104fn gen(func: *Func) !void {
...@@ -1101,7 +1156,19 @@ fn gen(func: *Func) !void {...@@ -1101,7 +1156,19 @@ fn gen(func: *Func) !void {
1101 const backpatch_ra_restore = try func.addPseudo(.pseudo_dead);1156 const backpatch_ra_restore = try func.addPseudo(.pseudo_dead);
1102 const backpatch_fp_restore = try func.addPseudo(.pseudo_dead);1157 const backpatch_fp_restore = try func.addPseudo(.pseudo_dead);
1103 const backpatch_stack_alloc_restore = try func.addPseudo(.pseudo_dead);1158 const backpatch_stack_alloc_restore = try func.addPseudo(.pseudo_dead);
1104 try func.addPseudoNone(.pseudo_ret);1159
1160 // ret
1161 _ = try func.addInst(.{
1162 .tag = .jalr,
1163 .ops = .rri,
1164 .data = .{
1165 .i_type = .{
1166 .rd = .zero,
1167 .rs1 = .ra,
1168 .imm12 = Immediate.s(0),
1169 },
1170 },
1171 });
11051172
1106 const frame_layout = try func.computeFrameLayout();1173 const frame_layout = try func.computeFrameLayout();
1107 const need_save_reg = frame_layout.save_reg_list.count() > 0;1174 const need_save_reg = frame_layout.save_reg_list.count() > 0;
...@@ -1842,8 +1909,8 @@ fn typeRegClass(func: *Func, ty: Type) abi.RegisterClass {...@@ -1842,8 +1909,8 @@ fn typeRegClass(func: *Func, ty: Type) abi.RegisterClass {
1842 const zcu = pt.zcu;1909 const zcu = pt.zcu;
1843 return switch (ty.zigTypeTag(zcu)) {1910 return switch (ty.zigTypeTag(zcu)) {
1844 .Float => .float,1911 .Float => .float,
1845 .Vector => @panic("TODO: typeRegClass for Vectors"),1912 .Vector => .vector,
1846 inline else => .int,1913 else => .int,
1847 };1914 };
1848}1915}
18491916
...@@ -1852,7 +1919,7 @@ fn regGeneralClassForType(func: *Func, ty: Type) RegisterManager.RegisterBitSet...@@ -1852,7 +1919,7 @@ fn regGeneralClassForType(func: *Func, ty: Type) RegisterManager.RegisterBitSet
1852 const zcu = pt.zcu;1919 const zcu = pt.zcu;
1853 return switch (ty.zigTypeTag(zcu)) {1920 return switch (ty.zigTypeTag(zcu)) {
1854 .Float => abi.Registers.Float.general_purpose,1921 .Float => abi.Registers.Float.general_purpose,
1855 .Vector => @panic("TODO: regGeneralClassForType for Vectors"),1922 .Vector => abi.Registers.Vector.general_purpose,
1856 else => abi.Registers.Integer.general_purpose,1923 else => abi.Registers.Integer.general_purpose,
1857 };1924 };
1858}1925}
...@@ -1862,7 +1929,7 @@ fn regTempClassForType(func: *Func, ty: Type) RegisterManager.RegisterBitSet {...@@ -1862,7 +1929,7 @@ fn regTempClassForType(func: *Func, ty: Type) RegisterManager.RegisterBitSet {
1862 const zcu = pt.zcu;1929 const zcu = pt.zcu;
1863 return switch (ty.zigTypeTag(zcu)) {1930 return switch (ty.zigTypeTag(zcu)) {
1864 .Float => abi.Registers.Float.temporary,1931 .Float => abi.Registers.Float.temporary,
1865 .Vector => @panic("TODO: regTempClassForType for Vectors"),1932 .Vector => abi.Registers.Vector.general_purpose, // there are no temporary vector registers
1866 else => abi.Registers.Integer.temporary,1933 else => abi.Registers.Integer.temporary,
1867 };1934 };
1868}1935}
...@@ -1870,20 +1937,19 @@ fn regTempClassForType(func: *Func, ty: Type) RegisterManager.RegisterBitSet {...@@ -1870,20 +1937,19 @@ fn regTempClassForType(func: *Func, ty: Type) RegisterManager.RegisterBitSet {
1870fn allocRegOrMem(func: *Func, elem_ty: Type, inst: ?Air.Inst.Index, reg_ok: bool) !MCValue {1937fn allocRegOrMem(func: *Func, elem_ty: Type, inst: ?Air.Inst.Index, reg_ok: bool) !MCValue {
1871 const pt = func.pt;1938 const pt = func.pt;
18721939
1873 const abi_size = math.cast(u32, elem_ty.abiSize(pt)) orelse {1940 const bit_size = elem_ty.bitSize(pt);
1874 return func.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(pt)});1941 const min_size: u64 = switch (elem_ty.zigTypeTag(pt.zcu)) {
1942 .Float => if (func.hasFeature(.d)) 64 else 32,
1943 .Vector => func.vec_len,
1944 else => 64,
1875 };1945 };
18761946
1877 const min_size: u32 = switch (elem_ty.zigTypeTag(pt.zcu)) {1947 if (reg_ok and bit_size <= min_size) {
1878 .Float => 4,
1879 .Vector => @panic("allocRegOrMem Vector"),
1880 else => 8,
1881 };
1882
1883 if (reg_ok and abi_size <= min_size) {
1884 if (func.register_manager.tryAllocReg(inst, func.regGeneralClassForType(elem_ty))) |reg| {1948 if (func.register_manager.tryAllocReg(inst, func.regGeneralClassForType(elem_ty))) |reg| {
1885 return .{ .register = reg };1949 return .{ .register = reg };
1886 }1950 }
1951 } else if (reg_ok and elem_ty.zigTypeTag(pt.zcu) == .Vector) {
1952 return func.fail("did you forget to extend vector registers before allocating", .{});
1887 }1953 }
18881954
1889 const frame_index = try func.allocFrameIndex(FrameAlloc.initSpill(elem_ty, pt));1955 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...@@ -1896,10 +1962,13 @@ fn allocRegOrMem(func: *Func, elem_ty: Type, inst: ?Air.Inst.Index, reg_ok: bool
1896fn allocReg(func: *Func, reg_class: abi.RegisterClass) !struct { Register, RegisterLock } {1962fn allocReg(func: *Func, reg_class: abi.RegisterClass) !struct { Register, RegisterLock } {
1897 if (reg_class == .float and !func.hasFeature(.f))1963 if (reg_class == .float and !func.hasFeature(.f))
1898 std.debug.panic("allocReg class == float where F isn't enabled", .{});1964 std.debug.panic("allocReg class == float where F isn't enabled", .{});
1965 if (reg_class == .vector and !func.hasFeature(.v))
1966 std.debug.panic("allocReg class == vector where V isn't enabled", .{});
18991967
1900 const class = switch (reg_class) {1968 const class = switch (reg_class) {
1901 .int => abi.Registers.Integer.general_purpose,1969 .int => abi.Registers.Integer.general_purpose,
1902 .float => abi.Registers.Float.general_purpose,1970 .float => abi.Registers.Float.general_purpose,
1971 .vector => abi.Registers.Vector.general_purpose,
1903 };1972 };
19041973
1905 const reg = try func.register_manager.allocReg(null, class);1974 const reg = try func.register_manager.allocReg(null, class);
...@@ -1915,7 +1984,8 @@ fn promoteReg(func: *Func, ty: Type, operand: MCValue) !struct { Register, ?Regi...@@ -1915,7 +1984,8 @@ fn promoteReg(func: *Func, ty: Type, operand: MCValue) !struct { Register, ?Regi
1915 return .{ op_reg, func.register_manager.lockReg(operand.register) };1984 return .{ op_reg, func.register_manager.lockReg(operand.register) };
1916 }1985 }
19171986
1918 const reg, const lock = try func.allocReg(func.typeRegClass(ty));1987 const class = func.typeRegClass(ty);
1988 const reg, const lock = try func.allocReg(class);
1919 try func.genSetReg(ty, reg, operand);1989 try func.genSetReg(ty, reg, operand);
1920 return .{ reg, lock };1990 return .{ reg, lock };
1921}1991}
...@@ -2372,6 +2442,42 @@ fn genBinOp(...@@ -2372,6 +2442,42 @@ fn genBinOp(
2372 },2442 },
2373 });2443 });
2374 },2444 },
2445 .Vector => {
2446 const mir_tag: Mir.Inst.Tag = switch (tag) {
2447 .add => .vaddvv,
2448 else => return func.fail("TODO: genBinOp {s} Vector", .{@tagName(tag)}),
2449 };
2450
2451 const num_elem: u5 = math.cast(u5, lhs_ty.vectorLen(zcu)) orelse {
2452 return func.fail("TODO: genBinOp use vsetvli for larger avl sizes", .{});
2453 };
2454 const elem_size = lhs_ty.childType(zcu).bitSize(pt);
2455
2456 try func.setVl(.zero, num_elem, .{
2457 .vlmul = .mf2,
2458 .vsew = switch (elem_size) {
2459 8 => .@"8",
2460 16 => .@"16",
2461 32 => .@"32",
2462 64 => .@"64",
2463 else => unreachable,
2464 },
2465 .vma = true,
2466 .vta = true,
2467 });
2468
2469 _ = try func.addInst(.{
2470 .tag = mir_tag,
2471 .ops = .rrr,
2472 .data = .{
2473 .r_type = .{
2474 .rd = dst_reg,
2475 .rs1 = lhs_reg,
2476 .rs2 = rhs_reg,
2477 },
2478 },
2479 });
2480 },
2375 else => unreachable,2481 else => unreachable,
2376 }2482 }
2377 },2483 },
...@@ -3560,13 +3666,12 @@ fn airSetUnionTag(func: *Func, inst: Air.Inst.Index) !void {...@@ -3560,13 +3666,12 @@ fn airSetUnionTag(func: *Func, inst: Air.Inst.Index) !void {
3560}3666}
35613667
3562fn airGetUnionTag(func: *Func, inst: Air.Inst.Index) !void {3668fn airGetUnionTag(func: *Func, inst: Air.Inst.Index) !void {
3563 const zcu = func.bin_file.comp.module.?;3669 const pt = func.pt;
3564 const mod = func.bin_file.comp.module.?;
3565 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;3670 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
35663671
3567 const tag_ty = func.typeOfIndex(inst);3672 const tag_ty = func.typeOfIndex(inst);
3568 const union_ty = func.typeOf(ty_op.operand);3673 const union_ty = func.typeOf(ty_op.operand);
3569 const layout = union_ty.unionGetLayout(mod);3674 const layout = union_ty.unionGetLayout(pt);
35703675
3571 if (layout.tag_size == 0) {3676 if (layout.tag_size == 0) {
3572 return func.finishAir(inst, .none, .{ ty_op.operand, .none, .none });3677 return func.finishAir(inst, .none, .{ ty_op.operand, .none, .none });
...@@ -3577,7 +3682,7 @@ fn airGetUnionTag(func: *Func, inst: Air.Inst.Index) !void {...@@ -3577,7 +3682,7 @@ fn airGetUnionTag(func: *Func, inst: Air.Inst.Index) !void {
3577 const frame_mcv = try func.allocRegOrMem(union_ty, null, false);3682 const frame_mcv = try func.allocRegOrMem(union_ty, null, false);
3578 try func.genCopy(union_ty, frame_mcv, operand);3683 try func.genCopy(union_ty, frame_mcv, operand);
35793684
3580 const tag_abi_size = tag_ty.abiSize(mod);3685 const tag_abi_size = tag_ty.abiSize(pt);
3581 const result_reg, const result_lock = try func.allocReg(.int);3686 const result_reg, const result_lock = try func.allocReg(.int);
3582 defer func.register_manager.unlockReg(result_lock);3687 defer func.register_manager.unlockReg(result_lock);
35833688
...@@ -3597,7 +3702,7 @@ fn airGetUnionTag(func: *Func, inst: Air.Inst.Index) !void {...@@ -3597,7 +3702,7 @@ fn airGetUnionTag(func: *Func, inst: Air.Inst.Index) !void {
3597 } else {3702 } else {
3598 return func.fail(3703 return func.fail(
3599 "TODO implement get_union_tag for ABI larger than 8 bytes and operand {}, tag {}",3704 "TODO implement get_union_tag for ABI larger than 8 bytes and operand {}, tag {}",
3600 .{ frame_mcv, tag_ty.fmt(zcu) },3705 .{ frame_mcv, tag_ty.fmt(pt) },
3601 );3706 );
3602 }3707 }
3603 },3708 },
...@@ -3781,13 +3886,13 @@ fn airBitReverse(func: *Func, inst: Air.Inst.Index) !void {...@@ -3781,13 +3886,13 @@ fn airBitReverse(func: *Func, inst: Air.Inst.Index) !void {
3781}3886}
37823887
3783fn airUnaryMath(func: *Func, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {3888fn airUnaryMath(func: *Func, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {
3784 const zcu = func.bin_file.comp.module.?;3889 const pt = func.pt;
3785 const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op;3890 const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
3786 const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else result: {3891 const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else result: {
3787 const ty = func.typeOf(un_op);3892 const ty = func.typeOf(un_op);
37883893
3789 const operand = try func.resolveInst(un_op);3894 const operand = try func.resolveInst(un_op);
3790 const operand_bit_size = ty.bitSize(zcu);3895 const operand_bit_size = ty.bitSize(pt);
37913896
3792 if (!math.isPowerOfTwo(operand_bit_size))3897 if (!math.isPowerOfTwo(operand_bit_size))
3793 return func.fail("TODO: airUnaryMath non-pow 2", .{});3898 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 {...@@ -3799,7 +3904,7 @@ fn airUnaryMath(func: *Func, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {
3799 const dst_reg, const dst_lock = try func.allocReg(dst_class);3904 const dst_reg, const dst_lock = try func.allocReg(dst_class);
3800 defer func.register_manager.unlockReg(dst_lock);3905 defer func.register_manager.unlockReg(dst_lock);
38013906
3802 switch (ty.zigTypeTag(zcu)) {3907 switch (ty.zigTypeTag(pt.zcu)) {
3803 .Float => {3908 .Float => {
3804 assert(dst_class == .float);3909 assert(dst_class == .float);
38053910
...@@ -3833,7 +3938,7 @@ fn airUnaryMath(func: *Func, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {...@@ -3833,7 +3938,7 @@ fn airUnaryMath(func: *Func, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {
3833 else => return func.fail("TODO: airUnaryMath Float {s}", .{@tagName(tag)}),3938 else => return func.fail("TODO: airUnaryMath Float {s}", .{@tagName(tag)}),
3834 }3939 }
3835 },3940 },
3836 else => return func.fail("TODO: airUnaryMath ty: {}", .{ty.fmt(zcu)}),3941 else => return func.fail("TODO: airUnaryMath ty: {}", .{ty.fmt(pt)}),
3837 }3942 }
38383943
3839 break :result MCValue{ .register = dst_reg };3944 break :result MCValue{ .register = dst_reg };
...@@ -4510,7 +4615,27 @@ fn airRet(func: *Func, inst: Air.Inst.Index, safety: bool) !void {...@@ -4510,7 +4615,27 @@ fn airRet(func: *Func, inst: Air.Inst.Index, safety: bool) !void {
4510 .none => {},4615 .none => {},
4511 .register,4616 .register,
4512 .register_pair,4617 .register_pair,
4513 => try func.genCopy(ret_ty, func.ret_mcv.short, .{ .air_ref = un_op }),4618 => {
4619 if (ret_ty.isVector(zcu)) {
4620 const bit_size = ret_ty.totalVectorBits(pt);
4621
4622 // set the vtype to hold the entire vector's contents in a single element
4623 try func.setVl(.zero, 0, .{
4624 .vsew = switch (bit_size) {
4625 8 => .@"8",
4626 16 => .@"16",
4627 32 => .@"32",
4628 64 => .@"64",
4629 else => unreachable,
4630 },
4631 .vlmul = .m1,
4632 .vma = true,
4633 .vta = true,
4634 });
4635 }
4636
4637 try func.genCopy(ret_ty, func.ret_mcv.short, .{ .air_ref = un_op });
4638 },
4514 .indirect => |reg_off| {4639 .indirect => |reg_off| {
4515 try func.register_manager.getReg(reg_off.reg, null);4640 try func.register_manager.getReg(reg_off.reg, null);
4516 const lock = func.register_manager.lockRegAssumeUnused(reg_off.reg);4641 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!...@@ -5735,7 +5860,12 @@ fn genSetReg(func: *Func, ty: Type, reg: Register, src_mcv: MCValue) InnerError!
5735 const zcu = pt.zcu;5860 const zcu = pt.zcu;
5736 const abi_size: u32 = @intCast(ty.abiSize(pt));5861 const abi_size: u32 = @intCast(ty.abiSize(pt));
57375862
5738 if (abi_size > 8) return std.debug.panic("tried to set reg with size {}", .{abi_size});5863 const max_size: u32 = switch (reg.class()) {
5864 .int => 64,
5865 .float => if (func.hasFeature(.d)) 64 else 32,
5866 .vector => func.vec_len,
5867 };
5868 if (abi_size > max_size) return std.debug.panic("tried to set reg with size {}", .{abi_size});
5739 const dst_reg_class = reg.class();5869 const dst_reg_class = reg.class();
57405870
5741 switch (src_mcv) {5871 switch (src_mcv) {
...@@ -5835,13 +5965,6 @@ fn genSetReg(func: *Func, ty: Type, reg: Register, src_mcv: MCValue) InnerError!...@@ -5835,13 +5965,6 @@ fn genSetReg(func: *Func, ty: Type, reg: Register, src_mcv: MCValue) InnerError!
5835 if (src_reg.id() == reg.id())5965 if (src_reg.id() == reg.id())
5836 return;5966 return;
58375967
5838 const src_reg_class = src_reg.class();
5839
5840 if (src_reg_class == .float and dst_reg_class == .int) {
5841 // to move from float -> int, we use FMV.X.W
5842 return func.fail("TODO: genSetReg float -> int", .{});
5843 }
5844
5845 // mv reg, src_reg5968 // mv reg, src_reg
5846 _ = try func.addInst(.{5969 _ = try func.addInst(.{
5847 .tag = .pseudo,5970 .tag = .pseudo,
...@@ -5854,6 +5977,43 @@ fn genSetReg(func: *Func, ty: Type, reg: Register, src_mcv: MCValue) InnerError!...@@ -5854,6 +5977,43 @@ fn genSetReg(func: *Func, ty: Type, reg: Register, src_mcv: MCValue) InnerError!
5854 },5977 },
5855 .register_pair => return func.fail("genSetReg should we allow reg -> reg_pair?", .{}),5978 .register_pair => return func.fail("genSetReg should we allow reg -> reg_pair?", .{}),
5856 .load_frame => |frame| {5979 .load_frame => |frame| {
5980 if (reg.class() == .vector) {
5981 if (abi_size > 8)
5982 return func.fail("TODO: genSetReg vectors > 8", .{});
5983
5984 const temp_reg = try func.register_manager.allocReg(null, abi.Registers.Integer.temporary);
5985 const temp_lock = func.register_manager.lockRegAssumeUnused(temp_reg);
5986 defer func.register_manager.unlockReg(temp_lock);
5987
5988 try func.setVl(.zero, 1, .{
5989 .vsew = switch (abi_size) {
5990 1 => .@"8",
5991 2 => .@"16",
5992 4 => .@"32",
5993 8 => .@"64",
5994 else => unreachable,
5995 },
5996 .vlmul = .m1,
5997 .vma = true,
5998 .vta = true,
5999 });
6000
6001 try func.genCopy(ty, .{ .register = temp_reg }, .{ .load_frame = frame });
6002
6003 _ = try func.addInst(.{
6004 .tag = .pseudo,
6005 .ops = .pseudo_mv,
6006 .data = .{
6007 .rr = .{
6008 .rd = reg,
6009 .rs = temp_reg,
6010 },
6011 },
6012 });
6013
6014 return;
6015 }
6016
5857 _ = try func.addInst(.{6017 _ = try func.addInst(.{
5858 .tag = .pseudo,6018 .tag = .pseudo,
5859 .ops = .pseudo_load_rm,6019 .ops = .pseudo_load_rm,
...@@ -6195,7 +6355,7 @@ fn airCmpxchg(func: *Func, inst: Air.Inst.Index) !void {...@@ -6195,7 +6355,7 @@ fn airCmpxchg(func: *Func, inst: Air.Inst.Index) !void {
6195}6355}
61966356
6197fn airAtomicRmw(func: *Func, inst: Air.Inst.Index) !void {6357fn airAtomicRmw(func: *Func, inst: Air.Inst.Index) !void {
6198 const zcu = func.pt.zcu;6358 const pt = func.pt;
6199 const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;6359 const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
6200 const extra = func.air.extraData(Air.AtomicRmw, pl_op.payload).data;6360 const extra = func.air.extraData(Air.AtomicRmw, pl_op.payload).data;
62016361
...@@ -6206,13 +6366,13 @@ fn airAtomicRmw(func: *Func, inst: Air.Inst.Index) !void {...@@ -6206,13 +6366,13 @@ fn airAtomicRmw(func: *Func, inst: Air.Inst.Index) !void {
6206 const ptr_mcv = try func.resolveInst(pl_op.operand);6366 const ptr_mcv = try func.resolveInst(pl_op.operand);
62076367
6208 const val_ty = func.typeOf(extra.operand);6368 const val_ty = func.typeOf(extra.operand);
6209 const val_size = val_ty.abiSize(func.pt);6369 const val_size = val_ty.abiSize(pt);
6210 const val_mcv = try func.resolveInst(extra.operand);6370 const val_mcv = try func.resolveInst(extra.operand);
62116371
6212 if (!math.isPowerOfTwo(val_size))6372 if (!math.isPowerOfTwo(val_size))
6213 return func.fail("TODO: airAtomicRmw non-pow 2", .{});6373 return func.fail("TODO: airAtomicRmw non-pow 2", .{});
62146374
6215 switch (val_ty.zigTypeTag(zcu)) {6375 switch (val_ty.zigTypeTag(pt.zcu)) {
6216 .Int => {},6376 .Int => {},
6217 inline .Bool, .Float, .Enum, .Pointer => |ty| return func.fail("TODO: airAtomicRmw {s}", .{@tagName(ty)}),6377 inline .Bool, .Float, .Enum, .Pointer => |ty| return func.fail("TODO: airAtomicRmw {s}", .{@tagName(ty)}),
6218 else => unreachable,6378 else => unreachable,
...@@ -6735,15 +6895,15 @@ fn getResolvedInstValue(func: *Func, inst: Air.Inst.Index) *InstTracking {...@@ -6735,15 +6895,15 @@ fn getResolvedInstValue(func: *Func, inst: Air.Inst.Index) *InstTracking {
6735}6895}
67366896
6737fn genTypedValue(func: *Func, val: Value) InnerError!MCValue {6897fn genTypedValue(func: *Func, val: Value) InnerError!MCValue {
6738 const zcu = func.pt.zcu;6898 const pt = func.pt;
6739 const gpa = func.gpa;6899 const gpa = func.gpa;
67406900
6741 const owner_decl_index = zcu.funcOwnerDeclIndex(func.func_index);6901 const owner_decl_index = pt.zcu.funcOwnerDeclIndex(func.func_index);
6742 const lf = func.bin_file;6902 const lf = func.bin_file;
6743 const src_loc = func.src_loc;6903 const src_loc = func.src_loc;
67446904
6745 if (val.isUndef(zcu)) {6905 if (val.isUndef(pt.zcu)) {
6746 const local_sym_index = lf.lowerUnnamedConst(func.pt, val, owner_decl_index) catch |err| {6906 const local_sym_index = lf.lowerUnnamedConst(pt, val, owner_decl_index) catch |err| {
6747 const msg = try ErrorMsg.create(gpa, src_loc, "lowering unnamed undefined constant failed: {s}", .{@errorName(err)});6907 const msg = try ErrorMsg.create(gpa, src_loc, "lowering unnamed undefined constant failed: {s}", .{@errorName(err)});
6748 func.err_msg = msg;6908 func.err_msg = msg;
6749 return error.CodegenFail;6909 return error.CodegenFail;
...@@ -6760,7 +6920,7 @@ fn genTypedValue(func: *Func, val: Value) InnerError!MCValue {...@@ -6760,7 +6920,7 @@ fn genTypedValue(func: *Func, val: Value) InnerError!MCValue {
67606920
6761 const result = try codegen.genTypedValue(6921 const result = try codegen.genTypedValue(
6762 lf,6922 lf,
6763 func.pt,6923 pt,
6764 src_loc,6924 src_loc,
6765 val,6925 val,
6766 owner_decl_index,6926 owner_decl_index,
src/arch/riscv64/Encoding.zig+133-13
...@@ -11,6 +11,7 @@ const OpCode = enum(u7) {...@@ -11,6 +11,7 @@ const OpCode = enum(u7) {
11 STORE = 0b0100011,11 STORE = 0b0100011,
12 STORE_FP = 0b0100111,12 STORE_FP = 0b0100111,
13 AMO = 0b0101111,13 AMO = 0b0101111,
14 OP_V = 0b1010111,
14 OP = 0b0110011,15 OP = 0b0110011,
15 OP_32 = 0b0111011,16 OP_32 = 0b0111011,
16 LUI = 0b0110111,17 LUI = 0b0110111,
...@@ -83,9 +84,51 @@ const Enc = struct {...@@ -83,9 +84,51 @@ const Enc = struct {
83 funct3: u3,84 funct3: u3,
84 has_5: bool,85 has_5: bool,
85 },86 },
87 vecls: struct {
88 width: VecWidth,
89 umop: Umop,
90 vm: bool,
91 mop: Mop,
92 mew: bool,
93 nf: u3,
94 },
95 vecmath: struct {
96 vm: bool,
97 funct6: u6,
98 funct3: VecType,
99 },
86 /// U-type100 /// U-type
87 none,101 none,
88 },102 },
103
104 const Mop = enum(u2) {
105 unit = 0b00,
106 unord = 0b01,
107 stride = 0b10,
108 ord = 0b11,
109 };
110
111 const Umop = enum(u5) {
112 unit = 0b00000,
113 whole = 0b01000,
114 mask = 0b01011,
115 fault = 0b10000,
116 };
117
118 const VecWidth = enum(u3) {
119 @"32" = 0b110,
120 @"64" = 0b111,
121 };
122
123 const VecType = enum(u3) {
124 OPIVV = 0b000,
125 OPFVV = 0b001,
126 OPMVV = 0b010,
127 OPIVI = 0b011,
128 OPIVX = 0b100,
129 OPFVF = 0b101,
130 OPMVX = 0b110,
131 };
89};132};
90133
91pub const Mnemonic = enum {134pub const Mnemonic = enum {
...@@ -115,6 +158,9 @@ pub const Mnemonic = enum {...@@ -115,6 +158,9 @@ pub const Mnemonic = enum {
115 addi,158 addi,
116 jalr,159 jalr,
117160
161 vsetivli,
162 vsetvli,
163
118 // U Type164 // U Type
119 lui,165 lui,
120 auipc,166 auipc,
...@@ -155,6 +201,8 @@ pub const Mnemonic = enum {...@@ -155,6 +201,8 @@ pub const Mnemonic = enum {
155 ebreak,201 ebreak,
156 unimp,202 unimp,
157203
204 csrrs,
205
158 // M extension206 // M extension
159 mul,207 mul,
160 mulw,208 mulw,
...@@ -217,6 +265,17 @@ pub const Mnemonic = enum {...@@ -217,6 +265,17 @@ pub const Mnemonic = enum {
217 fsgnjnd,265 fsgnjnd,
218 fsgnjxd,266 fsgnjxd,
219267
268 // V Extension
269 vle32v,
270 vle64v,
271
272 vse32v,
273 vse64v,
274
275 vaddvv,
276 vadcxv,
277 vadcvx,
278
220 // MISC279 // MISC
221 fence,280 fence,
222 fencetso,281 fencetso,
...@@ -373,6 +432,9 @@ pub const Mnemonic = enum {...@@ -373,6 +432,9 @@ pub const Mnemonic = enum {
373432
374 .flw => .{ .opcode = .LOAD_FP, .data = .{ .f = .{ .funct3 = 0b010 } } },433 .flw => .{ .opcode = .LOAD_FP, .data = .{ .f = .{ .funct3 = 0b010 } } },
375 .fld => .{ .opcode = .LOAD_FP, .data = .{ .f = .{ .funct3 = 0b011 } } },434 .fld => .{ .opcode = .LOAD_FP, .data = .{ .f = .{ .funct3 = 0b011 } } },
435
436 .vle32v => .{ .opcode = .LOAD_FP, .data = .{ .vecls = .{ .width = .@"32", .umop = .unit, .vm = true, .mop = .unit, .mew = true, .nf = 0b000 } } },
437 .vle64v => .{ .opcode = .LOAD_FP, .data = .{ .vecls = .{ .width = .@"64", .umop = .unit, .vm = true, .mop = .unit, .mew = true, .nf = 0b000 } } },
376 438
377439
378 // STORE_FP440 // STORE_FP
...@@ -380,6 +442,8 @@ pub const Mnemonic = enum {...@@ -380,6 +442,8 @@ pub const Mnemonic = enum {
380 .fsw => .{ .opcode = .STORE_FP, .data = .{ .f = .{ .funct3 = 0b010 } } },442 .fsw => .{ .opcode = .STORE_FP, .data = .{ .f = .{ .funct3 = 0b010 } } },
381 .fsd => .{ .opcode = .STORE_FP, .data = .{ .f = .{ .funct3 = 0b011 } } },443 .fsd => .{ .opcode = .STORE_FP, .data = .{ .f = .{ .funct3 = 0b011 } } },
382444
445 .vse32v => .{ .opcode = .STORE_FP, .data = .{ .vecls = .{ .width = .@"32", .umop = .unit, .vm = true, .mop = .unit, .mew = true, .nf = 0b000 } } },
446 .vse64v => .{ .opcode = .STORE_FP, .data = .{ .vecls = .{ .width = .@"64", .umop = .unit, .vm = true, .mop = .unit, .mew = true, .nf = 0b000 } } },
383447
384 // JALR448 // JALR
385449
...@@ -410,6 +474,8 @@ pub const Mnemonic = enum {...@@ -410,6 +474,8 @@ pub const Mnemonic = enum {
410474
411 .ecall => .{ .opcode = .SYSTEM, .data = .{ .f = .{ .funct3 = 0b000 } } },475 .ecall => .{ .opcode = .SYSTEM, .data = .{ .f = .{ .funct3 = 0b000 } } },
412 .ebreak => .{ .opcode = .SYSTEM, .data = .{ .f = .{ .funct3 = 0b000 } } },476 .ebreak => .{ .opcode = .SYSTEM, .data = .{ .f = .{ .funct3 = 0b000 } } },
477
478 .csrrs => .{ .opcode = .SYSTEM, .data = .{ .f = .{ .funct3 = 0b010 } } },
413 479
414480
415 // NONE481 // NONE
...@@ -449,7 +515,13 @@ pub const Mnemonic = enum {...@@ -449,7 +515,13 @@ pub const Mnemonic = enum {
449 .amominud => .{ .opcode = .AMO, .data = .{ .amo = .{ .width = .D, .funct5 = 0b11000 } } },515 .amominud => .{ .opcode = .AMO, .data = .{ .amo = .{ .width = .D, .funct5 = 0b11000 } } },
450 .amomaxud => .{ .opcode = .AMO, .data = .{ .amo = .{ .width = .D, .funct5 = 0b11100 } } },516 .amomaxud => .{ .opcode = .AMO, .data = .{ .amo = .{ .width = .D, .funct5 = 0b11100 } } },
451517
452 518 // OP_V
519 .vsetivli => .{ .opcode = .OP_V, .data = .{ .f = .{ .funct3 = 0b111 } } },
520 .vsetvli => .{ .opcode = .OP_V, .data = .{ .f = .{ .funct3 = 0b111 } } },
521 .vaddvv => .{ .opcode = .OP_V, .data = .{ .vecmath = .{ .vm = true, .funct6 = 0b000000, .funct3 = .OPIVV } } },
522 .vadcxv => .{ .opcode = .OP_V, .data = .{ .vecmath = .{ .vm = true, .funct6 = 0b010000, .funct3 = .OPMVX } } },
523 .vadcvx => .{ .opcode = .OP_V, .data = .{ .vecmath = .{ .vm = true, .funct6 = 0b010000, .funct3 = .OPMVV } } },
524
453 // zig fmt: on525 // zig fmt: on
454 };526 };
455 }527 }
...@@ -465,7 +537,6 @@ pub const InstEnc = enum {...@@ -465,7 +537,6 @@ pub const InstEnc = enum {
465 J,537 J,
466 fence,538 fence,
467 amo,539 amo,
468 /// extras that have unusual op counts
469 system,540 system,
470541
471 pub fn fromMnemonic(mnem: Mnemonic) InstEnc {542 pub fn fromMnemonic(mnem: Mnemonic) InstEnc {
...@@ -494,6 +565,10 @@ pub const InstEnc = enum {...@@ -494,6 +565,10 @@ pub const InstEnc = enum {
494565
495 .flw,566 .flw,
496 .fld,567 .fld,
568
569 .csrrs,
570 .vsetivli,
571 .vsetvli,
497 => .I,572 => .I,
498573
499 .lui,574 .lui,
...@@ -587,6 +662,14 @@ pub const InstEnc = enum {...@@ -587,6 +662,14 @@ pub const InstEnc = enum {
587662
588 .fsgnjxs,663 .fsgnjxs,
589 .fsgnjxd,664 .fsgnjxd,
665
666 .vle32v,
667 .vle64v,
668 .vse32v,
669 .vse64v,
670 .vaddvv,
671 .vadcxv,
672 .vadcvx,
590 => .R,673 => .R,
591674
592 .ecall,675 .ecall,
...@@ -757,6 +840,25 @@ pub const Data = union(InstEnc) {...@@ -757,6 +840,25 @@ pub const Data = union(InstEnc) {
757 },840 },
758 };841 };
759 },842 },
843 .csrrs => {
844 assert(ops.len == 3);
845
846 const csr = ops[0].csr;
847 const rs1 = ops[1].reg;
848 const rd = ops[2].reg;
849
850 return .{
851 .I = .{
852 .rd = rd.encodeId(),
853 .rs1 = rs1.encodeId(),
854
855 .imm0_11 = @intFromEnum(csr),
856
857 .opcode = @intFromEnum(enc.opcode),
858 .funct3 = enc.data.f.funct3,
859 },
860 };
861 },
760 else => {},862 else => {},
761 }863 }
762864
...@@ -783,6 +885,25 @@ pub const Data = union(InstEnc) {...@@ -783,6 +885,25 @@ pub const Data = union(InstEnc) {
783 .funct3 = fmt.rm,885 .funct3 = fmt.rm,
784 .funct7 = (@as(u7, fmt.funct5) << 2) | @intFromEnum(fmt.fmt),886 .funct7 = (@as(u7, fmt.funct5) << 2) | @intFromEnum(fmt.fmt),
785 },887 },
888 .vecls => |vec| .{
889 .rd = ops[0].reg.encodeId(),
890 .rs1 = ops[1].reg.encodeId(),
891
892 .rs2 = @intFromEnum(vec.umop),
893
894 .opcode = @intFromEnum(enc.opcode),
895 .funct3 = @intFromEnum(vec.width),
896 .funct7 = (@as(u7, vec.nf) << 4) | (@as(u7, @intFromBool(vec.mew)) << 3) | (@as(u7, @intFromEnum(vec.mop)) << 1) | @intFromBool(vec.vm),
897 },
898 .vecmath => |vec| .{
899 .rd = ops[0].reg.encodeId(),
900 .rs1 = ops[1].reg.encodeId(),
901 .rs2 = ops[2].reg.encodeId(),
902
903 .opcode = @intFromEnum(enc.opcode),
904 .funct3 = @intFromEnum(vec.funct3),
905 .funct7 = (@as(u7, vec.funct6) << 1) | @intFromBool(vec.vm),
906 },
786 else => unreachable,907 else => unreachable,
787 },908 },
788 };909 };
...@@ -897,21 +1018,21 @@ pub const Data = union(InstEnc) {...@@ -897,21 +1018,21 @@ pub const Data = union(InstEnc) {
897 .amo => {1018 .amo => {
898 assert(ops.len == 5);1019 assert(ops.len == 5);
8991020
900 const rd = ops[0];1021 const rd = ops[0].reg;
901 const rs1 = ops[1];1022 const rs1 = ops[1].reg;
902 const rs2 = ops[2];1023 const rs2 = ops[2].reg;
903 const rl = ops[3];1024 const rl = ops[3].barrier;
904 const aq = ops[4];1025 const aq = ops[4].barrier;
9051026
906 return .{1027 return .{
907 .amo = .{1028 .amo = .{
908 .rd = rd.reg.encodeId(),1029 .rd = rd.encodeId(),
909 .rs1 = rs1.reg.encodeId(),1030 .rs1 = rs1.encodeId(),
910 .rs2 = rs2.reg.encodeId(),1031 .rs2 = rs2.encodeId(),
9111032
912 // TODO: https://github.com/ziglang/zig/issues/201131033 // TODO: https://github.com/ziglang/zig/issues/20113
913 .rl = if (rl.barrier == .rl) true else false,1034 .rl = if (rl == .rl) true else false,
914 .aq = if (aq.barrier == .aq) true else false,1035 .aq = if (aq == .aq) true else false,
9151036
916 .opcode = @intFromEnum(enc.opcode),1037 .opcode = @intFromEnum(enc.opcode),
917 .funct3 = @intFromEnum(enc.data.amo.width),1038 .funct3 = @intFromEnum(enc.data.amo.width),
...@@ -919,7 +1040,6 @@ pub const Data = union(InstEnc) {...@@ -919,7 +1040,6 @@ pub const Data = union(InstEnc) {
919 },1040 },
920 };1041 };
921 },1042 },
922
923 else => std.debug.panic("TODO: construct {s}", .{@tagName(inst_enc)}),1043 else => std.debug.panic("TODO: construct {s}", .{@tagName(inst_enc)}),
924 }1044 }
925 }1045 }
src/arch/riscv64/Lower.zig+124-56
...@@ -80,58 +80,99 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index, options: struct {...@@ -80,58 +80,99 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index, options: struct {
80 .pseudo_load_rm => {80 .pseudo_load_rm => {
81 const dest_reg = rm.r;81 const dest_reg = rm.r;
82 const dest_reg_class = dest_reg.class();82 const dest_reg_class = dest_reg.class();
83 const float = dest_reg_class == .float;
8483
85 const src_size = rm.m.mod.size;84 const src_size = rm.m.mod.size;
86 const unsigned = rm.m.mod.unsigned;85 const unsigned = rm.m.mod.unsigned;
8786
88 const tag: Encoding.Mnemonic = if (!float)87 const tag: Encoding.Mnemonic = switch (dest_reg_class) {
89 switch (src_size) {88 .int => switch (src_size) {
90 .byte => if (unsigned) .lbu else .lb,89 .byte => if (unsigned) .lbu else .lb,
91 .hword => if (unsigned) .lhu else .lh,90 .hword => if (unsigned) .lhu else .lh,
92 .word => if (unsigned) .lwu else .lw,91 .word => if (unsigned) .lwu else .lw,
93 .dword => .ld,92 .dword => .ld,
94 }93 },
95 else switch (src_size) {94 .float => switch (src_size) {
96 .byte => unreachable, // Zig does not support 8-bit floats95 .byte => unreachable, // Zig does not support 8-bit floats
97 .hword => return lower.fail("TODO: lowerMir pseudo_load_rm support 16-bit floats", .{}),96 .hword => return lower.fail("TODO: lowerMir pseudo_load_rm support 16-bit floats", .{}),
98 .word => .flw,97 .word => .flw,
99 .dword => .fld,98 .dword => .fld,
99 },
100 .vector => switch (src_size) {
101 .byte,
102 .hword,
103 => return lower.fail(
104 "TODO: lowerMir pseudo_load_rm support {s} vector",
105 .{@tagName(src_size)},
106 ),
107 .word => .vle32v,
108 .dword => .vle64v,
109 },
100 };110 };
101111
102 try lower.emit(tag, &.{112 switch (dest_reg_class) {
103 .{ .reg = rm.r },113 .int, .float => {
104 .{ .reg = frame_loc.base },114 try lower.emit(tag, &.{
105 .{ .imm = Immediate.s(frame_loc.disp) },115 .{ .reg = rm.r },
106 });116 .{ .reg = frame_loc.base },
117 .{ .imm = Immediate.s(frame_loc.disp) },
118 });
119 },
120 .vector => {
121 try lower.emit(tag, &.{
122 .{ .reg = rm.r },
123 .{ .reg = frame_loc.base },
124 .{ .reg = .x0 },
125 });
126 },
127 }
107 },128 },
108 .pseudo_store_rm => {129 .pseudo_store_rm => {
109 const src_reg = rm.r;130 const src_reg = rm.r;
110 const src_reg_class = src_reg.class();131 const src_reg_class = src_reg.class();
111 const float = src_reg_class == .float;
112132
113 // TODO: do we actually need this? are all stores not usize?
114 const dest_size = rm.m.mod.size;133 const dest_size = rm.m.mod.size;
115134
116 const tag: Encoding.Mnemonic = if (!float)135 const tag: Encoding.Mnemonic = switch (src_reg_class) {
117 switch (dest_size) {136 .int => switch (dest_size) {
118 .byte => .sb,137 .byte => .sb,
119 .hword => .sh,138 .hword => .sh,
120 .word => .sw,139 .word => .sw,
121 .dword => .sd,140 .dword => .sd,
122 }141 },
123 else switch (dest_size) {142 .float => switch (dest_size) {
124 .byte => unreachable, // Zig does not support 8-bit floats143 .byte => unreachable, // Zig does not support 8-bit floats
125 .hword => return lower.fail("TODO: lowerMir pseudo_load_rm support 16-bit floats", .{}),144 .hword => return lower.fail("TODO: lowerMir pseudo_store_rm support 16-bit floats", .{}),
126 .word => .fsw,145 .word => .fsw,
127 .dword => .fsd,146 .dword => .fsd,
147 },
148 .vector => switch (dest_size) {
149 .byte,
150 .hword,
151 => return lower.fail(
152 "TODO: lowerMir pseudo_load_rm support {s} vector",
153 .{@tagName(dest_size)},
154 ),
155 .word => .vse32v,
156 .dword => .vse64v,
157 },
128 };158 };
129159
130 try lower.emit(tag, &.{160 switch (src_reg_class) {
131 .{ .reg = frame_loc.base },161 .int, .float => {
132 .{ .reg = rm.r },162 try lower.emit(tag, &.{
133 .{ .imm = Immediate.s(frame_loc.disp) },163 .{ .reg = frame_loc.base },
134 });164 .{ .reg = rm.r },
165 .{ .imm = Immediate.s(frame_loc.disp) },
166 });
167 },
168 .vector => {
169 try lower.emit(tag, &.{
170 .{ .reg = frame_loc.base },
171 .{ .reg = rm.r },
172 .{ .reg = .x0 },
173 });
174 },
175 }
135 },176 },
136 else => unreachable,177 else => unreachable,
137 }178 }
...@@ -143,34 +184,47 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index, options: struct {...@@ -143,34 +184,47 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index, options: struct {
143 const dst_class = rr.rd.class();184 const dst_class = rr.rd.class();
144 const src_class = rr.rs.class();185 const src_class = rr.rs.class();
145186
146 assert(dst_class == src_class);187 switch (src_class) {
147188 .float => switch (dst_class) {
148 switch (dst_class) {189 .float => {
149 .float => {190 try lower.emit(if (lower.hasFeature(.d)) .fsgnjnd else .fsgnjns, &.{
150 try lower.emit(if (lower.hasFeature(.d)) .fsgnjnd else .fsgnjns, &.{191 .{ .reg = rr.rd },
151 .{ .reg = rr.rd },192 .{ .reg = rr.rs },
152 .{ .reg = rr.rs },193 .{ .reg = rr.rs },
153 .{ .reg = rr.rs },194 });
154 });195 },
196 .int, .vector => return lower.fail("TODO: lowerMir pseudo_mv float -> {s}", .{@tagName(dst_class)}),
197 },
198 .int => switch (dst_class) {
199 .int => {
200 try lower.emit(.addi, &.{
201 .{ .reg = rr.rd },
202 .{ .reg = rr.rs },
203 .{ .imm = Immediate.s(0) },
204 });
205 },
206 .vector => {
207 try lower.emit(.vadcxv, &.{
208 .{ .reg = rr.rd },
209 .{ .reg = rr.rs },
210 .{ .reg = .zero },
211 });
212 },
213 .float => return lower.fail("TODO: lowerMir pseudo_mv int -> {s}", .{@tagName(dst_class)}),
155 },214 },
156 .int => {215 .vector => switch (dst_class) {
157 try lower.emit(.addi, &.{216 .int => {
158 .{ .reg = rr.rd },217 try lower.emit(.vadcvx, &.{
159 .{ .reg = rr.rs },218 .{ .reg = rr.rd },
160 .{ .imm = Immediate.s(0) },219 .{ .reg = rr.rs },
161 });220 .{ .reg = .zero },
221 });
222 },
223 .float, .vector => return lower.fail("TODO: lowerMir pseudo_mv vector -> {s}", .{@tagName(dst_class)}),
162 },224 },
163 }225 }
164 },226 },
165227
166 .pseudo_ret => {
167 try lower.emit(.jalr, &.{
168 .{ .reg = .zero },
169 .{ .reg = .ra },
170 .{ .imm = Immediate.s(0) },
171 });
172 },
173
174 .pseudo_j => {228 .pseudo_j => {
175 try lower.emit(.jal, &.{229 try lower.emit(.jal, &.{
176 .{ .reg = .zero },230 .{ .reg = .zero },
...@@ -209,7 +263,10 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index, options: struct {...@@ -209,7 +263,10 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index, options: struct {
209 const rm = inst.data.rm;263 const rm = inst.data.rm;
210 assert(rm.r.class() == .int);264 assert(rm.r.class() == .int);
211265
212 const frame = rm.m.toFrameLoc(lower.mir);266 const frame: Mir.FrameLoc = if (options.allow_frame_locs)
267 rm.m.toFrameLoc(lower.mir)
268 else
269 .{ .base = .s0, .disp = 0 };
213270
214 try lower.emit(.addi, &.{271 try lower.emit(.addi, &.{
215 .{ .reg = rm.r },272 .{ .reg = rm.r },
...@@ -376,6 +433,7 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index, options: struct {...@@ -376,6 +433,7 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index, options: struct {
376 });433 });
377 },434 },
378 },435 },
436 .vector => return lower.fail("TODO: lowerMir pseudo_cmp vector", .{}),
379 }437 }
380 },438 },
381439
...@@ -497,6 +555,11 @@ fn generic(lower: *Lower, inst: Mir.Inst) Error!void {...@@ -497,6 +555,11 @@ fn generic(lower: *Lower, inst: Mir.Inst) Error!void {
497 .{ .reg = inst.data.r_type.rs1 },555 .{ .reg = inst.data.r_type.rs1 },
498 .{ .reg = inst.data.r_type.rs2 },556 .{ .reg = inst.data.r_type.rs2 },
499 },557 },
558 .csr => &.{
559 .{ .csr = inst.data.csr.csr },
560 .{ .reg = inst.data.csr.rs1 },
561 .{ .reg = inst.data.csr.rd },
562 },
500 else => return lower.fail("TODO: generic lower ops {s}", .{@tagName(inst.ops)}),563 else => return lower.fail("TODO: generic lower ops {s}", .{@tagName(inst.ops)}),
501 });564 });
502}565}
...@@ -523,17 +586,22 @@ fn pushPopRegList(lower: *Lower, comptime spilling: bool, reg_list: Mir.Register...@@ -523,17 +586,22 @@ fn pushPopRegList(lower: *Lower, comptime spilling: bool, reg_list: Mir.Register
523 while (it.next()) |i| {586 while (it.next()) |i| {
524 const frame = lower.mir.frame_locs.get(@intFromEnum(bits.FrameIndex.spill_frame));587 const frame = lower.mir.frame_locs.get(@intFromEnum(bits.FrameIndex.spill_frame));
525 const reg = abi.Registers.all_preserved[i];588 const reg = abi.Registers.all_preserved[i];
589
526 const reg_class = reg.class();590 const reg_class = reg.class();
527 const is_float_reg = reg_class == .float;591 const load_inst: Encoding.Mnemonic, const store_inst: Encoding.Mnemonic = switch (reg_class) {
592 .int => .{ .ld, .sd },
593 .float => .{ .fld, .fsd },
594 .vector => unreachable,
595 };
528596
529 if (spilling) {597 if (spilling) {
530 try lower.emit(if (is_float_reg) .fsd else .sd, &.{598 try lower.emit(store_inst, &.{
531 .{ .reg = frame.base },599 .{ .reg = frame.base },
532 .{ .reg = abi.Registers.all_preserved[i] },600 .{ .reg = abi.Registers.all_preserved[i] },
533 .{ .imm = Immediate.s(frame.disp + reg_i) },601 .{ .imm = Immediate.s(frame.disp + reg_i) },
534 });602 });
535 } else {603 } else {
536 try lower.emit(if (is_float_reg) .fld else .ld, &.{604 try lower.emit(load_inst, &.{
537 .{ .reg = abi.Registers.all_preserved[i] },605 .{ .reg = abi.Registers.all_preserved[i] },
538 .{ .reg = frame.base },606 .{ .reg = frame.base },
539 .{ .imm = Immediate.s(frame.disp + reg_i) },607 .{ .imm = Immediate.s(frame.disp + reg_i) },
src/arch/riscv64/Mir.zig+19-51
...@@ -134,7 +134,16 @@ pub const Inst = struct {...@@ -134,7 +134,16 @@ pub const Inst = struct {
134 fltd,134 fltd,
135 fled,135 fled,
136136
137 /// A Extension Instructions137 // Zicsr Extension Instructions
138 csrrs,
139
140 // V Extension Instructions
141 vsetvli,
142 vsetivli,
143 vsetvl,
144 vaddvv,
145
146 // A Extension Instructions
138 amo,147 amo,
139148
140 /// A pseudo-instruction. Used for anything that isn't 1:1 with an149 /// A pseudo-instruction. Used for anything that isn't 1:1 with an
...@@ -146,91 +155,57 @@ pub const Inst = struct {...@@ -146,91 +155,57 @@ pub const Inst = struct {
146 /// this union. `Ops` determines which union field is active, as well as155 /// this union. `Ops` determines which union field is active, as well as
147 /// how to interpret the data within.156 /// how to interpret the data within.
148 pub const Data = union {157 pub const Data = union {
149 /// No additional data
150 ///
151 /// Used by e.g. ebreak
152 nop: void,158 nop: void,
153 /// Another instruction.
154 ///
155 /// Used by e.g. b
156 inst: Index,159 inst: Index,
157 /// Index into `extra`. Meaning of what can be found there is context-dependent.
158 ///
159 /// Used by e.g. load_memory
160 payload: u32,160 payload: u32,
161
162 r_type: struct {161 r_type: struct {
163 rd: Register,162 rd: Register,
164 rs1: Register,163 rs1: Register,
165 rs2: Register,164 rs2: Register,
166 },165 },
167
168 i_type: struct {166 i_type: struct {
169 rd: Register,167 rd: Register,
170 rs1: Register,168 rs1: Register,
171 imm12: Immediate,169 imm12: Immediate,
172 },170 },
173
174 s_type: struct {171 s_type: struct {
175 rs1: Register,172 rs1: Register,
176 rs2: Register,173 rs2: Register,
177 imm5: Immediate,174 imm5: Immediate,
178 imm7: Immediate,175 imm7: Immediate,
179 },176 },
180
181 b_type: struct {177 b_type: struct {
182 rs1: Register,178 rs1: Register,
183 rs2: Register,179 rs2: Register,
184 inst: Inst.Index,180 inst: Inst.Index,
185 },181 },
186
187 u_type: struct {182 u_type: struct {
188 rd: Register,183 rd: Register,
189 imm20: Immediate,184 imm20: Immediate,
190 },185 },
191
192 j_type: struct {186 j_type: struct {
193 rd: Register,187 rd: Register,
194 inst: Inst.Index,188 inst: Inst.Index,
195 },189 },
196
197 /// Debug info: line and column
198 ///
199 /// Used by e.g. pseudo_dbg_line
200 pseudo_dbg_line_column: struct {190 pseudo_dbg_line_column: struct {
201 line: u32,191 line: u32,
202 column: u32,192 column: u32,
203 },193 },
204
205 // Custom types to be lowered
206
207 /// Register + Memory
208 rm: struct {194 rm: struct {
209 r: Register,195 r: Register,
210 m: Memory,196 m: Memory,
211 },197 },
212
213 reg_list: Mir.RegisterList,198 reg_list: Mir.RegisterList,
214
215 /// A register
216 ///
217 /// Used by e.g. blr
218 reg: Register,199 reg: Register,
219
220 /// Two registers
221 ///
222 /// Used by e.g. mv
223 rr: struct {200 rr: struct {
224 rd: Register,201 rd: Register,
225 rs: Register,202 rs: Register,
226 },203 },
227
228 fabs: struct {204 fabs: struct {
229 rd: Register,205 rd: Register,
230 rs: Register,206 rs: Register,
231 bits: u16,207 bits: u16,
232 },208 },
233
234 compare: struct {209 compare: struct {
235 rd: Register,210 rd: Register,
236 rs1: Register,211 rs1: Register,
...@@ -245,12 +220,10 @@ pub const Inst = struct {...@@ -245,12 +220,10 @@ pub const Inst = struct {
245 },220 },
246 ty: Type,221 ty: Type,
247 },222 },
248
249 reloc: struct {223 reloc: struct {
250 atom_index: u32,224 atom_index: u32,
251 sym_index: u32,225 sym_index: u32,
252 },226 },
253
254 fence: struct {227 fence: struct {
255 pred: Barrier,228 pred: Barrier,
256 succ: Barrier,229 succ: Barrier,
...@@ -259,7 +232,6 @@ pub const Inst = struct {...@@ -259,7 +232,6 @@ pub const Inst = struct {
259 tso,232 tso,
260 },233 },
261 },234 },
262
263 amo: struct {235 amo: struct {
264 rd: Register,236 rd: Register,
265 rs1: Register,237 rs1: Register,
...@@ -269,6 +241,11 @@ pub const Inst = struct {...@@ -269,6 +241,11 @@ pub const Inst = struct {
269 op: AmoOp,241 op: AmoOp,
270 ty: Type,242 ty: Type,
271 },243 },
244 csr: struct {
245 csr: CSR,
246 rs1: Register,
247 rd: Register,
248 },
272 };249 };
273250
274 pub const Ops = enum {251 pub const Ops = enum {
...@@ -293,6 +270,9 @@ pub const Inst = struct {...@@ -293,6 +270,9 @@ pub const Inst = struct {
293 /// Another instruction.270 /// Another instruction.
294 inst,271 inst,
295272
273 /// Control and Status Register Instruction.
274 csr,
275
296 /// Pseudo-instruction that will generate a backpatched276 /// Pseudo-instruction that will generate a backpatched
297 /// function prologue.277 /// function prologue.
298 pseudo_prologue,278 pseudo_prologue,
...@@ -321,11 +301,6 @@ pub const Inst = struct {...@@ -321,11 +301,6 @@ pub const Inst = struct {
321 /// Uses `rm` payload.301 /// Uses `rm` payload.
322 pseudo_lea_rm,302 pseudo_lea_rm,
323303
324 /// Shorthand for returning, aka jumping to ra register.
325 ///
326 /// Uses nop payload.
327 pseudo_ret,
328
329 /// Jumps. Uses `inst` payload.304 /// Jumps. Uses `inst` payload.
330 pseudo_j,305 pseudo_j,
331306
...@@ -363,14 +338,6 @@ pub const Inst = struct {...@@ -363,14 +338,6 @@ pub const Inst = struct {
363 pseudo_amo,338 pseudo_amo,
364 };339 };
365340
366 // Make sure we don't accidentally make instructions bigger than expected.
367 // Note that in Debug builds, Zig is allowed to insert a secret field for safety checks.
368 // comptime {
369 // if (builtin.mode != .Debug) {
370 // assert(@sizeOf(Inst) == 8);
371 // }
372 // }
373
374 pub fn format(341 pub fn format(
375 inst: Inst,342 inst: Inst,
376 comptime fmt: []const u8,343 comptime fmt: []const u8,
...@@ -490,6 +457,7 @@ const assert = std.debug.assert;...@@ -490,6 +457,7 @@ const assert = std.debug.assert;
490457
491const bits = @import("bits.zig");458const bits = @import("bits.zig");
492const Register = bits.Register;459const Register = bits.Register;
460const CSR = bits.CSR;
493const Immediate = bits.Immediate;461const Immediate = bits.Immediate;
494const Memory = bits.Memory;462const Memory = bits.Memory;
495const FrameIndex = bits.FrameIndex;463const FrameIndex = bits.FrameIndex;
src/arch/riscv64/abi.zig+24-2
...@@ -193,6 +193,15 @@ pub fn classifySystem(ty: Type, pt: Zcu.PerThread) [8]SystemClass {...@@ -193,6 +193,15 @@ pub fn classifySystem(ty: Type, pt: Zcu.PerThread) [8]SystemClass {
193 }193 }
194 return memory_class;194 return memory_class;
195 },195 },
196 .Vector => {
197 // we pass vectors through integer registers if they are small enough to fit.
198 const vec_bits = ty.totalVectorBits(pt);
199 if (vec_bits <= 64) {
200 result[0] = .integer;
201 return result;
202 }
203 return memory_class;
204 },
196 else => |bad_ty| std.debug.panic("classifySystem {s}", .{@tagName(bad_ty)}),205 else => |bad_ty| std.debug.panic("classifySystem {s}", .{@tagName(bad_ty)}),
197 }206 }
198}207}
...@@ -254,15 +263,15 @@ fn classifyStruct(...@@ -254,15 +263,15 @@ fn classifyStruct(
254 }263 }
255}264}
256265
257const allocatable_registers = Registers.Integer.all_regs ++ Registers.Float.all_regs;266const allocatable_registers = Registers.Integer.all_regs ++ Registers.Float.all_regs ++ Registers.Vector.all_regs;
258pub const RegisterManager = RegisterManagerFn(@import("CodeGen.zig"), Register, &allocatable_registers);267pub const RegisterManager = RegisterManagerFn(@import("CodeGen.zig"), Register, &allocatable_registers);
259268
260// Register classes
261const RegisterBitSet = RegisterManager.RegisterBitSet;269const RegisterBitSet = RegisterManager.RegisterBitSet;
262270
263pub const RegisterClass = enum {271pub const RegisterClass = enum {
264 int,272 int,
265 float,273 float,
274 vector,
266};275};
267276
268pub const Registers = struct {277pub const Registers = struct {
...@@ -322,6 +331,19 @@ pub const Registers = struct {...@@ -322,6 +331,19 @@ pub const Registers = struct {
322331
323 pub const all_regs = callee_preserved_regs ++ function_arg_regs ++ temporary_regs;332 pub const all_regs = callee_preserved_regs ++ function_arg_regs ++ temporary_regs;
324 };333 };
334
335 pub const Vector = struct {
336 pub const general_purpose = initRegBitSet(Integer.all_regs.len + Float.all_regs.len, all_regs.len);
337
338 // zig fmt: off
339 pub const all_regs = [_]Register{
340 .v0, .v1, .v2, .v3, .v4, .v5, .v6, .v7,
341 .v8, .v9, .v10, .v11, .v12, .v13, .v14, .v15,
342 .v16, .v17, .v18, .v19, .v20, .v21, .v22, .v23,
343 .v24, .v25, .v26, .v27, .v28, .v29, .v30, .v31,
344 };
345 // zig fmt: on
346 };
325};347};
326348
327fn initRegBitSet(start: usize, length: usize) RegisterBitSet {349fn initRegBitSet(start: usize, length: usize) RegisterBitSet {
src/arch/riscv64/bits.zig+41-1
...@@ -128,6 +128,12 @@ pub const Immediate = union(enum) {...@@ -128,6 +128,12 @@ pub const Immediate = union(enum) {
128 }128 }
129};129};
130130
131pub const CSR = enum(u12) {
132 vl = 0xC20,
133 vtype = 0xC21,
134 vlenb = 0xC22,
135};
136
131pub const Register = enum(u8) {137pub const Register = enum(u8) {
132 // zig fmt: off138 // zig fmt: off
133139
...@@ -169,6 +175,13 @@ pub const Register = enum(u8) {...@@ -169,6 +175,13 @@ pub const Register = enum(u8) {
169 f16, f17, f18, f19, f20, f21, f22, f23, 175 f16, f17, f18, f19, f20, f21, f22, f23,
170 f24, f25, f26, f27, f28, f29, f30, f31, 176 f24, f25, f26, f27, f28, f29, f30, f31,
171177
178
179 // V extension registers
180 v0, v1, v2, v3, v4, v5, v6, v7,
181 v8, v9, v10, v11, v12, v13, v14, v15,
182 v16, v17, v18, v19, v20, v21, v22, v23,
183 v24, v25, v26, v27, v28, v29, v30, v31,
184
172 // zig fmt: on185 // zig fmt: on
173186
174 /// in RISC-V registers are stored as 5 bit IDs and a register can have187 /// in RISC-V registers are stored as 5 bit IDs and a register can have
...@@ -180,11 +193,12 @@ pub const Register = enum(u8) {...@@ -180,11 +193,12 @@ pub const Register = enum(u8) {
180 /// The goal of this function is to return the same ID for `zero` and `x0` but two193 /// The goal of this function is to return the same ID for `zero` and `x0` but two
181 /// seperate IDs for `x0` and `f0`. We will assume that each register set has 32 registers194 /// seperate IDs for `x0` and `f0`. We will assume that each register set has 32 registers
182 /// and is repeated twice, once for the named version, once for the number version.195 /// and is repeated twice, once for the named version, once for the number version.
183 pub fn id(reg: Register) u7 {196 pub fn id(reg: Register) u8 {
184 const base = switch (@intFromEnum(reg)) {197 const base = switch (@intFromEnum(reg)) {
185 // zig fmt: off198 // zig fmt: off
186 @intFromEnum(Register.zero) ... @intFromEnum(Register.x31) => @intFromEnum(Register.zero),199 @intFromEnum(Register.zero) ... @intFromEnum(Register.x31) => @intFromEnum(Register.zero),
187 @intFromEnum(Register.ft0) ... @intFromEnum(Register.f31) => @intFromEnum(Register.ft0),200 @intFromEnum(Register.ft0) ... @intFromEnum(Register.f31) => @intFromEnum(Register.ft0),
201 @intFromEnum(Register.v0) ... @intFromEnum(Register.v31) => @intFromEnum(Register.v0),
188 else => unreachable,202 else => unreachable,
189 // zig fmt: on203 // zig fmt: on
190 };204 };
...@@ -207,6 +221,7 @@ pub const Register = enum(u8) {...@@ -207,6 +221,7 @@ pub const Register = enum(u8) {
207 // zig fmt: off221 // zig fmt: off
208 @intFromEnum(Register.zero) ... @intFromEnum(Register.x31) => 64,222 @intFromEnum(Register.zero) ... @intFromEnum(Register.x31) => 64,
209 @intFromEnum(Register.ft0) ... @intFromEnum(Register.f31) => if (Target.riscv.featureSetHas(features, .d)) 64 else 32,223 @intFromEnum(Register.ft0) ... @intFromEnum(Register.f31) => if (Target.riscv.featureSetHas(features, .d)) 64 else 32,
224 @intFromEnum(Register.v0) ... @intFromEnum(Register.v31) => 1024, // TODO: look at suggestVectorSize
210 else => unreachable,225 else => unreachable,
211 // zig fmt: on226 // zig fmt: on
212 };227 };
...@@ -217,6 +232,7 @@ pub const Register = enum(u8) {...@@ -217,6 +232,7 @@ pub const Register = enum(u8) {
217 // zig fmt: off232 // zig fmt: off
218 @intFromEnum(Register.zero) ... @intFromEnum(Register.x31) => .int,233 @intFromEnum(Register.zero) ... @intFromEnum(Register.x31) => .int,
219 @intFromEnum(Register.ft0) ... @intFromEnum(Register.f31) => .float,234 @intFromEnum(Register.ft0) ... @intFromEnum(Register.f31) => .float,
235 @intFromEnum(Register.v0) ... @intFromEnum(Register.v31) => .vector,
220 else => unreachable,236 else => unreachable,
221 // zig fmt: on237 // zig fmt: on
222 };238 };
...@@ -272,3 +288,27 @@ pub const Symbol = struct {...@@ -272,3 +288,27 @@ pub const Symbol = struct {
272 /// Index into the linker's symbol table.288 /// Index into the linker's symbol table.
273 sym_index: u32,289 sym_index: u32,
274};290};
291
292pub const VType = packed struct(u8) {
293 vlmul: VlMul,
294 vsew: VSew,
295 vta: bool,
296 vma: bool,
297};
298
299const VSew = enum(u3) {
300 @"8" = 0b000,
301 @"16" = 0b001,
302 @"32" = 0b010,
303 @"64" = 0b011,
304};
305
306const VlMul = enum(u3) {
307 mf8 = 0b101,
308 mf4 = 0b110,
309 mf2 = 0b111,
310 m1 = 0b000,
311 m2 = 0b001,
312 m4 = 0b010,
313 m8 = 0b011,
314};
src/arch/riscv64/encoder.zig+3
...@@ -5,6 +5,7 @@ pub const Instruction = struct {...@@ -5,6 +5,7 @@ pub const Instruction = struct {
5 pub const Operand = union(enum) {5 pub const Operand = union(enum) {
6 none,6 none,
7 reg: Register,7 reg: Register,
8 csr: CSR,
8 mem: Memory,9 mem: Memory,
9 imm: Immediate,10 imm: Immediate,
10 barrier: Mir.Barrier,11 barrier: Mir.Barrier,
...@@ -58,6 +59,7 @@ pub const Instruction = struct {...@@ -58,6 +59,7 @@ pub const Instruction = struct {
58 .imm => |imm| try writer.print("{d}", .{imm.asSigned(64)}),59 .imm => |imm| try writer.print("{d}", .{imm.asSigned(64)}),
59 .mem => try writer.writeAll("mem"),60 .mem => try writer.writeAll("mem"),
60 .barrier => |barrier| try writer.writeAll(@tagName(barrier)),61 .barrier => |barrier| try writer.writeAll(@tagName(barrier)),
62 .csr => |csr| try writer.writeAll(@tagName(csr)),
61 }63 }
62 }64 }
63 }65 }
...@@ -71,6 +73,7 @@ const bits = @import("bits.zig");...@@ -71,6 +73,7 @@ const bits = @import("bits.zig");
71const Encoding = @import("Encoding.zig");73const Encoding = @import("Encoding.zig");
7274
73const Register = bits.Register;75const Register = bits.Register;
76const CSR = bits.CSR;
74const Memory = bits.Memory;77const Memory = bits.Memory;
75const Immediate = bits.Immediate;78const Immediate = bits.Immediate;
7679
test/tests.zig+6-5
...@@ -436,11 +436,12 @@ const test_targets = blk: {...@@ -436,11 +436,12 @@ const test_targets = blk: {
436 //},436 //},
437437
438 .{438 .{
439 .target = .{439 .target = std.Target.Query.parse(
440 .cpu_arch = .riscv64,440 .{
441 .os_tag = .linux,441 .arch_os_abi = "riscv64-linux-musl",
442 .abi = .musl,442 .cpu_features = "baseline+v",
443 },443 },
444 ) catch @panic("OOM"),
444 .use_llvm = false,445 .use_llvm = false,
445 .use_lld = false,446 .use_lld = false,
446 },447 },