From 9766b68c475438e24885dd75cf137d51e72ccfa3 Mon Sep 17 00:00:00 2001 From: David Rubin Date: Thu, 4 Jul 2024 17:38:03 -0700 Subject: [PATCH 01/27] riscv: un-cache the `avl` and `vtype` when returning from a function call the csrs `avl` and `vtype` are considered caller-saved so it could have changed while inside of the function. the easiest way to handle this is to just set the cached `vtype` and `avl` to null, so that the next time something needs to set it, it'll emit an instruction instead of relying on a potentially invalid setting. --- src/arch/riscv64/CodeGen.zig | 11 ++++++++++- src/arch/riscv64/Encoding.zig | 7 +++++++ src/arch/riscv64/Mir.zig | 2 ++ src/arch/riscv64/abi.zig | 2 ++ test/behavior/vector.zig | 5 +++-- 5 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index 6e203161c2..13713ccb84 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -2481,6 +2481,11 @@ fn genBinOp( .Float => .vfsubvv, else => unreachable, }, + .mul => switch (child_ty.zigTypeTag(zcu)) { + .Int => .vmulvv, + .Float => .vfmulvv, + else => unreachable, + }, else => return func.fail("TODO: genBinOp {s} Vector", .{@tagName(tag)}), }; @@ -2490,7 +2495,7 @@ fn genBinOp( 16 => .@"16", 32 => .@"32", 64 => .@"64", - else => unreachable, + else => return func.fail("TODO: genBinOp > 64 bit elements, found {d}", .{elem_size}), }, .vlmul = .m1, .vma = true, @@ -4638,6 +4643,10 @@ fn genCall( .lib => return func.fail("TODO: lib func calls", .{}), } + // reset the vector settings as they might have changed in the function + func.avl = null; + func.vtype = null; + return call_info.return_value.short; } diff --git a/src/arch/riscv64/Encoding.zig b/src/arch/riscv64/Encoding.zig index f4684fe42b..77cd905e7f 100644 --- a/src/arch/riscv64/Encoding.zig +++ b/src/arch/riscv64/Encoding.zig @@ -288,6 +288,9 @@ pub const Mnemonic = enum { vfaddvv, vfsubvv, + vmulvv, + vfmulvv, + vadcvv, vmvvx, @@ -546,9 +549,11 @@ pub const Mnemonic = enum { .vsetvli => .{ .opcode = .OP_V, .data = .{ .f = .{ .funct3 = 0b111 } } }, .vaddvv => .{ .opcode = .OP_V, .data = .{ .vecmath = .{ .vm = true, .funct6 = 0b000000, .funct3 = .OPIVV } } }, .vsubvv => .{ .opcode = .OP_V, .data = .{ .vecmath = .{ .vm = true, .funct6 = 0b000010, .funct3 = .OPIVV } } }, + .vmulvv => .{ .opcode = .OP_V, .data = .{ .vecmath = .{ .vm = true, .funct6 = 0b100101, .funct3 = .OPIVV } } }, .vfaddvv => .{ .opcode = .OP_V, .data = .{ .vecmath = .{ .vm = true, .funct6 = 0b000000, .funct3 = .OPFVV } } }, .vfsubvv => .{ .opcode = .OP_V, .data = .{ .vecmath = .{ .vm = true, .funct6 = 0b000010, .funct3 = .OPFVV } } }, + .vfmulvv => .{ .opcode = .OP_V, .data = .{ .vecmath = .{ .vm = true, .funct6 = 0b100100, .funct3 = .OPFVV } } }, .vadcvv => .{ .opcode = .OP_V, .data = .{ .vecmath = .{ .vm = true, .funct6 = 0b010000, .funct3 = .OPMVV } } }, .vmvvx => .{ .opcode = .OP_V, .data = .{ .vecmath = .{ .vm = true, .funct6 = 0b010111, .funct3 = .OPIVX } } }, @@ -710,8 +715,10 @@ pub const InstEnc = enum { .vaddvv, .vsubvv, + .vmulvv, .vfaddvv, .vfsubvv, + .vfmulvv, .vadcvv, .vmvvx, .vslidedownvx, diff --git a/src/arch/riscv64/Mir.zig b/src/arch/riscv64/Mir.zig index 1478bf5d5b..78da136706 100644 --- a/src/arch/riscv64/Mir.zig +++ b/src/arch/riscv64/Mir.zig @@ -145,6 +145,8 @@ pub const Inst = struct { vfaddvv, vsubvv, vfsubvv, + vmulvv, + vfmulvv, vslidedownvx, /// A pseudo-instruction. Used for anything that isn't 1:1 with an diff --git a/src/arch/riscv64/abi.zig b/src/arch/riscv64/abi.zig index 3f5f7f6744..baed5cc68c 100644 --- a/src/arch/riscv64/abi.zig +++ b/src/arch/riscv64/abi.zig @@ -200,6 +200,8 @@ pub fn classifySystem(ty: Type, pt: Zcu.PerThread) [8]SystemClass { result[0] = .integer; return result; } + // we should pass vector registers of size <= 128 through 2 integer registers + // but we haven't implemented seperating vector registers into register_pairs return memory_class; }, else => |bad_ty| std.debug.panic("classifySystem {s}", .{@tagName(bad_ty)}), diff --git a/test/behavior/vector.zig b/test/behavior/vector.zig index 6a1e36e9c4..d4b2acd6af 100644 --- a/test/behavior/vector.zig +++ b/test/behavior/vector.zig @@ -102,7 +102,6 @@ test "vector float operators" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .aarch64) { @@ -119,7 +118,7 @@ test "vector float operators" { try expectEqual(v + x, .{ 11, 22, 33, 44 }); try expectEqual(v - x, .{ 9, 18, 27, 36 }); try expectEqual(v * x, .{ 10, 40, 90, 160 }); - try expectEqual(-x, .{ -1, -2, -3, -4 }); + if (builtin.zig_backend != .stage2_riscv64) try expectEqual(-x, .{ -1, -2, -3, -4 }); } }; @@ -129,6 +128,8 @@ test "vector float operators" { try S.doTheTest(f64); try comptime S.doTheTest(f64); + if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; + try S.doTheTest(f16); try comptime S.doTheTest(f16); From 8d30fc45c424ed1aaf9067436a64b0744619c250 Mon Sep 17 00:00:00 2001 From: David Rubin Date: Fri, 5 Jul 2024 03:49:09 -0700 Subject: [PATCH 02/27] riscv: implement more operators we can run `std.debug.print` now, with both run-time strings and integers! --- lib/compiler/test_runner.zig | 16 +-- lib/std/builtin.zig | 9 +- src/Package/Module.zig | 2 +- src/arch/riscv64/CodeGen.zig | 206 ++++++++++++++++---------------- src/target.zig | 3 +- test/behavior/align.zig | 1 - test/behavior/basic.zig | 2 - test/behavior/call.zig | 1 - test/behavior/cast.zig | 2 - test/behavior/destructure.zig | 2 - test/behavior/enum.zig | 1 - test/behavior/fn.zig | 1 - test/behavior/for.zig | 2 - test/behavior/if.zig | 3 - test/behavior/math.zig | 9 +- test/behavior/null.zig | 1 - test/behavior/optional.zig | 3 - test/behavior/packed-struct.zig | 1 - test/behavior/struct.zig | 1 - test/behavior/switch.zig | 1 - test/behavior/threadlocal.zig | 2 - test/behavior/while.zig | 1 - 22 files changed, 124 insertions(+), 146 deletions(-) diff --git a/lib/compiler/test_runner.zig b/lib/compiler/test_runner.zig index 25f029a183..cf8d8df3f0 100644 --- a/lib/compiler/test_runner.zig +++ b/lib/compiler/test_runner.zig @@ -271,6 +271,7 @@ pub fn mainSimple() anyerror!void { }; // is the backend capable of using std.fmt.format to print a summary at the end? const print_summary = switch (builtin.zig_backend) { + .stage2_riscv64 => true, else => false, }; @@ -282,11 +283,13 @@ pub fn mainSimple() anyerror!void { const stderr = if (comptime enable_print) std.io.getStdErr() else {}; for (builtin.test_functions) |test_fn| { - if (enable_print) { - stderr.writeAll(test_fn.name) catch {}; - stderr.writeAll("... ") catch {}; - } - test_fn.func() catch |err| { + if (test_fn.func()) |_| { + if (enable_print) { + stderr.writeAll(test_fn.name) catch {}; + stderr.writeAll("... ") catch {}; + stderr.writeAll("PASS\n") catch {}; + } + } else |err| if (enable_print) { if (enable_print) { stderr.writeAll(test_fn.name) catch {}; stderr.writeAll("... ") catch {}; @@ -300,8 +303,7 @@ pub fn mainSimple() anyerror!void { if (enable_print) stderr.writeAll("SKIP\n") catch {}; skipped += 1; continue; - }; - if (enable_print) stderr.writeAll("PASS\n") catch {}; + } passed += 1; } if (enable_print and print_summary) { diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index 0ef5cffd24..a600f055df 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -775,14 +775,7 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace, ret_addr } if (builtin.zig_backend == .stage2_riscv64) { - asm volatile ("ecall" - : - : [number] "{a7}" (64), - [arg1] "{a0}" (1), - [arg2] "{a1}" (@intFromPtr(msg.ptr)), - [arg3] "{a2}" (msg.len), - : "memory" - ); + std.debug.print("panic: {s}\n", .{msg}); std.posix.exit(127); } diff --git a/src/Package/Module.zig b/src/Package/Module.zig index 02d9921016..371b8e9816 100644 --- a/src/Package/Module.zig +++ b/src/Package/Module.zig @@ -159,7 +159,7 @@ pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module { if (options.inherited.single_threaded) |x| break :b x; if (options.parent) |p| break :b p.single_threaded; - break :b target_util.defaultSingleThreaded(target); + break :b target_util.defaultSingleThreaded(target, zig_backend); }; const error_tracing = b: { diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index 13713ccb84..ad5bec87c6 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -51,7 +51,6 @@ const InnerError = CodeGenError || error{OutOfRegisters}; pt: Zcu.PerThread, air: Air, liveness: Liveness, -zcu: *Zcu, bin_file: *link.File, gpa: Allocator, @@ -264,13 +263,13 @@ const MCValue = union(enum) { .register_pair, .memory, .indirect, - .load_frame, .load_symbol, .lea_symbol, => switch (off) { 0 => mcv, - else => unreachable, // not offsettable + else => unreachable, }, + .load_frame => |frame| .{ .load_frame = .{ .index = frame.index, .off = frame.off + off } }, .immediate => |imm| .{ .immediate = @bitCast(@as(i64, @bitCast(imm)) +% off) }, .register => |reg| .{ .register_offset = .{ .reg = reg, .off = off } }, .register_offset => |reg_off| .{ .register_offset = .{ .reg = reg_off.reg, .off = reg_off.off + off } }, @@ -737,7 +736,6 @@ pub fn generate( .air = air, .pt = pt, .mod = mod, - .zcu = zcu, .bin_file = bin_file, .liveness = liveness, .target = target, @@ -946,7 +944,7 @@ fn formatDecl( } fn fmtDecl(func: *Func, decl_index: InternPool.DeclIndex) std.fmt.Formatter(formatDecl) { return .{ .data = .{ - .zcu = func.zcu, + .zcu = func.pt.zcu, .decl_index = decl_index, } }; } @@ -1325,6 +1323,7 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void { .mul, .mul_wrap, .div_trunc, + .rem, .shl, .shl_exact, .shr, .shr_exact, @@ -1344,7 +1343,6 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void { .ptr_add, .ptr_sub => try func.airPtrArithmetic(inst, tag), - .rem, .mod, .div_float, .div_floor, @@ -2151,11 +2149,16 @@ fn airTrunc(func: *Func, inst: Air.Inst.Index) !void { const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; if (func.liveness.isUnused(inst)) return func.finishAir(inst, .unreach, .{ ty_op.operand, .none, .none }); - + // we assume no zeroext in the "Zig ABI", so it's fine to just not truncate it. const operand = try func.resolveInst(ty_op.operand); - _ = operand; - return func.fail("TODO implement trunc for {}", .{func.target.cpu.arch}); - // return func.finishAir(inst, result, .{ ty_op.operand, .none, .none }); + + // we can do it just to be safe, but this shouldn't be needed for no-runtime safety modes + switch (operand) { + .register => |reg| try func.truncateRegister(func.typeOf(ty_op.operand), reg), + else => {}, + } + + return func.finishAir(inst, operand, .{ ty_op.operand, .none, .none }); } fn airIntFromBool(func: *Func, inst: Air.Inst.Index) !void { @@ -2305,10 +2308,7 @@ fn binOp( 80, 128 => true, else => unreachable, }; - switch (air_tag) { - .rem, .mod => {}, - else => if (!type_needs_libcall) break :libcall, - } + if (!type_needs_libcall) break :libcall; return func.fail("binOp libcall runtime-float ops", .{}); } @@ -2384,6 +2384,7 @@ fn genBinOp( .sub_wrap, .mul, .mul_wrap, + .rem, => { if (!math.isPowerOfTwo(bit_size)) return func.fail( @@ -2391,6 +2392,15 @@ fn genBinOp( .{ @tagName(tag), bit_size }, ); + switch (tag) { + .rem, + => { + try func.truncateRegister(lhs_ty, lhs_reg); + try func.truncateRegister(rhs_ty, rhs_reg); + }, + else => {}, + } + switch (lhs_ty.zigTypeTag(zcu)) { .Int => { const mir_tag: Mir.Inst.Tag = switch (tag) { @@ -2409,6 +2419,10 @@ fn genBinOp( 32 => .mulw, else => unreachable, }, + .rem => switch (bit_size) { + 64 => if (is_unsigned) .remu else .rem, + else => if (is_unsigned) .remuw else .remu, + }, else => unreachable, }; @@ -2423,14 +2437,6 @@ fn genBinOp( }, }, }); - - // truncate when the instruction is larger than the bit size. - switch (bit_size) { - 8, 16 => try func.truncateRegister(lhs_ty, dst_reg), - 32 => {}, // addw/subw affects the first 32-bits - 64 => {}, // add/sub affects the entire register - else => unreachable, - } }, .Float => { const mir_tag: Mir.Inst.Tag = switch (tag) { @@ -2627,23 +2633,17 @@ fn genBinOp( .shl, .shl_exact, => { - if (!math.isPowerOfTwo(bit_size)) - return func.fail( - "TODO: genBinOp {s} non-pow 2, found {}", - .{ @tagName(tag), bit_size }, - ); - - // it's important that the shift amount is exact + if (bit_size > 64) return func.fail("TODO: genBinOp shift > 64 bits, {}", .{bit_size}); try func.truncateRegister(rhs_ty, rhs_reg); const mir_tag: Mir.Inst.Tag = switch (tag) { .shl, .shl_exact => switch (bit_size) { - 8, 16, 64 => .sll, + 1...31, 33...64 => .sll, 32 => .sllw, else => unreachable, }, .shr, .shr_exact => switch (bit_size) { - 8, 16, 64 => .srl, + 1...31, 33...64 => .srl, 32 => .srlw, else => unreachable, }, @@ -2659,13 +2659,6 @@ fn genBinOp( .rs2 = rhs_reg, } }, }); - - switch (bit_size) { - 8, 16 => try func.truncateRegister(lhs_ty, dst_reg), - 32 => {}, - 64 => {}, - else => unreachable, - } }, // TODO: move the isel logic out of lower and into here. @@ -2810,10 +2803,6 @@ fn airAddWithOverflow(func: *Func, inst: Air.Inst.Index) !void { if (int_info.bits >= 8 and math.isPowerOfTwo(int_info.bits)) { const add_result = try func.binOp(null, .add, extra.lhs, extra.rhs); - const add_result_reg = try func.copyToTmpRegister(ty, add_result); - const add_result_reg_lock = func.register_manager.lockRegAssumeUnused(add_result_reg); - defer func.register_manager.unlockReg(add_result_reg_lock); - try func.genSetMem( .{ .frame = offset.index }, offset.off + @as(i32, @intCast(tuple_ty.structFieldOffset(0, pt))), @@ -2821,14 +2810,21 @@ fn airAddWithOverflow(func: *Func, inst: Air.Inst.Index) !void { add_result, ); + const trunc_reg = try func.copyToTmpRegister(ty, add_result); + const trunc_reg_lock = func.register_manager.lockRegAssumeUnused(trunc_reg); + defer func.register_manager.unlockReg(trunc_reg_lock); + const overflow_reg, const overflow_lock = try func.allocReg(.int); defer func.register_manager.unlockReg(overflow_lock); + // if the result isn't equal after truncating it to the given type, + // an overflow must have happened. + try func.truncateRegister(func.typeOf(extra.lhs), trunc_reg); try func.genBinOp( .cmp_neq, - .{ .register = add_result_reg }, + add_result, ty, - .{ .register = add_result_reg }, + .{ .register = trunc_reg }, ty, overflow_reg, ); @@ -3022,61 +3018,34 @@ fn airMulWithOverflow(func: *Func, inst: Air.Inst.Index) !void { switch (lhs_ty.zigTypeTag(zcu)) { else => |x| return func.fail("TODO: airMulWithOverflow {s}", .{@tagName(x)}), .Int => { - assert(lhs_ty.eql(rhs_ty, zcu)); - const int_info = lhs_ty.intInfo(zcu); - switch (int_info.bits) { - 1...32 => { - if (int_info.bits >= 8 and math.isPowerOfTwo(int_info.bits)) { - if (int_info.signedness == .unsigned) { - switch (int_info.bits) { - 1...8 => { - const max_val = std.math.pow(u16, 2, int_info.bits) - 1; + if (std.debug.runtime_safety) assert(lhs_ty.eql(rhs_ty, zcu)); - const add_reg, const add_lock = try func.promoteReg(lhs_ty, lhs); - defer if (add_lock) |lock| func.register_manager.unlockReg(lock); + const trunc_reg = try func.copyToTmpRegister(lhs_ty, .{ .register = dest_reg }); + const trunc_reg_lock = func.register_manager.lockRegAssumeUnused(trunc_reg); + defer func.register_manager.unlockReg(trunc_reg_lock); - const overflow_reg, const overflow_lock = try func.allocReg(.int); - defer func.register_manager.unlockReg(overflow_lock); + const overflow_reg, const overflow_lock = try func.allocReg(.int); + defer func.register_manager.unlockReg(overflow_lock); - _ = try func.addInst(.{ - .tag = .andi, - .ops = .rri, - .data = .{ .i_type = .{ - .rd = overflow_reg, - .rs1 = add_reg, - .imm12 = Immediate.s(max_val), - } }, - }); + // if the result isn't equal after truncating it to the given type, + // an overflow must have happened. + try func.truncateRegister(func.typeOf(extra.lhs), trunc_reg); + try func.genBinOp( + .cmp_neq, + .{ .register = dest_reg }, + lhs_ty, + .{ .register = trunc_reg }, + rhs_ty, + overflow_reg, + ); - try func.genBinOp( - .cmp_neq, - .{ .register = overflow_reg }, - lhs_ty, - .{ .register = add_reg }, - lhs_ty, - overflow_reg, - ); + try func.genCopy( + lhs_ty, + result_mcv.offset(overflow_off), + .{ .register = overflow_reg }, + ); - try func.genCopy( - lhs_ty, - result_mcv.offset(overflow_off), - .{ .register = overflow_reg }, - ); - - break :result result_mcv; - }, - - else => return func.fail("TODO: airMulWithOverflow check for size {d}", .{int_info.bits}), - } - } else { - return func.fail("TODO: airMulWithOverflow calculate carry for signed addition", .{}); - } - } else { - return func.fail("TODO: airMulWithOverflow with < 8 bits or non-pow of 2", .{}); - } - }, - else => return func.fail("TODO: airMulWithOverflow larger than 32-bit mul", .{}), - } + break :result result_mcv; }, } }; @@ -3317,7 +3286,17 @@ fn airWrapOptional(func: *Func, inst: Air.Inst.Index) !void { Type.u8, .{ .immediate = 1 }, ), - .register => return func.fail("TODO: airWrapOption opt_mcv register", .{}), + + .register => |opt_reg| { + try func.genBinOp( + .shl, + .{ .immediate = 1 }, + Type.u64, + .{ .immediate = 32 }, + Type.u64, + opt_reg, + ); + }, else => unreachable, } } @@ -4059,7 +4038,7 @@ fn airLoad(func: *Func, inst: Air.Inst.Index) !void { const elem_size = elem_ty.abiSize(pt); const dst_mcv: MCValue = blk: { - // Pointer is 8 bytes, and if the element is more than that, we cannot reuse it. + // "ptr" is 8 bytes, and if the element is more than that, we cannot reuse it. if (elem_size <= 8 and func.reuseOperand(inst, ty_op.operand, 0, ptr)) { // The MCValue that holds the pointer can be re-used as the value. break :blk ptr; @@ -4970,7 +4949,7 @@ fn isNull(func: *Func, inst: Air.Inst.Index, opt_ty: Type, opt_mcv: MCValue) !MC .lea_symbol, .reserved_frame, .air_ref, - => return func.fail("TODO: hmm {}", .{opt_mcv}), + => unreachable, .register => |opt_reg| { if (some_info.off == 0) { @@ -4993,9 +4972,27 @@ fn isNull(func: *Func, inst: Air.Inst.Index, opt_ty: Type, opt_mcv: MCValue) !MC return return_mcv; } assert(some_info.ty.ip_index == .bool_type); - const opt_abi_size: u32 = @intCast(opt_ty.abiSize(pt)); - _ = opt_abi_size; - return func.fail("TODO: isNull some_info.off != 0 register", .{}); + const bit_offset: u7 = @intCast(some_info.off * 8); + + try func.genBinOp( + .shr, + .{ .register = opt_reg }, + Type.u64, + .{ .immediate = bit_offset }, + Type.u8, + return_reg, + ); + try func.truncateRegister(Type.u8, return_reg); + try func.genBinOp( + .cmp_eq, + .{ .register = return_reg }, + Type.u64, + .{ .immediate = 0 }, + Type.u8, + return_reg, + ); + + return return_mcv; }, .load_frame => { @@ -6556,7 +6553,8 @@ fn airAtomicRmw(func: *Func, inst: Air.Inst.Index) !void { } fn airAtomicLoad(func: *Func, inst: Air.Inst.Index) !void { - const zcu = func.pt.zcu; + const pt = func.pt; + const zcu = pt.zcu; const atomic_load = func.air.instructions.items(.data)[@intFromEnum(inst)].atomic_load; const order: std.builtin.AtomicOrder = atomic_load.order; @@ -6564,6 +6562,9 @@ fn airAtomicLoad(func: *Func, inst: Air.Inst.Index) !void { const elem_ty = ptr_ty.childType(zcu); const ptr_mcv = try func.resolveInst(atomic_load.ptr); + const bit_size = elem_ty.bitSize(pt); + if (bit_size > 64) return func.fail("TODO: airAtomicStore > 64 bits", .{}); + const result_mcv = try func.allocRegOrMem(elem_ty, inst, true); assert(result_mcv == .register); // should be less than 8 bytes @@ -6616,6 +6617,9 @@ fn airAtomicStore(func: *Func, inst: Air.Inst.Index, order: std.builtin.AtomicOr const val_ty = func.typeOf(bin_op.rhs); const val_mcv = try func.resolveInst(bin_op.rhs); + const bit_size = val_ty.bitSize(func.pt); + if (bit_size > 64) return func.fail("TODO: airAtomicStore > 64 bits", .{}); + switch (order) { .unordered, .monotonic => {}, .release, .seq_cst => { diff --git a/src/target.zig b/src/target.zig index 6ff9e69e61..9b7e12408e 100644 --- a/src/target.zig +++ b/src/target.zig @@ -60,9 +60,10 @@ pub fn alwaysSingleThreaded(target: std.Target) bool { return false; } -pub fn defaultSingleThreaded(target: std.Target) bool { +pub fn defaultSingleThreaded(target: std.Target, backend: std.builtin.CompilerBackend) bool { switch (target.cpu.arch) { .wasm32, .wasm64 => return true, + .riscv64 => if (backend == .stage2_riscv64) return true, else => {}, } switch (target.os.tag) { diff --git a/test/behavior/align.zig b/test/behavior/align.zig index 0b588ce091..33e43740f6 100644 --- a/test/behavior/align.zig +++ b/test/behavior/align.zig @@ -16,7 +16,6 @@ test "global variable alignment" { } test "large alignment of local constant" { - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // flaky diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig index 05d6549683..494240c63a 100644 --- a/test/behavior/basic.zig +++ b/test/behavior/basic.zig @@ -16,8 +16,6 @@ test "empty function with comments" { } test "truncate" { - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - try expect(testTruncate(0x10fd) == 0xfd); comptime assert(testTruncate(0x10fd) == 0xfd); } diff --git a/test/behavior/call.zig b/test/behavior/call.zig index 8636955215..b89ffbaef4 100644 --- a/test/behavior/call.zig +++ b/test/behavior/call.zig @@ -441,7 +441,6 @@ test "non-anytype generic parameters provide result type" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { fn f(comptime T: type, y: T) !void { diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index 6cc881b64d..816fa02f5b 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -1845,7 +1845,6 @@ test "peer type resolution: three-way resolution combines error set and optional if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const E = error{Foo}; var a: E = error.Foo; @@ -1960,7 +1959,6 @@ test "peer type resolution: vector and tuple" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; var vec: @Vector(3, i32) = .{ 1, 2, 3 }; _ = &vec; diff --git a/test/behavior/destructure.zig b/test/behavior/destructure.zig index 3164d25187..43ddbb7a4d 100644 --- a/test/behavior/destructure.zig +++ b/test/behavior/destructure.zig @@ -23,8 +23,6 @@ test "simple destructure" { } test "destructure with comptime syntax" { - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - const S = struct { fn doTheTest() !void { { diff --git a/test/behavior/enum.zig b/test/behavior/enum.zig index 7972135bfa..982adc234d 100644 --- a/test/behavior/enum.zig +++ b/test/behavior/enum.zig @@ -1076,7 +1076,6 @@ test "enum literal casting to optional" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; var bar: ?Bar = undefined; bar = .B; diff --git a/test/behavior/fn.zig b/test/behavior/fn.zig index 73ef9bdbfe..c0cb29e33a 100644 --- a/test/behavior/fn.zig +++ b/test/behavior/fn.zig @@ -181,7 +181,6 @@ test "function with complex callconv and return type expressions" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; try expect(fComplexCallconvRet(3).x == 9); } diff --git a/test/behavior/for.zig b/test/behavior/for.zig index 4f873bbbe4..bc433c578a 100644 --- a/test/behavior/for.zig +++ b/test/behavior/for.zig @@ -112,7 +112,6 @@ test "for with null and T peer types and inferred result location type" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { fn doTheTest(slice: []const u8) !void { @@ -228,7 +227,6 @@ test "else continue outer for" { test "for loop with else branch" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; { var x = [_]u32{ 1, 2 }; diff --git a/test/behavior/if.zig b/test/behavior/if.zig index a82d9a5c61..2da6e84daf 100644 --- a/test/behavior/if.zig +++ b/test/behavior/if.zig @@ -82,7 +82,6 @@ test "const result loc, runtime if cond, else unreachable" { test "if copies its payload" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { fn doTheTest() !void { @@ -147,8 +146,6 @@ test "if-else expression with runtime condition result location is inferred opti } test "result location with inferred type ends up being pointer to comptime_int" { - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - var a: ?u32 = 1234; var b: u32 = 2000; _ = .{ &a, &b }; diff --git a/test/behavior/math.zig b/test/behavior/math.zig index cd110bc80d..ec326acc5a 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -689,6 +689,8 @@ fn testSignedWrappingEval(x: i32) !void { } test "signed negation wrapping" { + if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; + try testSignedNegationWrappingEval(minInt(i16)); try comptime testSignedNegationWrappingEval(minInt(i16)); } @@ -699,6 +701,8 @@ fn testSignedNegationWrappingEval(x: i16) !void { } test "unsigned negation wrapping" { + if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; + try testUnsignedNegationWrappingEval(1); try comptime testUnsignedNegationWrappingEval(1); } @@ -725,7 +729,6 @@ fn negateWrap(comptime T: type, x: T) T { test "unsigned 64-bit division" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch.isMIPS()) { // https://github.com/ziglang/zig/issues/16846 @@ -838,7 +841,6 @@ test "@addWithOverflow" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; try testAddWithOverflow(u8, 250, 100, 94, 1); try testAddWithOverflow(u8, 100, 150, 250, 0); @@ -927,7 +929,6 @@ fn testMulWithOverflow(comptime T: type, a: T, b: T, mul: T, bit: u1) !void { test "basic @mulWithOverflow" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; try testMulWithOverflow(u8, 86, 3, 2, 1); try testMulWithOverflow(u8, 85, 3, 255, 0); @@ -1330,6 +1331,8 @@ test "quad hex float literal parsing accurate" { } test "truncating shift left" { + if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; + try testShlTrunc(maxInt(u16)); try comptime testShlTrunc(maxInt(u16)); } diff --git a/test/behavior/null.zig b/test/behavior/null.zig index ebc390c36a..0d32f17388 100644 --- a/test/behavior/null.zig +++ b/test/behavior/null.zig @@ -188,7 +188,6 @@ test "unwrap optional which is field of global var" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; struct_with_optional.field = null; if (struct_with_optional.field) |payload| { diff --git a/test/behavior/optional.zig b/test/behavior/optional.zig index 80156d1dd6..ae9cc5ee4d 100644 --- a/test/behavior/optional.zig +++ b/test/behavior/optional.zig @@ -134,7 +134,6 @@ test "nested optional field in struct" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S2 = struct { y: u8, @@ -287,7 +286,6 @@ test "nested orelse" { test "self-referential struct through a slice of optional" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { const Node = struct { @@ -566,7 +564,6 @@ test "Optional slice passed to function" { test "peer type resolution in nested if expressions" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const Thing = struct { n: i32 }; var a = false; diff --git a/test/behavior/packed-struct.zig b/test/behavior/packed-struct.zig index 60fcd5e9f6..a6712d04ed 100644 --- a/test/behavior/packed-struct.zig +++ b/test/behavior/packed-struct.zig @@ -1096,7 +1096,6 @@ test "packed struct used as part of anon decl name" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = packed struct { a: u0 = 0 }; var a: u8 = 0; diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig index 97617a1fd1..689c725db3 100644 --- a/test/behavior/struct.zig +++ b/test/behavior/struct.zig @@ -1573,7 +1573,6 @@ test "no dependency loop on optional field wrapped in generic function" { test "optional field init with tuple" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { a: ?struct { b: u32 }, diff --git a/test/behavior/switch.zig b/test/behavior/switch.zig index 1275d0f433..4c0b4a88f4 100644 --- a/test/behavior/switch.zig +++ b/test/behavior/switch.zig @@ -516,7 +516,6 @@ test "switch with null and T peer types and inferred result location type" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { fn doTheTest(c: u8) !void { diff --git a/test/behavior/threadlocal.zig b/test/behavior/threadlocal.zig index 87daebda78..4418870149 100644 --- a/test/behavior/threadlocal.zig +++ b/test/behavior/threadlocal.zig @@ -6,7 +6,6 @@ test "thread local variable" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_llvm) switch (builtin.cpu.arch) { .x86_64, .x86 => {}, else => return error.SkipZigTest, @@ -47,7 +46,6 @@ test "reference a global threadlocal variable" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_llvm) switch (builtin.cpu.arch) { .x86_64, .x86 => {}, else => return error.SkipZigTest, diff --git a/test/behavior/while.zig b/test/behavior/while.zig index 71641ea265..fd288a9460 100644 --- a/test/behavior/while.zig +++ b/test/behavior/while.zig @@ -106,7 +106,6 @@ fn testBreakOuter() void { test "while copies its payload" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { fn doTheTest() !void { From 93e9c7a963a86f72cd7f603ee7e6d6af2ff3b0b5 Mon Sep 17 00:00:00 2001 From: David Rubin Date: Tue, 9 Jul 2024 22:53:55 -0700 Subject: [PATCH 03/27] riscv: implement `@clz` --- src/arch/riscv64/CodeGen.zig | 127 ++++++++++++++++++++-------------- src/arch/riscv64/Encoding.zig | 10 +++ src/arch/riscv64/Mir.zig | 4 ++ test/behavior/math.zig | 1 - test/tests.zig | 2 +- 5 files changed, 89 insertions(+), 55 deletions(-) diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index ad5bec87c6..56815f63b9 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -1123,6 +1123,7 @@ const required_features = [_]Target.riscv.Feature{ .a, .zicsr, .v, + .zbb, }; fn gen(func: *Func) !void { @@ -2385,20 +2386,24 @@ fn genBinOp( .mul, .mul_wrap, .rem, + .div_trunc, => { - if (!math.isPowerOfTwo(bit_size)) - return func.fail( - "TODO: genBinOp {s} non-pow 2, found {}", - .{ @tagName(tag), bit_size }, - ); - switch (tag) { .rem, + .div_trunc, => { - try func.truncateRegister(lhs_ty, lhs_reg); - try func.truncateRegister(rhs_ty, rhs_reg); + if (!math.isPowerOfTwo(bit_size)) { + try func.truncateRegister(lhs_ty, lhs_reg); + try func.truncateRegister(rhs_ty, rhs_reg); + } + }, + else => { + if (!math.isPowerOfTwo(bit_size)) + return func.fail( + "TODO: genBinOp verify {s} non-pow 2, found {}", + .{ @tagName(tag), bit_size }, + ); }, - else => {}, } switch (lhs_ty.zigTypeTag(zcu)) { @@ -2420,8 +2425,12 @@ fn genBinOp( else => unreachable, }, .rem => switch (bit_size) { - 64 => if (is_unsigned) .remu else .rem, - else => if (is_unsigned) .remuw else .remu, + 8, 16, 32 => if (is_unsigned) .remuw else .remw, + else => if (is_unsigned) .remu else .rem, + }, + .div_trunc => switch (bit_size) { + 8, 16, 32 => if (is_unsigned) .divuw else .divw, + else => if (is_unsigned) .divu else .div, }, else => unreachable, }; @@ -2455,7 +2464,7 @@ fn genBinOp( 64 => .fmuld, else => unreachable, }, - else => unreachable, + else => return func.fail("TODO: genBinOp {s} Float", .{@tagName(tag)}), }; _ = try func.addInst(.{ @@ -2588,46 +2597,6 @@ fn genBinOp( } }, - .div_trunc, - => { - if (!math.isPowerOfTwo(bit_size)) - return func.fail( - "TODO: genBinOp {s} non-pow 2, found {}", - .{ @tagName(tag), bit_size }, - ); - - const mir_tag: Mir.Inst.Tag = switch (tag) { - .div_trunc => switch (bit_size) { - 8, 16, 32 => if (is_unsigned) .divuw else .divw, - 64 => if (is_unsigned) .divu else .div, - else => unreachable, - }, - else => unreachable, - }; - - _ = try func.addInst(.{ - .tag = mir_tag, - .ops = .rrr, - .data = .{ - .r_type = .{ - .rd = dst_reg, - .rs1 = lhs_reg, - .rs2 = rhs_reg, - }, - }, - }); - - if (!is_unsigned) { - // truncate when the instruction is larger than the bit size. - switch (bit_size) { - 8, 16 => try func.truncateRegister(lhs_ty, dst_reg), - 32 => {}, // divw affects the first 32-bits - 64 => {}, // div affects the entire register - else => unreachable, - } - } - }, - .shr, .shr_exact, .shl, @@ -3740,7 +3709,59 @@ fn airGetUnionTag(func: *Func, inst: Air.Inst.Index) !void { fn airClz(func: *Func, inst: Air.Inst.Index) !void { const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else return func.fail("TODO implement airClz for {}", .{func.target.cpu.arch}); + const operand = try func.resolveInst(ty_op.operand); + const ty = func.typeOf(ty_op.operand); + + const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else result: { + const src_reg, const src_lock = try func.promoteReg(ty, operand); + defer if (src_lock) |lock| func.register_manager.unlockReg(lock); + + const dst_reg: Register = if (func.reuseOperand( + inst, + ty_op.operand, + 0, + operand, + ) and operand == .register) + operand.register + else + (try func.allocRegOrMem(func.typeOfIndex(inst), inst, true)).register; + + const bit_size = ty.bitSize(func.pt); + if (!math.isPowerOfTwo(bit_size)) try func.truncateRegister(ty, src_reg); + + if (bit_size > 64) { + return func.fail("TODO: airClz > 64 bits, found {d}", .{bit_size}); + } + + _ = try func.addInst(.{ + .tag = switch (bit_size) { + 32 => .clzw, + else => .clz, + }, + .ops = .rrr, + .data = .{ + .r_type = .{ + .rs2 = .zero, // rs2 is 0 filled in the spec + .rs1 = src_reg, + .rd = dst_reg, + }, + }, + }); + + if (!(bit_size == 32 or bit_size == 64)) { + _ = try func.addInst(.{ + .tag = .addi, + .ops = .rri, + .data = .{ .i_type = .{ + .rd = dst_reg, + .rs1 = dst_reg, + .imm12 = Immediate.s(-@as(i12, @intCast(64 - bit_size % 64))), + } }, + }); + } + + break :result .{ .register = dst_reg }; + }; return func.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } diff --git a/src/arch/riscv64/Encoding.zig b/src/arch/riscv64/Encoding.zig index 77cd905e7f..b1b0712779 100644 --- a/src/arch/riscv64/Encoding.zig +++ b/src/arch/riscv64/Encoding.zig @@ -135,6 +135,7 @@ const Enc = struct { }; }; +// TODO: this is basically a copy of the MIR table, we should be able to de-dupe them somehow. pub const Mnemonic = enum { // base mnemonics @@ -324,6 +325,10 @@ pub const Mnemonic = enum { // TODO: Q extension + // Zbb Extension + clz, + clzw, + pub fn encoding(mnem: Mnemonic) Enc { return switch (mnem) { // zig fmt: off @@ -368,6 +373,7 @@ pub const Mnemonic = enum { .srli => .{ .opcode = .OP_IMM, .data = .{ .sh = .{ .typ = 0b000000, .funct3 = 0b101, .has_5 = true } } }, .srai => .{ .opcode = .OP_IMM, .data = .{ .sh = .{ .typ = 0b010000, .funct3 = 0b101, .has_5 = true } } }, + .clz => .{ .opcode = .OP_IMM, .data = .{ .ff = .{ .funct3 = 0b001, .funct7 = 0b0110000 } } }, // OP_IMM_32 @@ -375,6 +381,7 @@ pub const Mnemonic = enum { .srliw => .{ .opcode = .OP_IMM_32, .data = .{ .sh = .{ .typ = 0b000000, .funct3 = 0b101, .has_5 = false } } }, .sraiw => .{ .opcode = .OP_IMM_32, .data = .{ .sh = .{ .typ = 0b010000, .funct3 = 0b101, .has_5 = false } } }, + .clzw => .{ .opcode = .OP_IMM_32, .data = .{ .ff = .{ .funct3 = 0b001, .funct7 = 0b0110000 } } }, // OP_32 @@ -722,6 +729,9 @@ pub const InstEnc = enum { .vadcvv, .vmvvx, .vslidedownvx, + + .clz, + .clzw, => .R, .ecall, diff --git a/src/arch/riscv64/Mir.zig b/src/arch/riscv64/Mir.zig index 78da136706..fe4e24e133 100644 --- a/src/arch/riscv64/Mir.zig +++ b/src/arch/riscv64/Mir.zig @@ -149,6 +149,10 @@ pub const Inst = struct { vfmulvv, vslidedownvx, + // Zbb Extension Instructions + clz, + clzw, + /// A pseudo-instruction. Used for anything that isn't 1:1 with an /// assembly instruction. pseudo, diff --git a/test/behavior/math.zig b/test/behavior/math.zig index ec326acc5a..17ca44c6f1 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -65,7 +65,6 @@ test "@clz" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; try testClz(); try comptime testClz(); diff --git a/test/tests.zig b/test/tests.zig index 7116233c7e..b4c720a1ff 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -439,7 +439,7 @@ const test_targets = blk: { .target = std.Target.Query.parse( .{ .arch_os_abi = "riscv64-linux-musl", - .cpu_features = "baseline+v", + .cpu_features = "baseline+v+zbb", }, ) catch @panic("OOM"), .use_llvm = false, From c78ebeb44ce078a165e3302b629432eeee6a656d Mon Sep 17 00:00:00 2001 From: David Rubin Date: Fri, 5 Jul 2024 23:00:55 -0700 Subject: [PATCH 04/27] riscv: implement `ptr_slice_ptr_ptr` just one step closer to allocation --- lib/std/builtin.zig | 1 + src/arch/riscv64/CodeGen.zig | 9 +++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index a600f055df..86f8da6cd4 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -776,6 +776,7 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace, ret_addr if (builtin.zig_backend == .stage2_riscv64) { std.debug.print("panic: {s}\n", .{msg}); + @breakpoint(); std.posix.exit(127); } diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index 56815f63b9..6ca4f37695 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -3433,8 +3433,13 @@ fn airPtrSliceLenPtr(func: *Func, inst: Air.Inst.Index) !void { fn airPtrSlicePtrPtr(func: *Func, inst: Air.Inst.Index) !void { const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else return func.fail("TODO implement ptr_slice_ptr_ptr for {}", .{func.target.cpu.arch}); - return func.finishAir(inst, result, .{ ty_op.operand, .none, .none }); + + const opt_mcv = try func.resolveInst(ty_op.operand); + const dst_mcv = if (func.reuseOperand(inst, ty_op.operand, 0, opt_mcv)) + opt_mcv + else + try func.copyToNewRegister(inst, opt_mcv); + return func.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none }); } fn airSliceElemVal(func: *Func, inst: Air.Inst.Index) !void { From d3f75522d7e18188748558c2ae20928b83d173bf Mon Sep 17 00:00:00 2001 From: David Rubin Date: Mon, 15 Jul 2024 01:50:35 -0700 Subject: [PATCH 05/27] lower: fix logic bug in `cmp_gt` --- src/arch/riscv64/Lower.zig | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/arch/riscv64/Lower.zig b/src/arch/riscv64/Lower.zig index 012f520485..f33fe68fae 100644 --- a/src/arch/riscv64/Lower.zig +++ b/src/arch/riscv64/Lower.zig @@ -304,7 +304,6 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index, options: struct { }; const is_unsigned = ty.isUnsignedInt(pt.zcu); - const less_than: Encoding.Mnemonic = if (is_unsigned) .sltu else .slt; switch (class) { @@ -338,8 +337,8 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index, options: struct { .gt => { try lower.emit(less_than, &.{ .{ .reg = rd }, - .{ .reg = rs1 }, .{ .reg = rs2 }, + .{ .reg = rs1 }, }); }, .gte => { @@ -348,7 +347,6 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index, options: struct { .{ .reg = rs1 }, .{ .reg = rs2 }, }); - try lower.emit(.xori, &.{ .{ .reg = rd }, .{ .reg = rd }, From ba58b7b88143f93f671a53a4809c9724de898b8d Mon Sep 17 00:00:00 2001 From: David Rubin Date: Mon, 15 Jul 2024 01:52:08 -0700 Subject: [PATCH 06/27] heap: create a work-around page-allocator the risc-v backend doesn't have `@cmpxchg*` implemented and so it can't use the hint that the current page-allocator uses. this work-around branch can be removed when I implement the atomic built-in. --- lib/std/heap/PageAllocator.zig | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/lib/std/heap/PageAllocator.zig b/lib/std/heap/PageAllocator.zig index 4188c25528..92c8ca6b28 100644 --- a/lib/std/heap/PageAllocator.zig +++ b/lib/std/heap/PageAllocator.zig @@ -34,6 +34,20 @@ fn alloc(_: *anyopaque, n: usize, log2_align: u8, ra: usize) ?[*]u8 { return @ptrCast(addr); } + if (builtin.zig_backend == .stage2_riscv64) { + const aligned_len = mem.alignForward(usize, n, mem.page_size); + const slice = posix.mmap( + null, + aligned_len, + posix.PROT.READ | posix.PROT.WRITE, + .{ .TYPE = .PRIVATE, .ANONYMOUS = true }, + -1, + 0, + ) catch return null; + assert(mem.isAligned(@intFromPtr(slice.ptr), mem.page_size)); + return slice.ptr; + } + const aligned_len = mem.alignForward(usize, n, mem.page_size); const hint = @atomicLoad(@TypeOf(std.heap.next_mmap_addr_hint), &std.heap.next_mmap_addr_hint, .unordered); const slice = posix.mmap( From cde6956b2128d9c28b00e0e25e27346abd1c3a88 Mon Sep 17 00:00:00 2001 From: David Rubin Date: Mon, 15 Jul 2024 02:34:40 -0700 Subject: [PATCH 07/27] riscv: remove redundant assert in `genBinOp` --- src/arch/riscv64/CodeGen.zig | 1 - test/behavior/byteswap.zig | 1 - test/behavior/cast.zig | 1 - test/behavior/floatop.zig | 1 - test/behavior/generics.zig | 3 --- test/behavior/globals.zig | 1 - test/behavior/pointers.zig | 2 -- test/behavior/src.zig | 1 - test/behavior/string_literals.zig | 1 - test/behavior/struct.zig | 1 - test/behavior/vector.zig | 4 ---- 11 files changed, 17 deletions(-) diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index 6ca4f37695..34c782f66b 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -2368,7 +2368,6 @@ fn genBinOp( const pt = func.pt; const zcu = pt.zcu; const bit_size = lhs_ty.bitSize(pt); - assert(bit_size <= 64); const is_unsigned = lhs_ty.isUnsignedInt(zcu); diff --git a/test/behavior/byteswap.zig b/test/behavior/byteswap.zig index 0c6e655b25..fd7e2af850 100644 --- a/test/behavior/byteswap.zig +++ b/test/behavior/byteswap.zig @@ -100,7 +100,6 @@ test "@byteSwap vectors u8" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; try comptime vector8(); try vector8(); diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index 816fa02f5b..44fcb08cf3 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -1983,7 +1983,6 @@ test "peer type resolution: vector and array and tuple" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; var vec: @Vector(2, i8) = .{ 10, 20 }; var arr: [2]i8 = .{ 30, 40 }; diff --git a/test/behavior/floatop.zig b/test/behavior/floatop.zig index f644465d63..f9f2579b0a 100644 --- a/test/behavior/floatop.zig +++ b/test/behavior/floatop.zig @@ -134,7 +134,6 @@ test "cmp f16" { test "cmp f32/f64" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; try testCmp(f32); try comptime testCmp(f32); diff --git a/test/behavior/generics.zig b/test/behavior/generics.zig index 6bd627dfe3..2a40123259 100644 --- a/test/behavior/generics.zig +++ b/test/behavior/generics.zig @@ -19,7 +19,6 @@ fn checkSize(comptime T: type) usize { test "simple generic fn" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; try expect(max(i32, 3, -1) == 3); try expect(max(u8, 1, 100) == 100); @@ -56,7 +55,6 @@ test "fn with comptime args" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; try expect(gimmeTheBigOne(1234, 5678) == 5678); try expect(shouldCallSameInstance(34, 12) == 34); @@ -67,7 +65,6 @@ test "anytype params" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; try expect(max_i32(12, 34) == 34); try expect(max_f64(1.2, 3.4) == 3.4); diff --git a/test/behavior/globals.zig b/test/behavior/globals.zig index f7a23b725f..89dc20c5c7 100644 --- a/test/behavior/globals.zig +++ b/test/behavior/globals.zig @@ -18,7 +18,6 @@ test "store to global vector" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; try expect(vpos[1] == 0.0); vpos = @Vector(2, f32){ 0.0, 1.0 }; diff --git a/test/behavior/pointers.zig b/test/behavior/pointers.zig index 36152cf81a..d8a773f69c 100644 --- a/test/behavior/pointers.zig +++ b/test/behavior/pointers.zig @@ -125,7 +125,6 @@ test "initialize const optional C pointer to null" { test "assigning integer to C pointer" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; var x: i32 = 0; var y: i32 = 1; @@ -143,7 +142,6 @@ test "assigning integer to C pointer" { test "C pointer comparison and arithmetic" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { fn doTheTest() !void { diff --git a/test/behavior/src.zig b/test/behavior/src.zig index 7c2b377d5b..f0455bdd2e 100644 --- a/test/behavior/src.zig +++ b/test/behavior/src.zig @@ -17,7 +17,6 @@ test "@src" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; try doTheTest(); } diff --git a/test/behavior/string_literals.zig b/test/behavior/string_literals.zig index a45403af97..8a0a105228 100644 --- a/test/behavior/string_literals.zig +++ b/test/behavior/string_literals.zig @@ -65,7 +65,6 @@ fn testFnForSrc() std.builtin.SourceLocation { test "@src() returns a struct containing 0-terminated string slices" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const src = testFnForSrc(); try std.testing.expect([:0]const u8 == @TypeOf(src.file)); diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig index 689c725db3..38bbd4b0c4 100644 --- a/test/behavior/struct.zig +++ b/test/behavior/struct.zig @@ -1587,7 +1587,6 @@ test "optional field init with tuple" { test "if inside struct init inside if" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const MyStruct = struct { x: u32 }; const b: u32 = 5; diff --git a/test/behavior/vector.zig b/test/behavior/vector.zig index d4b2acd6af..5ae6e400ff 100644 --- a/test/behavior/vector.zig +++ b/test/behavior/vector.zig @@ -445,7 +445,6 @@ test "load vector elements via runtime index" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { fn doTheTest() !void { @@ -1240,7 +1239,6 @@ test "loading the second vector from a slice of vectors" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; @setRuntimeSafety(false); var small_bases = [2]@Vector(2, u8){ @@ -1327,7 +1325,6 @@ test "zero multiplicand" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const zeros = @Vector(2, u32){ 0.0, 0.0 }; var ones = @Vector(2, u32){ 1.0, 1.0 }; @@ -1488,7 +1485,6 @@ test "store vector with memset" { test "addition of vectors represented as strings" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const V = @Vector(3, u8); const foo: V = "foo".*; From 81ca3a1d594cb25dcd7dbedf175dd9931cad0d0f Mon Sep 17 00:00:00 2001 From: David Rubin Date: Mon, 15 Jul 2024 03:37:06 -0700 Subject: [PATCH 08/27] riscv: fix logic bug in `ptr_elem_ptr` I was doing duplicate work with `elemOffset` multiplying by the abi size and then the `ptr_add` `genBinOp` also multiplying. This led to having writes happening in the wrong place. --- src/arch/riscv64/CodeGen.zig | 18 ++++++------------ test/behavior/array.zig | 12 ------------ test/behavior/basic.zig | 1 - test/behavior/bit_shifting.zig | 1 - test/behavior/pointers.zig | 1 - test/behavior/slice.zig | 2 -- test/behavior/union.zig | 2 -- test/behavior/vector.zig | 4 ---- 8 files changed, 6 insertions(+), 35 deletions(-) diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index 34c782f66b..beff7ec879 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -3620,15 +3620,13 @@ fn airPtrElemPtr(func: *Func, inst: Air.Inst.Index) !void { defer if (base_ptr_lock) |lock| func.register_manager.unlockReg(lock); if (elem_ptr_ty.ptrInfo(zcu).flags.vector_index != .none) { - break :result if (func.reuseOperand(inst, extra.lhs, 0, base_ptr_mcv)) - base_ptr_mcv - else - try func.copyToNewRegister(inst, base_ptr_mcv); + // break :result if (func.reuseOperand(inst, extra.lhs, 0, base_ptr_mcv)) + // base_ptr_mcv + // else + // try func.copyToNewRegister(inst, base_ptr_mcv); + @panic("audit"); } - const elem_ty = base_ptr_ty.elemType2(zcu); - const elem_abi_size = elem_ty.abiSize(pt); - const index_ty = func.typeOf(extra.rhs); const index_mcv = try func.resolveInst(extra.rhs); const index_lock: ?RegisterLock = switch (index_mcv) { .register => |reg| func.register_manager.lockRegAssumeUnused(reg), @@ -3636,10 +3634,6 @@ fn airPtrElemPtr(func: *Func, inst: Air.Inst.Index) !void { }; defer if (index_lock) |lock| func.register_manager.unlockReg(lock); - const offset_reg = try func.elemOffset(index_ty, index_mcv, elem_abi_size); - const offset_reg_lock = func.register_manager.lockRegAssumeUnused(offset_reg); - defer func.register_manager.unlockReg(offset_reg_lock); - const result_reg, const result_lock = try func.allocReg(.int); defer func.register_manager.unlockReg(result_lock); @@ -3647,7 +3641,7 @@ fn airPtrElemPtr(func: *Func, inst: Air.Inst.Index) !void { .ptr_add, base_ptr_mcv, base_ptr_ty, - .{ .register = offset_reg }, + index_mcv, Type.usize, result_reg, ); diff --git a/test/behavior/array.zig b/test/behavior/array.zig index f6d59ae0fa..4520fdde68 100644 --- a/test/behavior/array.zig +++ b/test/behavior/array.zig @@ -22,7 +22,6 @@ test "arrays" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; var array: [5]u32 = undefined; @@ -614,7 +613,6 @@ test "type coercion of pointer to anon struct literal to pointer to array" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { const U = union { @@ -667,7 +665,6 @@ test "array init of container level array variable" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { var pair: [2]usize = .{ 1, 2 }; @@ -802,8 +799,6 @@ test "runtime side-effects in comptime-known array init" { } test "slice initialized through reference to anonymous array init provides result types" { - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - var my_u32: u32 = 123; var my_u64: u64 = 456; _ = .{ &my_u32, &my_u64 }; @@ -817,8 +812,6 @@ test "slice initialized through reference to anonymous array init provides resul } test "sentinel-terminated slice initialized through reference to anonymous array init provides result types" { - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - var my_u32: u32 = 123; var my_u64: u64 = 456; _ = .{ &my_u32, &my_u64 }; @@ -869,8 +862,6 @@ test "many-item sentinel-terminated pointer initialized through reference to ano } test "pointer to array initialized through reference to anonymous array init provides result types" { - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - var my_u32: u32 = 123; var my_u64: u64 = 456; _ = .{ &my_u32, &my_u64 }; @@ -884,8 +875,6 @@ test "pointer to array initialized through reference to anonymous array init pro } test "pointer to sentinel-terminated array initialized through reference to anonymous array init provides result types" { - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - var my_u32: u32 = 123; var my_u64: u64 = 456; _ = .{ &my_u32, &my_u64 }; @@ -912,7 +901,6 @@ test "copied array element doesn't alias source" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; var x: [10][10]u32 = undefined; diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig index 494240c63a..bd942bab49 100644 --- a/test/behavior/basic.zig +++ b/test/behavior/basic.zig @@ -620,7 +620,6 @@ var global_ptr = &gdt[0]; test "global constant is loaded with a runtime-known index" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { fn doTheTest() !void { diff --git a/test/behavior/bit_shifting.zig b/test/behavior/bit_shifting.zig index 9d203dcfe3..e2b63b622b 100644 --- a/test/behavior/bit_shifting.zig +++ b/test/behavior/bit_shifting.zig @@ -65,7 +65,6 @@ test "sharded table" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // realistic 16-way sharding try testShardedTable(u32, 4, 8); diff --git a/test/behavior/pointers.zig b/test/behavior/pointers.zig index d8a773f69c..1cc9092fd7 100644 --- a/test/behavior/pointers.zig +++ b/test/behavior/pointers.zig @@ -515,7 +515,6 @@ test "element pointer to slice" { test "element pointer arithmetic to slice" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { fn doTheTest() !void { diff --git a/test/behavior/slice.zig b/test/behavior/slice.zig index e1576ca302..f0231bfbb7 100644 --- a/test/behavior/slice.zig +++ b/test/behavior/slice.zig @@ -30,7 +30,6 @@ comptime { test "slicing" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; var array: [20]i32 = undefined; @@ -256,7 +255,6 @@ test "C pointer slice access" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; var buf: [10]u32 = [1]u32{42} ** 10; const c_ptr = @as([*c]const u32, @ptrCast(&buf)); diff --git a/test/behavior/union.zig b/test/behavior/union.zig index 413362caba..f6d37db439 100644 --- a/test/behavior/union.zig +++ b/test/behavior/union.zig @@ -1700,7 +1700,6 @@ test "packed union field pointer has correct alignment" { test "union with 128 bit integer" { if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const ValueTag = enum { int, other }; @@ -2196,7 +2195,6 @@ test "copied union field doesn't alias source" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const U = union(enum) { array: [10]u32, diff --git a/test/behavior/vector.zig b/test/behavior/vector.zig index 5ae6e400ff..bc38650f1e 100644 --- a/test/behavior/vector.zig +++ b/test/behavior/vector.zig @@ -395,7 +395,6 @@ test "load vector elements via comptime index" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { fn doTheTest() !void { @@ -417,7 +416,6 @@ test "store vector elements via comptime index" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { fn doTheTest() !void { @@ -467,7 +465,6 @@ test "store vector elements via runtime index" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { fn doTheTest() !void { @@ -1511,7 +1508,6 @@ test "vector pointer is indexable" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const V = @Vector(2, u32); From 1820f4410450a0dfa35eafefa217465f762e26fd Mon Sep 17 00:00:00 2001 From: David Rubin Date: Mon, 15 Jul 2024 04:01:44 -0700 Subject: [PATCH 09/27] riscv: implement sub-byte addition --- src/arch/riscv64/CodeGen.zig | 82 +++++++++++++++++++++++++++++---- test/behavior/array.zig | 2 - test/behavior/packed-struct.zig | 1 - test/behavior/struct.zig | 2 - 4 files changed, 73 insertions(+), 14 deletions(-) diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index beff7ec879..9e5412fbc9 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -2757,12 +2757,14 @@ fn airAddWithOverflow(func: *Func, inst: Air.Inst.Index) !void { const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const extra = func.air.extraData(Air.Bin, ty_pl.payload).data; + const rhs_ty = func.typeOf(extra.rhs); + const lhs_ty = func.typeOf(extra.lhs); + const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else result: { - const ty = func.typeOf(extra.lhs); - switch (ty.zigTypeTag(zcu)) { + switch (lhs_ty.zigTypeTag(zcu)) { .Vector => return func.fail("TODO implement add with overflow for Vector type", .{}), .Int => { - const int_info = ty.intInfo(zcu); + const int_info = lhs_ty.intInfo(zcu); const tuple_ty = func.typeOfIndex(inst); const result_mcv = try func.allocRegOrMem(tuple_ty, inst, false); @@ -2774,11 +2776,11 @@ fn airAddWithOverflow(func: *Func, inst: Air.Inst.Index) !void { try func.genSetMem( .{ .frame = offset.index }, offset.off + @as(i32, @intCast(tuple_ty.structFieldOffset(0, pt))), - ty, + lhs_ty, add_result, ); - const trunc_reg = try func.copyToTmpRegister(ty, add_result); + const trunc_reg = try func.copyToTmpRegister(lhs_ty, add_result); const trunc_reg_lock = func.register_manager.lockRegAssumeUnused(trunc_reg); defer func.register_manager.unlockReg(trunc_reg_lock); @@ -2787,13 +2789,13 @@ fn airAddWithOverflow(func: *Func, inst: Air.Inst.Index) !void { // if the result isn't equal after truncating it to the given type, // an overflow must have happened. - try func.truncateRegister(func.typeOf(extra.lhs), trunc_reg); + try func.truncateRegister(lhs_ty, trunc_reg); try func.genBinOp( .cmp_neq, add_result, - ty, + lhs_ty, .{ .register = trunc_reg }, - ty, + rhs_ty, overflow_reg, ); @@ -2806,7 +2808,69 @@ fn airAddWithOverflow(func: *Func, inst: Air.Inst.Index) !void { break :result result_mcv; } else { - return func.fail("TODO: less than 8 bit or non-pow 2 addition", .{}); + const rhs_mcv = try func.resolveInst(extra.rhs); + const lhs_mcv = try func.resolveInst(extra.lhs); + + const rhs_reg, const rhs_lock = try func.promoteReg(rhs_ty, rhs_mcv); + const lhs_reg, const lhs_lock = try func.promoteReg(lhs_ty, lhs_mcv); + defer { + if (rhs_lock) |lock| func.register_manager.unlockReg(lock); + if (lhs_lock) |lock| func.register_manager.unlockReg(lock); + } + + try func.truncateRegister(rhs_ty, rhs_reg); + try func.truncateRegister(lhs_ty, lhs_reg); + + const dest_reg, const dest_lock = try func.allocReg(.int); + defer func.register_manager.unlockReg(dest_lock); + + _ = try func.addInst(.{ + .tag = .add, + .ops = .rrr, + .data = .{ .r_type = .{ + .rs1 = rhs_reg, + .rs2 = lhs_reg, + .rd = dest_reg, + } }, + }); + + try func.truncateRegister(func.typeOfIndex(inst), dest_reg); + const add_result: MCValue = .{ .register = dest_reg }; + + try func.genSetMem( + .{ .frame = offset.index }, + offset.off + @as(i32, @intCast(tuple_ty.structFieldOffset(0, pt))), + lhs_ty, + add_result, + ); + + const trunc_reg = try func.copyToTmpRegister(lhs_ty, add_result); + const trunc_reg_lock = func.register_manager.lockRegAssumeUnused(trunc_reg); + defer func.register_manager.unlockReg(trunc_reg_lock); + + const overflow_reg, const overflow_lock = try func.allocReg(.int); + defer func.register_manager.unlockReg(overflow_lock); + + // if the result isn't equal after truncating it to the given type, + // an overflow must have happened. + try func.truncateRegister(lhs_ty, trunc_reg); + try func.genBinOp( + .cmp_neq, + add_result, + lhs_ty, + .{ .register = trunc_reg }, + rhs_ty, + overflow_reg, + ); + + try func.genSetMem( + .{ .frame = offset.index }, + offset.off + @as(i32, @intCast(tuple_ty.structFieldOffset(1, pt))), + Type.u1, + .{ .register = overflow_reg }, + ); + + break :result result_mcv; } }, else => unreachable, diff --git a/test/behavior/array.zig b/test/behavior/array.zig index 4520fdde68..05da574edf 100644 --- a/test/behavior/array.zig +++ b/test/behavior/array.zig @@ -773,8 +773,6 @@ test "array init with no result pointer sets field result types" { } test "runtime side-effects in comptime-known array init" { - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - var side_effects: u4 = 0; const init = [4]u4{ blk: { diff --git a/test/behavior/packed-struct.zig b/test/behavior/packed-struct.zig index a6712d04ed..5b449ab8fc 100644 --- a/test/behavior/packed-struct.zig +++ b/test/behavior/packed-struct.zig @@ -1274,7 +1274,6 @@ test "2-byte packed struct argument in C calling convention" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = packed struct(u16) { x: u15 = 0, diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig index 38bbd4b0c4..7fdf7b489a 100644 --- a/test/behavior/struct.zig +++ b/test/behavior/struct.zig @@ -1768,8 +1768,6 @@ test "struct init with no result pointer sets field result types" { } test "runtime side-effects in comptime-known struct init" { - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - var side_effects: u4 = 0; const S = struct { a: u4, b: u4, c: u4, d: u4 }; const init = S{ From 6ac1b2d82ae5ea1067117031d52799bf86a5b2b8 Mon Sep 17 00:00:00 2001 From: David Rubin Date: Tue, 16 Jul 2024 01:48:55 -0700 Subject: [PATCH 10/27] riscv: add 32-bit support to integer `@abs` --- src/arch/riscv64/CodeGen.zig | 122 ++++++++++++++++++++--------------- 1 file changed, 71 insertions(+), 51 deletions(-) diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index 9e5412fbc9..e77746d83e 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -2685,60 +2685,65 @@ fn genBinOp( // a1, s0 was -1, flipping all the bits in a2 and effectively restoring a0. If a0 was greater than or equal to a1, // s0 was 0, leaving a2 unchanged as a0. .min, .max => { - const int_info = lhs_ty.intInfo(zcu); + switch (lhs_ty.zigTypeTag(zcu)) { + .Int => { + const int_info = lhs_ty.intInfo(zcu); - const mask_reg, const mask_lock = try func.allocReg(.int); - defer func.register_manager.unlockReg(mask_lock); + const mask_reg, const mask_lock = try func.allocReg(.int); + defer func.register_manager.unlockReg(mask_lock); - _ = try func.addInst(.{ - .tag = if (int_info.signedness == .unsigned) .sltu else .slt, - .ops = .rrr, - .data = .{ .r_type = .{ - .rd = mask_reg, - .rs1 = lhs_reg, - .rs2 = rhs_reg, - } }, - }); + _ = try func.addInst(.{ + .tag = if (int_info.signedness == .unsigned) .sltu else .slt, + .ops = .rrr, + .data = .{ .r_type = .{ + .rd = mask_reg, + .rs1 = lhs_reg, + .rs2 = rhs_reg, + } }, + }); - _ = try func.addInst(.{ - .tag = .sub, - .ops = .rrr, - .data = .{ .r_type = .{ - .rd = mask_reg, - .rs1 = .zero, - .rs2 = mask_reg, - } }, - }); + _ = try func.addInst(.{ + .tag = .sub, + .ops = .rrr, + .data = .{ .r_type = .{ + .rd = mask_reg, + .rs1 = .zero, + .rs2 = mask_reg, + } }, + }); - _ = try func.addInst(.{ - .tag = .xor, - .ops = .rrr, - .data = .{ .r_type = .{ - .rd = dst_reg, - .rs1 = lhs_reg, - .rs2 = rhs_reg, - } }, - }); + _ = try func.addInst(.{ + .tag = .xor, + .ops = .rrr, + .data = .{ .r_type = .{ + .rd = dst_reg, + .rs1 = lhs_reg, + .rs2 = rhs_reg, + } }, + }); - _ = try func.addInst(.{ - .tag = .@"and", - .ops = .rrr, - .data = .{ .r_type = .{ - .rd = mask_reg, - .rs1 = dst_reg, - .rs2 = mask_reg, - } }, - }); + _ = try func.addInst(.{ + .tag = .@"and", + .ops = .rrr, + .data = .{ .r_type = .{ + .rd = mask_reg, + .rs1 = dst_reg, + .rs2 = mask_reg, + } }, + }); - _ = try func.addInst(.{ - .tag = .xor, - .ops = .rrr, - .data = .{ .r_type = .{ - .rd = dst_reg, - .rs1 = if (tag == .min) rhs_reg else lhs_reg, - .rs2 = mask_reg, - } }, - }); + _ = try func.addInst(.{ + .tag = .xor, + .ops = .rrr, + .data = .{ .r_type = .{ + .rd = dst_reg, + .rs1 = if (tag == .min) rhs_reg else lhs_reg, + .rs2 = mask_reg, + } }, + }); + }, + else => |t| return func.fail("TODO: genBinOp min/max for {s}", .{@tagName(t)}), + } }, else => return func.fail("TODO: genBinOp {}", .{tag}), } @@ -3852,6 +3857,13 @@ fn airAbs(func: *Func, inst: Air.Inst.Index) !void { .Int => if (ty.zigTypeTag(zcu) == .Vector) { return func.fail("TODO implement airAbs for {}", .{ty.fmt(pt)}); } else { + const int_info = scalar_ty.intInfo(zcu); + const int_bits = int_info.bits; + switch (int_bits) { + 32, 64 => {}, + else => return func.fail("TODO: airAbs Int size {d}", .{int_bits}), + } + const return_mcv = try func.copyToNewRegister(inst, operand); const operand_reg = return_mcv.register; @@ -3859,12 +3871,16 @@ fn airAbs(func: *Func, inst: Air.Inst.Index) !void { defer func.register_manager.unlockReg(temp_lock); _ = try func.addInst(.{ - .tag = .srai, + .tag = switch (int_bits) { + 32 => .sraiw, + 64 => .srai, + else => unreachable, + }, .ops = .rri, .data = .{ .i_type = .{ .rd = temp_reg, .rs1 = operand_reg, - .imm12 = Immediate.u(63), + .imm12 = Immediate.u(int_bits - 1), } }, }); @@ -3879,7 +3895,11 @@ fn airAbs(func: *Func, inst: Air.Inst.Index) !void { }); _ = try func.addInst(.{ - .tag = .sub, + .tag = switch (int_bits) { + 32 => .subw, + 64 => .sub, + else => unreachable, + }, .ops = .rrr, .data = .{ .r_type = .{ .rd = operand_reg, From 45443f9c37101da1721ccbef5a29b7536f7db3a5 Mon Sep 17 00:00:00 2001 From: David Rubin Date: Sat, 20 Jul 2024 03:41:22 -0700 Subject: [PATCH 11/27] cmake: update to reflect added riscv64 backend files --- CMakeLists.txt | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f9436d7c2a..2e27935d3d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -535,11 +535,14 @@ set(ZIG_STAGE2_SOURCES src/arch/arm/Mir.zig src/arch/arm/abi.zig src/arch/arm/bits.zig - src/arch/riscv64/CodeGen.zig - src/arch/riscv64/Emit.zig - src/arch/riscv64/Mir.zig src/arch/riscv64/abi.zig src/arch/riscv64/bits.zig + src/arch/riscv64/CodeGen.zig + src/arch/riscv64/Emit.zig + src/arch/riscv64/encoder.zig + src/arch/riscv64/Encoding.zig + src/arch/riscv64/Lower.zig + src/arch/riscv64/Mir.zig src/arch/sparc64/CodeGen.zig src/arch/sparc64/Emit.zig src/arch/sparc64/Mir.zig From 64c6473443722c933ece642b0c2cfbd74b2638bd Mon Sep 17 00:00:00 2001 From: David Rubin Date: Sun, 21 Jul 2024 01:36:26 -0700 Subject: [PATCH 12/27] riscv: implement `add_sat` and `ptr_slice_len_ptr` this is enough to use the basic functions of an ArrayList! --- src/arch/riscv64/CodeGen.zig | 128 +++++++++++++++++++++++++++++++---- 1 file changed, 115 insertions(+), 13 deletions(-) diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index e77746d83e..a509b799e4 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -1321,6 +1321,8 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void { .sub, .sub_wrap, + .add_sat, + .mul, .mul_wrap, .div_trunc, @@ -1372,7 +1374,6 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void { .shl_with_overflow => try func.airShlWithOverflow(inst), - .add_sat => try func.airAddSat(inst), .sub_sat => try func.airSubSat(inst), .mul_sat => try func.airMulSat(inst), .shl_sat => try func.airShlSat(inst), @@ -1578,7 +1579,7 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void { for (tracking.getRegs()) |reg| { if (RegisterManager.indexOfRegIntoTracked(reg).? == index) break; } else return std.debug.panic( - \\%{} takes up these regs: {any}, however these regs {any}, don't use it + \\%{} takes up these regs: {any}, however this regs {any}, don't use it , .{ tracked_inst, tracking.getRegs(), RegisterManager.regAtTrackedIndex(@intCast(index)) }); } } @@ -2532,6 +2533,57 @@ fn genBinOp( } }, + .add_sat, + => { + if (bit_size != 64 or !is_unsigned) + return func.fail("TODO: genBinOp ty: {}", .{lhs_ty.fmt(pt)}); + + const tmp_reg = try func.copyToTmpRegister(rhs_ty, .{ .register = rhs_reg }); + const tmp_lock = func.register_manager.lockRegAssumeUnused(tmp_reg); + defer func.register_manager.unlockReg(tmp_lock); + + _ = try func.addInst(.{ + .tag = .add, + .ops = .rrr, + .data = .{ .r_type = .{ + .rd = tmp_reg, + .rs1 = rhs_reg, + .rs2 = lhs_reg, + } }, + }); + + _ = try func.addInst(.{ + .tag = .sltu, + .ops = .rrr, + .data = .{ .r_type = .{ + .rd = dst_reg, + .rs1 = tmp_reg, + .rs2 = lhs_reg, + } }, + }); + + // neg dst_reg, dst_reg + _ = try func.addInst(.{ + .tag = .sub, + .ops = .rrr, + .data = .{ .r_type = .{ + .rd = dst_reg, + .rs1 = .zero, + .rs2 = dst_reg, + } }, + }); + + _ = try func.addInst(.{ + .tag = .@"or", + .ops = .rrr, + .data = .{ .r_type = .{ + .rd = dst_reg, + .rs1 = dst_reg, + .rs2 = tmp_reg, + } }, + }); + }, + .ptr_add, .ptr_sub, => { @@ -3096,12 +3148,6 @@ fn airShlWithOverflow(func: *Func, inst: Air.Inst.Index) !void { return func.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } -fn airAddSat(func: *Func, inst: Air.Inst.Index) !void { - const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; - const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else return func.fail("TODO implement airAddSat", .{}); - return func.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); -} - fn airSubSat(func: *Func, inst: Air.Inst.Index) !void { const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else return func.fail("TODO implement airSubSat", .{}); @@ -3495,7 +3541,16 @@ fn airSliceLen(func: *Func, inst: Air.Inst.Index) !void { fn airPtrSliceLenPtr(func: *Func, inst: Air.Inst.Index) !void { const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else return func.fail("TODO implement ptr_slice_len_ptr for {}", .{func.target.cpu.arch}); + const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else result: { + const src_mcv = try func.resolveInst(ty_op.operand); + + const dst_reg, const dst_lock = try func.allocReg(.int); + defer func.register_manager.unlockReg(dst_lock); + const dst_mcv: MCValue = .{ .register = dst_reg }; + + try func.genCopy(Type.u64, dst_mcv, src_mcv.offset(8)); + break :result dst_mcv; + }; return func.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } @@ -5838,6 +5893,17 @@ fn genInlineMemcpy( try func.genSetReg(Type.usize, src, src_ptr); try func.genSetReg(Type.usize, dst, dst_ptr); + // if count is 0, there's nothing to copy + _ = try func.addInst(.{ + .tag = .beq, + .ops = .rr_inst, + .data = .{ .b_type = .{ + .rs1 = count, + .rs2 = .zero, + .inst = @intCast(func.mir_instructions.len + 9), + } }, + }); + // lb tmp, 0(src) const first_inst = try func.addInst(.{ .tag = .lb, @@ -6145,7 +6211,9 @@ fn genSetReg(func: *Func, ty: Type, reg: Register, src_mcv: MCValue) InnerError! } }, }); }, - .register_pair => return func.fail("genSetReg should we allow reg -> reg_pair?", .{}), + // useful in cases like slice_ptr, which can easily reuse the operand + // but we need to get only the pointer out. + .register_pair => |pair| try func.genSetReg(ty, reg, .{ .register = pair[0] }), .load_frame => |frame| { if (reg.class() == .vector) { // vectors don't support an offset memory load so we need to put the true @@ -6507,7 +6575,7 @@ fn airBitCast(func: *Func, inst: Air.Inst.Index) !void { const src_lock = if (src_mcv.getReg()) |reg| func.register_manager.lockReg(reg) else null; defer if (src_lock) |lock| func.register_manager.unlockReg(lock); - const dst_mcv = if (dst_ty.abiSize(pt) <= src_ty.abiSize(pt) and + const dst_mcv = if (dst_ty.abiSize(pt) <= src_ty.abiSize(pt) and src_mcv != .register_pair and func.reuseOperand(inst, ty_op.operand, 0, src_mcv)) src_mcv else dst: { const dst_mcv = try func.allocRegOrMem(dst_ty, inst, true); try func.genCopy(switch (math.order(dst_ty.abiSize(pt), src_ty.abiSize(pt))) { @@ -6827,8 +6895,42 @@ fn airMemset(func: *Func, inst: Air.Inst.Index, safety: bool) !void { } fn airMemcpy(func: *Func, inst: Air.Inst.Index) !void { - _ = inst; - return func.fail("TODO implement airMemcpy for {}", .{func.target.cpu.arch}); + const pt = func.pt; + const zcu = pt.zcu; + const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; + + const dst_ptr = try func.resolveInst(bin_op.lhs); + const src_ptr = try func.resolveInst(bin_op.rhs); + + const dst_ty = func.typeOf(bin_op.lhs); + + const len_mcv: MCValue = switch (dst_ty.ptrSize(zcu)) { + .Slice => len: { + const len_reg, const len_lock = try func.allocReg(.int); + defer func.register_manager.unlockReg(len_lock); + + const elem_size = dst_ty.childType(zcu).abiSize(pt); + try func.genBinOp( + .mul, + .{ .immediate = elem_size }, + Type.u64, + dst_ptr.address().offset(8).deref(), + Type.u64, + len_reg, + ); + break :len .{ .register = len_reg }; + }, + else => |size| return func.fail("TODO: airMemcpy size {s}", .{@tagName(size)}), + }; + const len_lock: ?RegisterLock = switch (len_mcv) { + .register => |reg| func.register_manager.lockRegAssumeUnused(reg), + else => null, + }; + defer if (len_lock) |lock| func.register_manager.unlockReg(lock); + + try func.genInlineMemcpy(dst_ptr, src_ptr, len_mcv); + + return func.finishAir(inst, .unreach, .{ bin_op.lhs, bin_op.rhs, .none }); } fn airTagName(func: *Func, inst: Air.Inst.Index) !void { From 9bc7e8c85293fd737e7ca3fb2f783618069b6e61 Mon Sep 17 00:00:00 2001 From: David Rubin Date: Sun, 21 Jul 2024 02:00:05 -0700 Subject: [PATCH 13/27] riscv: update tests --- test/behavior/abs.zig | 1 + test/behavior/call.zig | 1 - test/behavior/generics.zig | 1 - test/behavior/memcpy.zig | 1 - test/behavior/struct.zig | 1 - test/behavior/tuple.zig | 4 ---- 6 files changed, 1 insertion(+), 8 deletions(-) diff --git a/test/behavior/abs.zig b/test/behavior/abs.zig index 980d9446ef..802b755c4c 100644 --- a/test/behavior/abs.zig +++ b/test/behavior/abs.zig @@ -6,6 +6,7 @@ test "@abs integers" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; try comptime testAbsIntegers(); try testAbsIntegers(); diff --git a/test/behavior/call.zig b/test/behavior/call.zig index b89ffbaef4..4a6e369918 100644 --- a/test/behavior/call.zig +++ b/test/behavior/call.zig @@ -342,7 +342,6 @@ test "inline call preserves tail call" { test "inline call doesn't re-evaluate non generic struct" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { fn foo(f: struct { a: u8, b: u8 }) !void { diff --git a/test/behavior/generics.zig b/test/behavior/generics.zig index 2a40123259..712df05011 100644 --- a/test/behavior/generics.zig +++ b/test/behavior/generics.zig @@ -500,7 +500,6 @@ test "union in struct captures argument" { test "function argument tuple used as struct field" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { fn DeleagateWithContext(comptime Function: type) type { diff --git a/test/behavior/memcpy.zig b/test/behavior/memcpy.zig index a571b1e2f7..c570f28ab7 100644 --- a/test/behavior/memcpy.zig +++ b/test/behavior/memcpy.zig @@ -50,7 +50,6 @@ test "@memcpy dest many pointer" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; try testMemcpyDestManyPtr(); try comptime testMemcpyDestManyPtr(); diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig index 7fdf7b489a..f84404c1e3 100644 --- a/test/behavior/struct.zig +++ b/test/behavior/struct.zig @@ -1133,7 +1133,6 @@ test "packed struct with undefined initializers" { test "for loop over pointers to struct, getting field from struct pointer" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { const Foo = struct { diff --git a/test/behavior/tuple.zig b/test/behavior/tuple.zig index 5cab5e9375..030c4b6a56 100644 --- a/test/behavior/tuple.zig +++ b/test/behavior/tuple.zig @@ -280,7 +280,6 @@ test "tuple in tuple passed to generic function" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { fn pair(x: f32, y: f32) std.meta.Tuple(&.{ f32, f32 }) { @@ -300,7 +299,6 @@ test "coerce tuple to tuple" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const T = std.meta.Tuple(&.{u8}); const S = struct { @@ -315,7 +313,6 @@ test "tuple type with void field" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const T = std.meta.Tuple(&[_]type{void}); const x = T{{}}; @@ -352,7 +349,6 @@ test "zero sized struct in tuple handled correctly" { test "tuple type with void field and a runtime field" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const T = std.meta.Tuple(&[_]type{ usize, void }); var t: T = .{ 5, {} }; From 574028ed5ec6ad455961ed46babdb7a5fe9f68bb Mon Sep 17 00:00:00 2001 From: David Rubin Date: Tue, 23 Jul 2024 18:36:51 -0700 Subject: [PATCH 14/27] riscv: boilerplate for creating lazy functions --- src/arch/riscv64/CodeGen.zig | 493 +++++++++++++++++++++++------------ src/arch/riscv64/bits.zig | 3 +- src/codegen.zig | 4 +- 3 files changed, 328 insertions(+), 172 deletions(-) diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index a509b799e4..d52f07438f 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -56,7 +56,6 @@ gpa: Allocator, mod: *Package.Module, target: *const std.Target, -func_index: InternPool.Index, debug_output: DebugInfoOutput, err_msg: ?*ErrorMsg, args: []MCValue, @@ -68,6 +67,8 @@ src_loc: Zcu.LazySrcLoc, mir_instructions: std.MultiArrayList(Mir.Inst) = .{}, mir_extra: std.ArrayListUnmanaged(u32) = .{}, +owner: Owner, + /// Byte offset within the source file of the ending curly. end_di_line: u32, end_di_column: u32, @@ -112,6 +113,34 @@ const SymbolOffset = struct { sym: u32, off: i32 = 0 }; const RegisterOffset = struct { reg: Register, off: i32 = 0 }; pub const FrameAddr = struct { index: FrameIndex, off: i32 = 0 }; +const Owner = union(enum) { + func_index: InternPool.Index, + lazy_sym: link.File.LazySymbol, + + fn getDecl(owner: Owner, zcu: *Zcu) InternPool.DeclIndex { + return switch (owner) { + .func_index => |func_index| zcu.funcOwnerDeclIndex(func_index), + .lazy_sym => |lazy_sym| lazy_sym.ty.getOwnerDecl(zcu), + }; + } + + fn getSymbolIndex(owner: Owner, func: *Func) !u32 { + const pt = func.pt; + switch (owner) { + .func_index => |func_index| { + const decl_index = func.pt.zcu.funcOwnerDeclIndex(func_index); + const elf_file = func.bin_file.cast(link.File.Elf).?; + return elf_file.zigObjectPtr().?.getOrCreateMetadataForDecl(elf_file, decl_index); + }, + .lazy_sym => |lazy_sym| { + const elf_file = func.bin_file.cast(link.File.Elf).?; + return elf_file.zigObjectPtr().?.getOrCreateMetadataForLazySymbol(elf_file, pt, lazy_sym) catch |err| + func.fail("{s} creating lazy symbol", .{@errorName(err)}); + }, + } + } +}; + const MCValue = union(enum) { /// No runtime bits. `void` types, empty structs, u0, enums with 1 tag, etc. /// TODO Look into deleting this tag and using `dead` instead, since every use @@ -739,8 +768,8 @@ pub fn generate( .bin_file = bin_file, .liveness = liveness, .target = target, - .func_index = func_index, .debug_output = debug_output, + .owner = .{ .func_index = func_index }, .err_msg = null, .args = undefined, // populated after `resolveCallingConventionValues` .ret_mcv = undefined, // populated after `resolveCallingConventionValues` @@ -797,11 +826,11 @@ pub fn generate( function.args = call_info.args; function.ret_mcv = call_info.return_value; function.frame_allocs.set(@intFromEnum(FrameIndex.ret_addr), FrameAlloc.init(.{ - .size = Type.usize.abiSize(pt), - .alignment = Type.usize.abiAlignment(pt).min(call_info.stack_align), + .size = Type.u64.abiSize(pt), + .alignment = Type.u64.abiAlignment(pt).min(call_info.stack_align), })); function.frame_allocs.set(@intFromEnum(FrameIndex.base_ptr), FrameAlloc.init(.{ - .size = Type.usize.abiSize(pt), + .size = Type.u64.abiSize(pt), .alignment = Alignment.min( call_info.stack_align, Alignment.fromNonzeroByteUnits(function.target.stackAlignment()), @@ -813,7 +842,7 @@ pub fn generate( })); function.frame_allocs.set(@intFromEnum(FrameIndex.spill_frame), FrameAlloc.init(.{ .size = 0, - .alignment = Type.usize.abiAlignment(pt), + .alignment = Type.u64.abiAlignment(pt), })); function.gen() catch |err| switch (err) { @@ -876,6 +905,106 @@ pub fn generate( } } +pub fn generateLazy( + bin_file: *link.File, + pt: Zcu.PerThread, + src_loc: Zcu.LazySrcLoc, + lazy_sym: link.File.LazySymbol, + code: *std.ArrayList(u8), + debug_output: DebugInfoOutput, +) CodeGenError!Result { + const comp = bin_file.comp; + const gpa = comp.gpa; + const mod = comp.root_mod; + + var function: Func = .{ + .gpa = gpa, + .air = undefined, + .pt = pt, + .mod = mod, + .bin_file = bin_file, + .liveness = undefined, + .target = &mod.resolved_target.result, + .debug_output = debug_output, + .owner = .{ .lazy_sym = lazy_sym }, + .err_msg = null, + .args = undefined, // populated after `resolveCallingConventionValues` + .ret_mcv = undefined, // populated after `resolveCallingConventionValues` + .fn_type = undefined, + .arg_index = 0, + .branch_stack = undefined, + .src_loc = src_loc, + .end_di_line = undefined, + .end_di_column = undefined, + .scope_generation = 0, + .avl = null, + .vtype = null, + }; + defer { + function.mir_instructions.deinit(gpa); + function.mir_extra.deinit(gpa); + } + + function.genLazy(lazy_sym) catch |err| switch (err) { + error.CodegenFail => return Result{ .fail = function.err_msg.? }, + error.OutOfRegisters => return Result{ + .fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), + }, + else => |e| return e, + }; + + var mir: Mir = .{ + .instructions = function.mir_instructions.toOwnedSlice(), + .extra = try function.mir_extra.toOwnedSlice(gpa), + .frame_locs = function.frame_locs.toOwnedSlice(), + }; + defer mir.deinit(gpa); + + var emit: Emit = .{ + .lower = .{ + .pt = pt, + .allocator = gpa, + .mir = mir, + .cc = .Unspecified, + .src_loc = src_loc, + .output_mode = comp.config.output_mode, + .link_mode = comp.config.link_mode, + .pic = mod.pic, + }, + .bin_file = bin_file, + .debug_output = debug_output, + .code = code, + .prev_di_pc = undefined, // no debug info yet + .prev_di_line = undefined, // no debug info yet + .prev_di_column = undefined, // no debug info yet + }; + defer emit.deinit(); + + emit.emitMir() catch |err| switch (err) { + error.LowerFail, error.EmitFail => return Result{ .fail = emit.lower.err_msg.? }, + error.InvalidInstruction => |e| { + const msg = switch (e) { + error.InvalidInstruction => "CodeGen failed to find a viable instruction.", + }; + return Result{ + .fail = try ErrorMsg.create( + gpa, + src_loc, + "{s} This is a bug in the Zig compiler.", + .{msg}, + ), + }; + }, + else => |e| return e, + }; + + if (function.err_msg) |em| { + return Result{ .fail = em }; + } else { + return Result.ok; + } +} + const FormatWipMirData = struct { func: *Func, inst: Mir.Inst.Index, @@ -1050,7 +1179,7 @@ pub fn addExtraAssumeCapacity(func: *Func, extra: anytype) u32 { /// 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)); + const dst_reg = try func.register_manager.allocReg(null, func.regTempClassForType(Type.u64)); _ = try func.addInst(.{ .tag = .csrrs, .ops = .csr, @@ -1103,7 +1232,7 @@ fn setVl(func: *Func, dst_reg: Register, avl: u64, options: bits.VType) !void { }); } else { const options_int: u12 = @as(u12, 0) | @as(u8, @bitCast(options)); - const temp_reg = try func.copyToTmpRegister(Type.usize, .{ .immediate = avl }); + const temp_reg = try func.copyToTmpRegister(Type.u64, .{ .immediate = avl }); _ = try func.addInst(.{ .tag = .vsetvli, .ops = .rri, @@ -1155,11 +1284,11 @@ fn gen(func: *Func) !void { // The address where to store the return value for the caller is in a // register which the callee is free to clobber. Therefore, we purposely // spill it to stack immediately. - const frame_index = try func.allocFrameIndex(FrameAlloc.initSpill(Type.usize, pt)); + const frame_index = try func.allocFrameIndex(FrameAlloc.initSpill(Type.u64, pt)); try func.genSetMem( .{ .frame = frame_index }, 0, - Type.usize, + Type.u64, func.ret_mcv.long.address().offset(-func.ret_mcv.short.indirect.off), ); func.ret_mcv.long = .{ .load_frame = .{ .index = frame_index } }; @@ -1300,6 +1429,102 @@ fn gen(func: *Func) !void { }); } +fn genLazy(func: *Func, lazy_sym: link.File.LazySymbol) InnerError!void { + const pt = func.pt; + const mod = pt.zcu; + const ip = &mod.intern_pool; + switch (lazy_sym.ty.zigTypeTag(mod)) { + .Enum => { + const enum_ty = lazy_sym.ty; + wip_mir_log.debug("{}.@tagName:", .{enum_ty.fmt(pt)}); + + const param_regs = abi.Registers.Integer.function_arg_regs; + const ret_reg = param_regs[0]; + const enum_mcv: MCValue = .{ .register = param_regs[1] }; + + const exitlude_jump_relocs = try func.gpa.alloc(Mir.Inst.Index, enum_ty.enumFieldCount(mod)); + defer func.gpa.free(exitlude_jump_relocs); + + const data_reg, const data_lock = try func.allocReg(.int); + defer func.register_manager.unlockReg(data_lock); + + const elf_file = func.bin_file.cast(link.File.Elf).?; + const sym_index = elf_file.zigObjectPtr().?.getOrCreateMetadataForLazySymbol(elf_file, pt, .{ + .kind = .const_data, + .ty = enum_ty, + }) catch |err| + return func.fail("{s} creating lazy symbol", .{@errorName(err)}); + const sym = elf_file.symbol(sym_index); + + try func.genSetReg(Type.u64, data_reg, .{ .lea_symbol = .{ .sym = sym.esym_index } }); + + const cmp_reg, const cmp_lock = try func.allocReg(.int); + defer func.register_manager.unlockReg(cmp_lock); + + var data_off: i32 = 0; + const tag_names = enum_ty.enumFields(mod); + for (exitlude_jump_relocs, 0..) |*exitlude_jump_reloc, tag_index| { + const tag_name_len = tag_names.get(ip)[tag_index].length(ip); + const tag_val = try pt.enumValueFieldIndex(enum_ty, @intCast(tag_index)); + const tag_mcv = try func.genTypedValue(tag_val); + + _ = try func.genBinOp( + .cmp_neq, + enum_mcv, + enum_ty, + tag_mcv, + enum_ty, + cmp_reg, + ); + const skip_reloc = try func.condBr(Type.bool, .{ .register = cmp_reg }); + + try func.genSetMem( + .{ .reg = ret_reg }, + 0, + Type.u64, + .{ .register_offset = .{ .reg = data_reg, .off = data_off } }, + ); + + try func.genSetMem( + .{ .reg = ret_reg }, + 8, + Type.u64, + .{ .immediate = tag_name_len }, + ); + + exitlude_jump_reloc.* = try func.addInst(.{ + .tag = .pseudo, + .ops = .pseudo_j, + .data = .{ .inst = undefined }, + }); + func.performReloc(skip_reloc); + + data_off += @intCast(tag_name_len + 1); + } + + try func.airTrap(); + + for (exitlude_jump_relocs) |reloc| func.performReloc(reloc); + + _ = try func.addInst(.{ + .tag = .jalr, + .ops = .rri, + .data = .{ + .i_type = .{ + .rd = .zero, + .rs1 = .ra, + .imm12 = Immediate.s(0), + }, + }, + }); + }, + else => return func.fail( + "TODO implement {s} for {}", + .{ @tagName(lazy_sym.kind), lazy_sym.ty.fmt(pt) }, + ), + } +} + fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void { const pt = func.pt; const zcu = pt.zcu; @@ -1717,8 +1942,8 @@ fn computeFrameLayout(func: *Func) !FrameLayout { } break :blk i; }; - const saved_reg_size = save_reg_list.size(); + const saved_reg_size = save_reg_list.size(); frame_size[@intFromEnum(FrameIndex.spill_frame)] = @intCast(saved_reg_size); // The total frame size is calculated by the amount of s registers you need to save * 8, as each @@ -1879,15 +2104,6 @@ fn truncateRegister(func: *Func, ty: Type, reg: Register) !void { } } -fn symbolIndex(func: *Func) !u32 { - const pt = func.pt; - const zcu = pt.zcu; - const decl_index = zcu.funcOwnerDeclIndex(func.func_index); - const elf_file = func.bin_file.cast(link.File.Elf).?; - const atom_index = try elf_file.zigObjectPtr().?.getOrCreateMetadataForDecl(elf_file, decl_index); - return atom_index; -} - fn allocFrameIndex(func: *Func, alloc: FrameAlloc) !FrameIndex { const frame_allocs_slice = func.frame_allocs.slice(); const frame_size = frame_allocs_slice.items(.abi_size); @@ -2051,6 +2267,10 @@ pub fn spillInstruction(func: *Func, reg: Register, inst: Air.Inst.Index) !void try tracking.trackSpill(func, inst); } +pub fn spillRegisters(func: *Func, comptime registers: []const Register) !void { + inline for (registers) |reg| try func.register_manager.getKnownReg(reg, null); +} + /// Copies a value to a register without tracking the register. The register is not considered /// allocated. A second call to `copyToTmpRegister` may return the same register. /// This can have a side effect of spilling instructions to the stack to free up a register. @@ -2594,14 +2814,14 @@ fn genBinOp( // RISC-V has no immediate mul, so we copy the size to a temporary register const elem_size = lhs_ty.elemType2(zcu).abiSize(pt); - const elem_size_reg = try func.copyToTmpRegister(Type.usize, .{ .immediate = elem_size }); + const elem_size_reg = try func.copyToTmpRegister(Type.u64, .{ .immediate = elem_size }); try func.genBinOp( .mul, tmp_mcv, rhs_ty, .{ .register = elem_size_reg }, - Type.usize, + Type.u64, tmp_reg, ); @@ -2612,9 +2832,9 @@ fn genBinOp( else => unreachable, }, lhs_mcv, - Type.usize, // we know it's a pointer, so it'll be usize. + Type.u64, // we know it's a pointer, so it'll be usize. tmp_mcv, - Type.usize, + Type.u64, dst_reg, ); }, @@ -2980,7 +3200,7 @@ fn airSubWithOverflow(func: *Func, inst: Air.Inst.Index) !void { const rhs_reg, const rhs_lock = try func.promoteReg(rhs_ty, rhs); defer if (rhs_lock) |lock| func.register_manager.unlockReg(lock); - const overflow_reg = try func.copyToTmpRegister(Type.usize, .{ .immediate = 0 }); + const overflow_reg = try func.copyToTmpRegister(Type.u64, .{ .immediate = 0 }); const overflow_lock = func.register_manager.lockRegAssumeUnused(overflow_reg); defer func.register_manager.unlockReg(overflow_lock); @@ -3042,9 +3262,9 @@ fn airSubWithOverflow(func: *Func, inst: Air.Inst.Index) !void { try func.genBinOp( .cmp_neq, .{ .register = overflow_reg }, - Type.usize, + Type.u64, .{ .register = rhs_reg }, - Type.usize, + Type.u64, overflow_reg, ); @@ -3521,7 +3741,7 @@ fn airSliceLen(func: *Func, inst: Air.Inst.Index) !void { if (func.reuseOperand(inst, ty_op.operand, 0, src_mcv)) break :result len_mcv; const dst_mcv = try func.allocRegOrMem(ty, inst, true); - try func.genCopy(Type.usize, dst_mcv, len_mcv); + try func.genCopy(Type.u64, dst_mcv, len_mcv); break :result dst_mcv; }, .register_pair => |pair| { @@ -3530,7 +3750,7 @@ fn airSliceLen(func: *Func, inst: Air.Inst.Index) !void { if (func.reuseOperand(inst, ty_op.operand, 0, src_mcv)) break :result len_mcv; const dst_mcv = try func.allocRegOrMem(ty, inst, true); - try func.genCopy(Type.usize, dst_mcv, len_mcv); + try func.genCopy(Type.u64, dst_mcv, len_mcv); break :result dst_mcv; }, else => return func.fail("TODO airSliceLen for {}", .{src_mcv}), @@ -3619,7 +3839,7 @@ fn genSliceElemPtr(func: *Func, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref) !MCValue { const addr_reg, const addr_lock = try func.allocReg(.int); defer func.register_manager.unlockReg(addr_lock); - try func.genSetReg(Type.usize, addr_reg, slice_mcv); + try func.genSetReg(Type.u64, addr_reg, slice_mcv); _ = try func.addInst(.{ .tag = .add, @@ -3657,12 +3877,12 @@ fn airArrayElemVal(func: *Func, inst: Air.Inst.Index) !void { .register => { const frame_index = try func.allocFrameIndex(FrameAlloc.initType(array_ty, pt)); try func.genSetMem(.{ .frame = frame_index }, 0, array_ty, array_mcv); - try func.genSetReg(Type.usize, addr_reg, .{ .lea_frame = .{ .index = frame_index } }); + try func.genSetReg(Type.u64, addr_reg, .{ .lea_frame = .{ .index = frame_index } }); }, .load_frame => |frame_addr| { - try func.genSetReg(Type.usize, addr_reg, .{ .lea_frame = frame_addr }); + try func.genSetReg(Type.u64, addr_reg, .{ .lea_frame = frame_addr }); }, - else => try func.genSetReg(Type.usize, addr_reg, array_mcv.address()), + else => try func.genSetReg(Type.u64, addr_reg, array_mcv.address()), } const dst_mcv = try func.allocRegOrMem(result_ty, inst, false); @@ -3683,7 +3903,7 @@ fn airArrayElemVal(func: *Func, inst: Air.Inst.Index) !void { // we can do a shortcut here where we don't need a vslicedown // and can just copy to the frame index. if (!(index_mcv == .immediate and index_mcv.immediate == 0)) { - const index_reg = try func.copyToTmpRegister(Type.usize, index_mcv); + const index_reg = try func.copyToTmpRegister(Type.u64, index_mcv); _ = try func.addInst(.{ .tag = .vslidedownvx, @@ -3766,7 +3986,7 @@ fn airPtrElemPtr(func: *Func, inst: Air.Inst.Index) !void { base_ptr_mcv, base_ptr_ty, index_mcv, - Type.usize, + Type.u64, result_reg, ); @@ -4371,7 +4591,7 @@ fn airStructFieldVal(func: *Func, inst: Air.Inst.Index) !void { const dst_reg = if (field_off == 0) (try func.copyToNewRegister(inst, src_mcv)).register else - try func.copyToTmpRegister(Type.usize, .{ .register = src_reg }); + try func.copyToTmpRegister(Type.u64, .{ .register = src_reg }); const dst_mcv: MCValue = .{ .register = dst_reg }; const dst_lock = func.register_manager.lockReg(dst_reg); @@ -4431,8 +4651,8 @@ fn airStructFieldVal(func: *Func, inst: Air.Inst.Index) !void { const hi_mcv = dst_mcv.address().offset(@intCast(field_bit_size / 64 * 8)).deref(); - try func.genSetReg(Type.usize, tmp_reg, hi_mcv); - try func.genCopy(Type.usize, hi_mcv, .{ .register = tmp_reg }); + try func.genSetReg(Type.u64, tmp_reg, hi_mcv); + try func.genCopy(Type.u64, hi_mcv, .{ .register = tmp_reg }); } break :result dst_mcv; } @@ -4456,7 +4676,7 @@ fn genArgDbgInfo(func: Func, inst: Air.Inst.Index, mcv: MCValue) !void { const zcu = pt.zcu; const arg = func.air.instructions.items(.data)[@intFromEnum(inst)].arg; const ty = arg.ty.toType(); - const owner_decl = zcu.funcOwnerDeclIndex(func.func_index); + const owner_decl = func.owner.getDecl(zcu); if (arg.name == .none) return; const name = func.air.nullTerminatedString(@intFromEnum(arg.name)); @@ -4517,13 +4737,13 @@ fn airBreakpoint(func: *Func) !void { fn airRetAddr(func: *Func, inst: Air.Inst.Index) !void { const dst_mcv = try func.allocRegOrMem(func.typeOfIndex(inst), inst, true); - try func.genCopy(Type.usize, dst_mcv, .{ .load_frame = .{ .index = .ret_addr } }); + try func.genCopy(Type.u64, dst_mcv, .{ .load_frame = .{ .index = .ret_addr } }); return func.finishAir(inst, dst_mcv, .{ .none, .none, .none }); } fn airFrameAddress(func: *Func, inst: Air.Inst.Index) !void { const dst_mcv = try func.allocRegOrMem(func.typeOfIndex(inst), inst, true); - try func.genCopy(Type.usize, dst_mcv, .{ .lea_frame = .{ .index = .base_ptr } }); + try func.genCopy(Type.u64, dst_mcv, .{ .lea_frame = .{ .index = .base_ptr } }); return func.finishAir(inst, dst_mcv, .{ .none, .none, .none }); } @@ -4682,7 +4902,7 @@ fn genCall( .indirect => |reg_off| { const ret_ty = Type.fromInterned(fn_info.return_type); const frame_index = try func.allocFrameIndex(FrameAlloc.initSpill(ret_ty, pt)); - try func.genSetReg(Type.usize, reg_off.reg, .{ + try func.genSetReg(Type.u64, reg_off.reg, .{ .lea_frame = .{ .index = frame_index, .off = -reg_off.off }, }); call_info.return_value.short = .{ .load_frame = .{ .index = frame_index } }; @@ -4700,7 +4920,7 @@ fn genCall( dst_reg, src_arg, ), - .indirect => |reg_off| try func.genSetReg(Type.usize, reg_off.reg, .{ + .indirect => |reg_off| try func.genSetReg(Type.u64, reg_off.reg, .{ .lea_frame = .{ .index = frame_index, .off = -reg_off.off }, }), else => return func.fail("TODO: genCall actual set {s}", .{@tagName(dst_arg)}), @@ -4728,7 +4948,7 @@ fn genCall( if (func.mod.pic) { return func.fail("TODO: genCall pic", .{}); } else { - try func.genSetReg(Type.usize, .ra, .{ .load_symbol = .{ .sym = sym.esym_index } }); + try func.genSetReg(Type.u64, .ra, .{ .load_symbol = .{ .sym = sym.esym_index } }); _ = try func.addInst(.{ .tag = .jalr, .ops = .rri, @@ -4745,7 +4965,7 @@ fn genCall( const owner_decl = zcu.declPtr(extern_func.decl); const lib_name = extern_func.lib_name.toSlice(&zcu.intern_pool); const decl_name = owner_decl.name.toSlice(&zcu.intern_pool); - const atom_index = try func.symbolIndex(); + const atom_index = try func.owner.getSymbolIndex(func); if (func.bin_file.cast(link.File.Elf)) |elf_file| { _ = try func.addInst(.{ @@ -4764,7 +4984,7 @@ fn genCall( assert(func.typeOf(callee).zigTypeTag(zcu) == .Pointer); const addr_reg, const addr_lock = try func.allocReg(.int); defer func.register_manager.unlockReg(addr_lock); - try func.genSetReg(Type.usize, addr_reg, .{ .air_ref = callee }); + try func.genSetReg(Type.u64, addr_reg, .{ .air_ref = callee }); _ = try func.addInst(.{ .tag = .jalr, @@ -4829,7 +5049,7 @@ fn airRet(func: *Func, inst: Air.Inst.Index, safety: bool) !void { const lock = func.register_manager.lockRegAssumeUnused(reg_off.reg); defer func.register_manager.unlockReg(lock); - try func.genSetReg(Type.usize, reg_off.reg, func.ret_mcv.long); + try func.genSetReg(Type.u64, reg_off.reg, func.ret_mcv.long); try func.genSetMem( .{ .reg = reg_off.reg }, reg_off.off, @@ -4897,14 +5117,14 @@ fn airCmp(func: *Func, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { .Enum => lhs_ty.intTagType(zcu), .Int => lhs_ty, .Bool => Type.u1, - .Pointer => Type.usize, + .Pointer => Type.u64, .ErrorSet => Type.anyerror, .Optional => blk: { const payload_ty = lhs_ty.optionalChild(zcu); if (!payload_ty.hasRuntimeBitsIgnoreComptime(pt)) { break :blk Type.u1; } else if (lhs_ty.isPtrLikeOptional(zcu)) { - break :blk Type.usize; + break :blk Type.u64; } else { return func.fail("TODO riscv cmp non-pointer optionals", .{}); } @@ -5014,7 +5234,7 @@ fn genVarDbgInfo( break :blk .nop; }, }; - try dw.genVarDbgInfo(name, ty, zcu.funcOwnerDeclIndex(func.func_index), is_ptr, loc); + try dw.genVarDbgInfo(name, ty, func.owner.getDecl(zcu), is_ptr, loc); }, .plan9 => {}, .none => {}, @@ -5837,7 +6057,7 @@ fn genCopy(func: *Func, ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !void { const src_info: ?struct { addr_reg: Register, addr_lock: ?RegisterLock } = switch (src_mcv) { .register_pair, .memory, .indirect, .load_frame => null, .load_symbol => src: { - const src_addr_reg, const src_addr_lock = try func.promoteReg(Type.usize, src_mcv.address()); + const src_addr_reg, const src_addr_lock = try func.promoteReg(Type.u64, src_mcv.address()); errdefer func.register_manager.unlockReg(src_addr_lock); break :src .{ .addr_reg = src_addr_reg, .addr_lock = src_addr_lock }; @@ -5889,9 +6109,9 @@ fn genInlineMemcpy( const src = regs[2]; const dst = regs[3]; - try func.genSetReg(Type.usize, count, len); - try func.genSetReg(Type.usize, src, src_ptr); - try func.genSetReg(Type.usize, dst, dst_ptr); + try func.genSetReg(Type.u64, count, len); + try func.genSetReg(Type.u64, src, src_ptr); + try func.genSetReg(Type.u64, dst, dst_ptr); // if count is 0, there's nothing to copy _ = try func.addInst(.{ @@ -6003,9 +6223,9 @@ fn genInlineMemset( const src = regs[1]; const dst = regs[2]; - try func.genSetReg(Type.usize, count, len); - try func.genSetReg(Type.usize, src, src_value); - try func.genSetReg(Type.usize, dst, dst_ptr); + try func.genSetReg(Type.u64, count, len); + try func.genSetReg(Type.u64, src, src_value); + try func.genSetReg(Type.u64, dst, dst_ptr); // sb src, 0(dst) const first_inst = try func.addInst(.{ @@ -6355,7 +6575,7 @@ fn genSetReg(func: *Func, ty: Type, reg: Register, src_mcv: MCValue) InnerError! }, .lea_symbol => |sym_off| { assert(sym_off.off == 0); - const atom_index = try func.symbolIndex(); + const atom_index = try func.owner.getSymbolIndex(func); _ = try func.addInst(.{ .tag = .pseudo, @@ -6437,7 +6657,7 @@ fn genSetMem( }, .register => |reg| { if (reg.class() == .vector) { - const addr_reg = try func.copyToTmpRegister(Type.usize, dst_ptr_mcv); + const addr_reg = try func.copyToTmpRegister(Type.u64, dst_ptr_mcv); const num_elem = ty.vectorLen(zcu); const elem_size = ty.childType(zcu).bitSize(pt); @@ -6614,7 +6834,7 @@ fn airArrayToSlice(func: *Func, inst: Air.Inst.Index) !void { try func.genSetMem( .{ .frame = frame_index }, @intCast(ptr_ty.abiSize(pt)), - Type.usize, + Type.u64, .{ .immediate = array_len }, ); @@ -6880,8 +7100,8 @@ fn airMemset(func: *Func, inst: Air.Inst.Index, safety: bool) !void { const second_elem_ptr_mcv: MCValue = .{ .register = second_elem_ptr_reg }; - try func.genSetReg(Type.usize, second_elem_ptr_reg, .{ .register_offset = .{ - .reg = try func.copyToTmpRegister(Type.usize, dst_ptr), + try func.genSetReg(Type.u64, second_elem_ptr_reg, .{ .register_offset = .{ + .reg = try func.copyToTmpRegister(Type.u64, dst_ptr), .off = elem_abi_size, } }); @@ -6934,118 +7154,52 @@ fn airMemcpy(func: *Func, inst: Air.Inst.Index) !void { } fn airTagName(func: *Func, inst: Air.Inst.Index) !void { + const pt = func.pt; + const zcu = pt.zcu; + const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op; - const operand = try func.resolveInst(un_op); - const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else { - _ = operand; - return func.fail("TODO implement airTagName for riscv64", .{}); + const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else result: { + const enum_ty = func.typeOf(un_op); + + // TODO: work out the bugs + if (true) return func.fail("TODO: airTagName", .{}); + + const param_regs = abi.Registers.Integer.function_arg_regs; + const dst_mcv = try func.allocRegOrMem(Type.u64, inst, false); + try func.genSetReg(Type.u64, param_regs[0], dst_mcv.address()); + + const operand = try func.resolveInst(un_op); + try func.genSetReg(enum_ty, param_regs[1], operand); + + const lazy_sym = link.File.LazySymbol.initDecl(.code, enum_ty.getOwnerDecl(zcu), zcu); + const elf_file = func.bin_file.cast(link.File.Elf).?; + const sym_index = elf_file.zigObjectPtr().?.getOrCreateMetadataForLazySymbol(elf_file, pt, lazy_sym) catch |err| + return func.fail("{s} creating lazy symbol", .{@errorName(err)}); + const sym = elf_file.symbol(sym_index); + + if (func.mod.pic) { + return func.fail("TODO: airTagName pic", .{}); + } else { + try func.genSetReg(Type.u64, .ra, .{ .load_symbol = .{ .sym = sym.esym_index } }); + _ = try func.addInst(.{ + .tag = .jalr, + .ops = .rri, + .data = .{ .i_type = .{ + .rd = .ra, + .rs1 = .ra, + .imm12 = Immediate.s(0), + } }, + }); + } + + break :result dst_mcv; }; return func.finishAir(inst, result, .{ un_op, .none, .none }); } fn airErrorName(func: *Func, inst: Air.Inst.Index) !void { - const pt = func.pt; - const zcu = pt.zcu; - const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op; - - const err_ty = func.typeOf(un_op); - const err_mcv = try func.resolveInst(un_op); - - const err_reg = try func.copyToTmpRegister(err_ty, err_mcv); - const err_lock = func.register_manager.lockRegAssumeUnused(err_reg); - defer func.register_manager.unlockReg(err_lock); - - const addr_reg, const addr_lock = try func.allocReg(.int); - defer func.register_manager.unlockReg(addr_lock); - - // this is now the base address of the error name table - const lazy_sym = link.File.LazySymbol.initDecl(.const_data, null, zcu); - if (func.bin_file.cast(link.File.Elf)) |elf_file| { - const sym_index = elf_file.zigObjectPtr().?.getOrCreateMetadataForLazySymbol(elf_file, pt, lazy_sym) catch |err| - return func.fail("{s} creating lazy symbol", .{@errorName(err)}); - const sym = elf_file.symbol(sym_index); - try func.genSetReg(Type.usize, addr_reg, .{ .load_symbol = .{ .sym = sym.esym_index } }); - } else { - return func.fail("TODO: riscv non-elf", .{}); - } - - const start_reg, const start_lock = try func.allocReg(.int); - defer func.register_manager.unlockReg(start_lock); - - const end_reg, const end_lock = try func.allocReg(.int); - defer func.register_manager.unlockReg(end_lock); - - // const tmp_reg, const tmp_lock = try func.allocReg(.int); - // defer func.register_manager.unlockReg(tmp_lock); - - // we move the base address forward by the following formula: base + (errno * 8) - - // shifting left by 4 is the same as multiplying by 8 - _ = try func.addInst(.{ - .tag = .slli, - .ops = .rri, - .data = .{ .i_type = .{ - .imm12 = Immediate.u(4), - .rd = err_reg, - .rs1 = err_reg, - } }, - }); - - _ = try func.addInst(.{ - .tag = .add, - .ops = .rrr, - .data = .{ .r_type = .{ - .rd = addr_reg, - .rs1 = addr_reg, - .rs2 = err_reg, - } }, - }); - - _ = try func.addInst(.{ - .tag = .pseudo, - .ops = .pseudo_load_rm, - .data = .{ - .rm = .{ - .r = start_reg, - .m = .{ - .base = .{ .reg = addr_reg }, - .mod = .{ .size = .dword, .unsigned = true }, - }, - }, - }, - }); - - _ = try func.addInst(.{ - .tag = .pseudo, - .ops = .pseudo_load_rm, - .data = .{ - .rm = .{ - .r = end_reg, - .m = .{ - .base = .{ .reg = addr_reg }, - .mod = .{ .size = .dword, .unsigned = true }, - }, - }, - }, - }); - - const dst_mcv = try func.allocRegOrMem(func.typeOfIndex(inst), inst, false); - const frame = dst_mcv.load_frame; - try func.genSetMem( - .{ .frame = frame.index }, - frame.off, - Type.usize, - .{ .register = start_reg }, - ); - - try func.genSetMem( - .{ .frame = frame.index }, - frame.off + 8, - Type.usize, - .{ .register = end_reg }, - ); - - return func.finishAir(inst, dst_mcv, .{ un_op, .none, .none }); + _ = inst; + return func.fail("TODO: airErrorName", .{}); } fn airSplat(func: *Func, inst: Air.Inst.Index) !void { @@ -7231,9 +7385,10 @@ fn getResolvedInstValue(func: *Func, inst: Air.Inst.Index) *InstTracking { fn genTypedValue(func: *Func, val: Value) InnerError!MCValue { const pt = func.pt; + const zcu = pt.zcu; const gpa = func.gpa; - const owner_decl_index = pt.zcu.funcOwnerDeclIndex(func.func_index); + const owner_decl_index = func.owner.getDecl(zcu); const lf = func.bin_file; const src_loc = func.src_loc; diff --git a/src/arch/riscv64/bits.zig b/src/arch/riscv64/bits.zig index 97db43b50e..60efa077c5 100644 --- a/src/arch/riscv64/bits.zig +++ b/src/arch/riscv64/bits.zig @@ -251,8 +251,7 @@ pub const FrameIndex = enum(u32) { /// This index referes to a frame dedicated to setting up args for function called /// in this function. Useful for aligning args separately. call_frame, - /// This index referes to the frame where callee saved registers are spilled and restore - /// from. + /// This index referes to the frame where callee saved registers are spilled and restored from. spill_frame, /// Other indices are used for local variable stack slots _, diff --git a/src/codegen.zig b/src/codegen.zig index ce1488f020..9c3fd1914b 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -106,7 +106,9 @@ pub fn generateLazyFunction( const target = namespace.fileScope(zcu).mod.resolved_target.result; switch (target_util.zigBackend(target, false)) { else => unreachable, - inline .stage2_x86_64 => |backend| { + inline .stage2_x86_64, + .stage2_riscv64, + => |backend| { dev.check(devFeatureForBackend(backend)); return importBackend(backend).generateLazy(lf, pt, src_loc, lazy_sym, code, debug_output); }, From 1a7d89a84d7fef87eb45da8207bbeb852e3f0c02 Mon Sep 17 00:00:00 2001 From: David Rubin Date: Wed, 24 Jul 2024 05:53:57 -0700 Subject: [PATCH 15/27] riscv: clean up and unify encoding logic --- CMakeLists.txt | 4 +- lib/compiler/test_runner.zig | 1 - src/arch/riscv64/CodeGen.zig | 523 +++++++-------- src/arch/riscv64/Emit.zig | 138 ++-- src/arch/riscv64/Encoding.zig | 1136 --------------------------------- src/arch/riscv64/Lower.zig | 881 +++++++++++++------------ src/arch/riscv64/Mir.zig | 300 +-------- src/arch/riscv64/bits.zig | 3 +- src/arch/riscv64/encoder.zig | 80 --- src/arch/riscv64/encoding.zig | 716 +++++++++++++++++++++ src/arch/riscv64/mnem.zig | 232 +++++++ src/link/riscv.zig | 40 +- test/behavior/align.zig | 1 - test/behavior/byteswap.zig | 1 + test/behavior/defer.zig | 2 + test/behavior/optional.zig | 1 + test/behavior/pointers.zig | 1 + test/behavior/switch.zig | 2 + test/behavior/try.zig | 1 + 19 files changed, 1696 insertions(+), 2367 deletions(-) delete mode 100644 src/arch/riscv64/Encoding.zig delete mode 100644 src/arch/riscv64/encoder.zig create mode 100644 src/arch/riscv64/encoding.zig create mode 100644 src/arch/riscv64/mnem.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index 2e27935d3d..83fd4ebece 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -539,10 +539,10 @@ set(ZIG_STAGE2_SOURCES src/arch/riscv64/bits.zig src/arch/riscv64/CodeGen.zig src/arch/riscv64/Emit.zig - src/arch/riscv64/encoder.zig - src/arch/riscv64/Encoding.zig + src/arch/riscv64/encoding.zig src/arch/riscv64/Lower.zig src/arch/riscv64/Mir.zig + src/arch/riscv64/mnem.zig src/arch/sparc64/CodeGen.zig src/arch/sparc64/Emit.zig src/arch/sparc64/Mir.zig diff --git a/lib/compiler/test_runner.zig b/lib/compiler/test_runner.zig index cf8d8df3f0..3e97062982 100644 --- a/lib/compiler/test_runner.zig +++ b/lib/compiler/test_runner.zig @@ -271,7 +271,6 @@ pub fn mainSimple() anyerror!void { }; // is the backend capable of using std.fmt.format to print a summary at the end? const print_summary = switch (builtin.zig_backend) { - .stage2_riscv64 => true, else => false, }; diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index d52f07438f..6e807f1f3b 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -20,6 +20,7 @@ const InternPool = @import("../../InternPool.zig"); const Compilation = @import("../../Compilation.zig"); const trace = @import("../../tracy.zig").trace; const codegen = @import("../../codegen.zig"); +const Mnemonic = @import("mnem.zig").Mnemonic; const ErrorMsg = Zcu.ErrorMsg; const Target = std.Target; @@ -65,7 +66,6 @@ arg_index: usize, src_loc: Zcu.LazySrcLoc, mir_instructions: std.MultiArrayList(Mir.Inst) = .{}, -mir_extra: std.ArrayListUnmanaged(u32) = .{}, owner: Owner, @@ -794,7 +794,6 @@ pub fn generate( function.const_tracking.deinit(gpa); function.exitlude_jump_relocs.deinit(gpa); function.mir_instructions.deinit(gpa); - function.mir_extra.deinit(gpa); } wip_mir_log.debug("{}:", .{function.fmtDecl(func.owner_decl)}); @@ -855,7 +854,6 @@ pub fn generate( var mir: Mir = .{ .instructions = function.mir_instructions.toOwnedSlice(), - .extra = try function.mir_extra.toOwnedSlice(gpa), .frame_locs = function.frame_locs.toOwnedSlice(), }; defer mir.deinit(gpa); @@ -940,10 +938,7 @@ pub fn generateLazy( .avl = null, .vtype = null, }; - defer { - function.mir_instructions.deinit(gpa); - function.mir_extra.deinit(gpa); - } + defer function.mir_instructions.deinit(gpa); function.genLazy(lazy_sym) catch |err| switch (err) { error.CodegenFail => return Result{ .fail = function.err_msg.? }, @@ -955,7 +950,6 @@ pub fn generateLazy( var mir: Mir = .{ .instructions = function.mir_instructions.toOwnedSlice(), - .extra = try function.mir_extra.toOwnedSlice(gpa), .frame_locs = function.frame_locs.toOwnedSlice(), }; defer mir.deinit(gpa); @@ -1022,7 +1016,6 @@ fn formatWipMir( .allocator = data.func.gpa, .mir = .{ .instructions = data.func.mir_instructions.slice(), - .extra = data.func.mir_extra.items, .frame_locs = data.func.frame_locs.slice(), }, .cc = .Unspecified, @@ -1120,7 +1113,7 @@ fn addInst(func: *Func, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index { try func.mir_instructions.ensureUnusedCapacity(gpa, 1); const result_index: Mir.Inst.Index = @intCast(func.mir_instructions.len); func.mir_instructions.appendAssumeCapacity(inst); - if (inst.tag != .pseudo or switch (inst.ops) { + if (switch (inst.tag) { else => true, .pseudo_dbg_prologue_end, .pseudo_dbg_line_column, @@ -1131,49 +1124,13 @@ fn addInst(func: *Func, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index { return result_index; } -fn addNop(func: *Func) error{OutOfMemory}!Mir.Inst.Index { +fn addPseudo(func: *Func, mnem: Mnemonic) error{OutOfMemory}!Mir.Inst.Index { return func.addInst(.{ - .tag = .nop, - .ops = .none, - .data = undefined, + .tag = mnem, + .data = .none, }); } -fn addPseudoNone(func: *Func, ops: Mir.Inst.Ops) !void { - _ = try func.addInst(.{ - .tag = .pseudo, - .ops = ops, - .data = undefined, - }); -} - -fn addPseudo(func: *Func, ops: Mir.Inst.Ops) !Mir.Inst.Index { - return func.addInst(.{ - .tag = .pseudo, - .ops = ops, - .data = undefined, - }); -} - -pub fn addExtra(func: *Func, extra: anytype) Allocator.Error!u32 { - const fields = std.meta.fields(@TypeOf(extra)); - try func.mir_extra.ensureUnusedCapacity(func.gpa, fields.len); - return func.addExtraAssumeCapacity(extra); -} - -pub fn addExtraAssumeCapacity(func: *Func, extra: anytype) u32 { - const fields = std.meta.fields(@TypeOf(extra)); - const result: u32 = @intCast(func.mir_extra.items.len); - inline for (fields) |field| { - func.mir_extra.appendAssumeCapacity(switch (field.type) { - u32 => @field(extra, field.name), - i32 => @bitCast(@field(extra, field.name)), - else => @compileError("bad field type"), - }); - } - return result; -} - /// Returns a temporary register that contains the value of the `reg` csr. /// /// Caller's duty to lock the return register is needed. @@ -1182,14 +1139,11 @@ fn getCsr(func: *Func, csr: CSR) !Register { const dst_reg = try func.register_manager.allocReg(null, func.regTempClassForType(Type.u64)); _ = try func.addInst(.{ .tag = .csrrs, - .ops = .csr, - .data = .{ - .csr = .{ - .csr = csr, - .rd = dst_reg, - .rs1 = .x0, - }, - }, + .data = .{ .csr = .{ + .csr = csr, + .rd = dst_reg, + .rs1 = .x0, + } }, }); return dst_reg; } @@ -1208,7 +1162,6 @@ fn setVl(func: *Func, dst_reg: Register, avl: u64, options: bits.VType) !void { 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, @@ -1221,7 +1174,6 @@ fn setVl(func: *Func, dst_reg: Register, avl: u64, options: bits.VType) !void { 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, @@ -1235,7 +1187,6 @@ fn setVl(func: *Func, dst_reg: Register, avl: u64, options: bits.VType) !void { const temp_reg = try func.copyToTmpRegister(Type.u64, .{ .immediate = avl }); _ = try func.addInst(.{ .tag = .vsetvli, - .ops = .rri, .data = .{ .i_type = .{ .rd = dst_reg, .rs1 = temp_reg, @@ -1270,7 +1221,7 @@ fn gen(func: *Func) !void { } if (fn_info.cc != .Naked) { - try func.addPseudoNone(.pseudo_dbg_prologue_end); + _ = try func.addPseudo(.pseudo_dbg_prologue_end); const backpatch_stack_alloc = try func.addPseudo(.pseudo_dead); const backpatch_ra_spill = try func.addPseudo(.pseudo_dead); @@ -1300,11 +1251,11 @@ fn gen(func: *Func) !void { try func.genBody(func.air.getMainBody()); for (func.exitlude_jump_relocs.items) |jmp_reloc| { - func.mir_instructions.items(.data)[jmp_reloc].inst = + func.mir_instructions.items(.data)[jmp_reloc].j_type.inst = @intCast(func.mir_instructions.len); } - try func.addPseudoNone(.pseudo_dbg_epilogue_begin); + _ = try func.addPseudo(.pseudo_dbg_epilogue_begin); const backpatch_restore_callee_preserved_regs = try func.addPseudo(.pseudo_dead); const backpatch_ra_restore = try func.addPseudo(.pseudo_dead); @@ -1314,7 +1265,6 @@ fn gen(func: *Func) !void { // ret _ = try func.addInst(.{ .tag = .jalr, - .ops = .rri, .data = .{ .i_type = .{ .rd = .zero, @@ -1329,7 +1279,6 @@ fn gen(func: *Func) !void { func.mir_instructions.set(backpatch_stack_alloc, .{ .tag = .addi, - .ops = .rri, .data = .{ .i_type = .{ .rd = .sp, .rs1 = .sp, @@ -1337,8 +1286,7 @@ fn gen(func: *Func) !void { } }, }); func.mir_instructions.set(backpatch_ra_spill, .{ - .tag = .pseudo, - .ops = .pseudo_store_rm, + .tag = .pseudo_store_rm, .data = .{ .rm = .{ .r = .ra, .m = .{ @@ -1348,8 +1296,7 @@ fn gen(func: *Func) !void { } }, }); func.mir_instructions.set(backpatch_ra_restore, .{ - .tag = .pseudo, - .ops = .pseudo_load_rm, + .tag = .pseudo_load_rm, .data = .{ .rm = .{ .r = .ra, .m = .{ @@ -1359,8 +1306,7 @@ fn gen(func: *Func) !void { } }, }); func.mir_instructions.set(backpatch_fp_spill, .{ - .tag = .pseudo, - .ops = .pseudo_store_rm, + .tag = .pseudo_store_rm, .data = .{ .rm = .{ .r = .s0, .m = .{ @@ -1370,8 +1316,7 @@ fn gen(func: *Func) !void { } }, }); func.mir_instructions.set(backpatch_fp_restore, .{ - .tag = .pseudo, - .ops = .pseudo_load_rm, + .tag = .pseudo_load_rm, .data = .{ .rm = .{ .r = .s0, .m = .{ @@ -1382,7 +1327,6 @@ fn gen(func: *Func) !void { }); func.mir_instructions.set(backpatch_fp_add, .{ .tag = .addi, - .ops = .rri, .data = .{ .i_type = .{ .rd = .s0, .rs1 = .sp, @@ -1391,7 +1335,6 @@ fn gen(func: *Func) !void { }); func.mir_instructions.set(backpatch_stack_alloc_restore, .{ .tag = .addi, - .ops = .rri, .data = .{ .i_type = .{ .rd = .sp, .rs1 = .sp, @@ -1401,27 +1344,24 @@ fn gen(func: *Func) !void { if (need_save_reg) { func.mir_instructions.set(backpatch_spill_callee_preserved_regs, .{ - .tag = .pseudo, - .ops = .pseudo_spill_regs, + .tag = .pseudo_spill_regs, .data = .{ .reg_list = frame_layout.save_reg_list }, }); func.mir_instructions.set(backpatch_restore_callee_preserved_regs, .{ - .tag = .pseudo, - .ops = .pseudo_restore_regs, + .tag = .pseudo_restore_regs, .data = .{ .reg_list = frame_layout.save_reg_list }, }); } } else { - try func.addPseudoNone(.pseudo_dbg_prologue_end); + _ = try func.addPseudo(.pseudo_dbg_prologue_end); try func.genBody(func.air.getMainBody()); - try func.addPseudoNone(.pseudo_dbg_epilogue_begin); + _ = try func.addPseudo(.pseudo_dbg_epilogue_begin); } // Drop them off at the rbrace. _ = try func.addInst(.{ - .tag = .pseudo, - .ops = .pseudo_dbg_line_column, + .tag = .pseudo_dbg_line_column, .data = .{ .pseudo_dbg_line_column = .{ .line = func.end_di_line, .column = func.end_di_column, @@ -1493,9 +1433,11 @@ fn genLazy(func: *Func, lazy_sym: link.File.LazySymbol) InnerError!void { ); exitlude_jump_reloc.* = try func.addInst(.{ - .tag = .pseudo, - .ops = .pseudo_j, - .data = .{ .inst = undefined }, + .tag = .pseudo_j, + .data = .{ .j_type = .{ + .rd = .zero, + .inst = undefined, + } }, }); func.performReloc(skip_reloc); @@ -1508,7 +1450,7 @@ fn genLazy(func: *Func, lazy_sym: link.File.LazySymbol) InnerError!void { _ = try func.addInst(.{ .tag = .jalr, - .ops = .rri, + .data = .{ .i_type = .{ .rd = .zero, @@ -2041,7 +1983,7 @@ fn truncateRegister(func: *Func, ty: Type, reg: Register) !void { .signed => { _ = try func.addInst(.{ .tag = .slli, - .ops = .rri, + .data = .{ .i_type = .{ .rd = reg, @@ -2052,7 +1994,7 @@ fn truncateRegister(func: *Func, ty: Type, reg: Register) !void { }); _ = try func.addInst(.{ .tag = .srai, - .ops = .rri, + .data = .{ .i_type = .{ .rd = reg, @@ -2067,7 +2009,7 @@ fn truncateRegister(func: *Func, ty: Type, reg: Register) !void { if (mask < 256) { _ = try func.addInst(.{ .tag = .andi, - .ops = .rri, + .data = .{ .i_type = .{ .rd = reg, @@ -2079,7 +2021,7 @@ fn truncateRegister(func: *Func, ty: Type, reg: Register) !void { } else { _ = try func.addInst(.{ .tag = .slli, - .ops = .rri, + .data = .{ .i_type = .{ .rd = reg, @@ -2090,7 +2032,7 @@ fn truncateRegister(func: *Func, ty: Type, reg: Register) !void { }); _ = try func.addInst(.{ .tag = .srli, - .ops = .rri, + .data = .{ .i_type = .{ .rd = reg, @@ -2411,8 +2353,7 @@ fn airNot(func: *Func, inst: Air.Inst.Index) !void { switch (ty.zigTypeTag(zcu)) { .Bool => { _ = try func.addInst(.{ - .tag = .pseudo, - .ops = .pseudo_not, + .tag = .pseudo_not, .data = .{ .rr = .{ .rs = operand_reg, @@ -2430,7 +2371,6 @@ fn airNot(func: *Func, inst: Air.Inst.Index) !void { 32, 64 => { _ = try func.addInst(.{ .tag = .xori, - .ops = .rri, .data = .{ .i_type = .{ .rd = dst_reg, @@ -2628,7 +2568,7 @@ fn genBinOp( switch (lhs_ty.zigTypeTag(zcu)) { .Int => { - const mir_tag: Mir.Inst.Tag = switch (tag) { + const mnem: Mnemonic = switch (tag) { .add, .add_wrap => switch (bit_size) { 8, 16, 64 => .add, 32 => .addw, @@ -2656,8 +2596,7 @@ fn genBinOp( }; _ = try func.addInst(.{ - .tag = mir_tag, - .ops = .rrr, + .tag = mnem, .data = .{ .r_type = .{ .rd = dst_reg, @@ -2668,7 +2607,7 @@ fn genBinOp( }); }, .Float => { - const mir_tag: Mir.Inst.Tag = switch (tag) { + const mir_tag: Mnemonic = switch (tag) { .add => switch (bit_size) { 32 => .fadds, 64 => .faddd, @@ -2689,7 +2628,6 @@ fn genBinOp( _ = try func.addInst(.{ .tag = mir_tag, - .ops = .rrr, .data = .{ .r_type = .{ .rd = dst_reg, @@ -2705,7 +2643,7 @@ fn genBinOp( const child_ty = lhs_ty.childType(zcu); - const mir_tag: Mir.Inst.Tag = switch (tag) { + const mir_tag: Mnemonic = switch (tag) { .add => switch (child_ty.zigTypeTag(zcu)) { .Int => .vaddvv, .Float => .vfaddvv, @@ -2739,7 +2677,6 @@ fn genBinOp( _ = try func.addInst(.{ .tag = mir_tag, - .ops = .rrr, .data = .{ .r_type = .{ .rd = dst_reg, @@ -2764,7 +2701,6 @@ fn genBinOp( _ = try func.addInst(.{ .tag = .add, - .ops = .rrr, .data = .{ .r_type = .{ .rd = tmp_reg, .rs1 = rhs_reg, @@ -2774,7 +2710,6 @@ fn genBinOp( _ = try func.addInst(.{ .tag = .sltu, - .ops = .rrr, .data = .{ .r_type = .{ .rd = dst_reg, .rs1 = tmp_reg, @@ -2785,7 +2720,6 @@ fn genBinOp( // neg dst_reg, dst_reg _ = try func.addInst(.{ .tag = .sub, - .ops = .rrr, .data = .{ .r_type = .{ .rd = dst_reg, .rs1 = .zero, @@ -2795,7 +2729,6 @@ fn genBinOp( _ = try func.addInst(.{ .tag = .@"or", - .ops = .rrr, .data = .{ .r_type = .{ .rd = dst_reg, .rs1 = dst_reg, @@ -2850,7 +2783,6 @@ fn genBinOp( .bit_or, .bool_or => .@"or", else => unreachable, }, - .ops = .rrr, .data = .{ .r_type = .{ .rd = dst_reg, @@ -2876,7 +2808,7 @@ fn genBinOp( if (bit_size > 64) return func.fail("TODO: genBinOp shift > 64 bits, {}", .{bit_size}); try func.truncateRegister(rhs_ty, rhs_reg); - const mir_tag: Mir.Inst.Tag = switch (tag) { + const mir_tag: Mnemonic = switch (tag) { .shl, .shl_exact => switch (bit_size) { 1...31, 33...64 => .sll, 32 => .sllw, @@ -2892,7 +2824,6 @@ fn genBinOp( _ = try func.addInst(.{ .tag = mir_tag, - .ops = .rrr, .data = .{ .r_type = .{ .rd = dst_reg, .rs1 = lhs_reg, @@ -2910,8 +2841,7 @@ fn genBinOp( .cmp_gte, => { _ = try func.addInst(.{ - .tag = .pseudo, - .ops = .pseudo_compare, + .tag = .pseudo_compare, .data = .{ .compare = .{ .op = switch (tag) { @@ -2966,7 +2896,6 @@ fn genBinOp( _ = try func.addInst(.{ .tag = if (int_info.signedness == .unsigned) .sltu else .slt, - .ops = .rrr, .data = .{ .r_type = .{ .rd = mask_reg, .rs1 = lhs_reg, @@ -2976,7 +2905,6 @@ fn genBinOp( _ = try func.addInst(.{ .tag = .sub, - .ops = .rrr, .data = .{ .r_type = .{ .rd = mask_reg, .rs1 = .zero, @@ -2986,7 +2914,6 @@ fn genBinOp( _ = try func.addInst(.{ .tag = .xor, - .ops = .rrr, .data = .{ .r_type = .{ .rd = dst_reg, .rs1 = lhs_reg, @@ -2996,7 +2923,6 @@ fn genBinOp( _ = try func.addInst(.{ .tag = .@"and", - .ops = .rrr, .data = .{ .r_type = .{ .rd = mask_reg, .rs1 = dst_reg, @@ -3006,7 +2932,6 @@ fn genBinOp( _ = try func.addInst(.{ .tag = .xor, - .ops = .rrr, .data = .{ .r_type = .{ .rd = dst_reg, .rs1 = if (tag == .min) rhs_reg else lhs_reg, @@ -3103,7 +3028,6 @@ fn airAddWithOverflow(func: *Func, inst: Air.Inst.Index) !void { _ = try func.addInst(.{ .tag = .add, - .ops = .rrr, .data = .{ .r_type = .{ .rs1 = rhs_reg, .rs2 = lhs_reg, @@ -3209,7 +3133,6 @@ fn airSubWithOverflow(func: *Func, inst: Air.Inst.Index) !void { .unsigned => { _ = try func.addInst(.{ .tag = .sltu, - .ops = .rrr, .data = .{ .r_type = .{ .rd = overflow_reg, .rs1 = lhs_reg, @@ -3231,7 +3154,6 @@ fn airSubWithOverflow(func: *Func, inst: Air.Inst.Index) !void { 64 => { _ = try func.addInst(.{ .tag = .slt, - .ops = .rrr, .data = .{ .r_type = .{ .rd = overflow_reg, .rs1 = overflow_reg, @@ -3241,7 +3163,6 @@ fn airSubWithOverflow(func: *Func, inst: Air.Inst.Index) !void { _ = try func.addInst(.{ .tag = .slt, - .ops = .rrr, .data = .{ .r_type = .{ .rd = rhs_reg, .rs1 = rhs_reg, @@ -3251,7 +3172,6 @@ fn airSubWithOverflow(func: *Func, inst: Air.Inst.Index) !void { _ = try func.addInst(.{ .tag = .xor, - .ops = .rrr, .data = .{ .r_type = .{ .rd = lhs_reg, .rs1 = overflow_reg, @@ -3843,7 +3763,6 @@ fn genSliceElemPtr(func: *Func, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref) !MCValue { _ = try func.addInst(.{ .tag = .add, - .ops = .rrr, .data = .{ .r_type = .{ .rd = addr_reg, .rs1 = addr_reg, @@ -3907,7 +3826,6 @@ fn airArrayElemVal(func: *Func, inst: Air.Inst.Index) !void { _ = try func.addInst(.{ .tag = .vslidedownvx, - .ops = .rrr, .data = .{ .r_type = .{ .rd = src_reg, .rs1 = index_reg, @@ -3925,7 +3843,6 @@ fn airArrayElemVal(func: *Func, inst: Air.Inst.Index) !void { defer func.register_manager.unlockReg(offset_lock); _ = try func.addInst(.{ .tag = .add, - .ops = .rrr, .data = .{ .r_type = .{ .rd = addr_reg, .rs1 = addr_reg, @@ -4080,7 +3997,6 @@ fn airClz(func: *Func, inst: Air.Inst.Index) !void { 32 => .clzw, else => .clz, }, - .ops = .rrr, .data = .{ .r_type = .{ .rs2 = .zero, // rs2 is 0 filled in the spec @@ -4093,7 +4009,6 @@ fn airClz(func: *Func, inst: Air.Inst.Index) !void { if (!(bit_size == 32 or bit_size == 64)) { _ = try func.addInst(.{ .tag = .addi, - .ops = .rri, .data = .{ .i_type = .{ .rd = dst_reg, .rs1 = dst_reg, @@ -4151,7 +4066,6 @@ fn airAbs(func: *Func, inst: Air.Inst.Index) !void { 64 => .srai, else => unreachable, }, - .ops = .rri, .data = .{ .i_type = .{ .rd = temp_reg, .rs1 = operand_reg, @@ -4161,7 +4075,6 @@ fn airAbs(func: *Func, inst: Air.Inst.Index) !void { _ = try func.addInst(.{ .tag = .xor, - .ops = .rrr, .data = .{ .r_type = .{ .rd = operand_reg, .rs1 = operand_reg, @@ -4175,7 +4088,6 @@ fn airAbs(func: *Func, inst: Air.Inst.Index) !void { 64 => .sub, else => unreachable, }, - .ops = .rrr, .data = .{ .r_type = .{ .rd = operand_reg, .rs1 = operand_reg, @@ -4187,14 +4099,14 @@ fn airAbs(func: *Func, inst: Air.Inst.Index) !void { }, .Float => { const float_bits = scalar_ty.floatBits(zcu.getTarget()); - switch (float_bits) { + const mnem: Mnemonic = switch (float_bits) { 16 => return func.fail("TODO: airAbs 16-bit float", .{}), - 32 => {}, - 64 => {}, + 32 => .fsgnjxs, + 64 => .fsgnjxd, 80 => return func.fail("TODO: airAbs 80-bit float", .{}), 128 => return func.fail("TODO: airAbs 128-bit float", .{}), else => unreachable, - } + }; const return_mcv = try func.copyToNewRegister(inst, operand); const operand_reg = return_mcv.register; @@ -4202,13 +4114,12 @@ fn airAbs(func: *Func, inst: Air.Inst.Index) !void { assert(operand_reg.class() == .float); _ = try func.addInst(.{ - .tag = .pseudo, - .ops = .pseudo_fabs, + .tag = mnem, .data = .{ - .fabs = .{ + .r_type = .{ .rd = operand_reg, - .rs = operand_reg, - .bits = float_bits, + .rs1 = operand_reg, + .rs2 = operand_reg, }, }, }); @@ -4231,54 +4142,56 @@ fn airByteSwap(func: *Func, inst: Air.Inst.Index) !void { const ty = func.typeOf(ty_op.operand); const operand = try func.resolveInst(ty_op.operand); - const int_bits = ty.intInfo(zcu).bits; + switch (ty.zigTypeTag(zcu)) { + .Int => { + const int_bits = ty.intInfo(zcu).bits; - // bytes are no-op - if (int_bits == 8 and func.reuseOperand(inst, ty_op.operand, 0, operand)) { - return func.finishAir(inst, operand, .{ ty_op.operand, .none, .none }); - } + // bytes are no-op + if (int_bits == 8 and func.reuseOperand(inst, ty_op.operand, 0, operand)) { + return func.finishAir(inst, operand, .{ ty_op.operand, .none, .none }); + } - const dest_mcv = try func.copyToNewRegister(inst, operand); - const dest_reg = dest_mcv.register; + const dest_mcv = try func.copyToNewRegister(inst, operand); + const dest_reg = dest_mcv.register; - switch (int_bits) { - 16 => { - const temp_reg, const temp_lock = try func.allocReg(.int); - defer func.register_manager.unlockReg(temp_lock); + switch (int_bits) { + 16 => { + const temp_reg, const temp_lock = try func.allocReg(.int); + defer func.register_manager.unlockReg(temp_lock); - _ = try func.addInst(.{ - .tag = .srli, - .ops = .rri, - .data = .{ .i_type = .{ - .imm12 = Immediate.s(8), - .rd = temp_reg, - .rs1 = dest_reg, - } }, - }); + _ = try func.addInst(.{ + .tag = .srli, + .data = .{ .i_type = .{ + .imm12 = Immediate.s(8), + .rd = temp_reg, + .rs1 = dest_reg, + } }, + }); - _ = try func.addInst(.{ - .tag = .slli, - .ops = .rri, - .data = .{ .i_type = .{ - .imm12 = Immediate.s(8), - .rd = dest_reg, - .rs1 = dest_reg, - } }, - }); - _ = try func.addInst(.{ - .tag = .@"or", - .ops = .rri, - .data = .{ .r_type = .{ - .rd = dest_reg, - .rs1 = dest_reg, - .rs2 = temp_reg, - } }, - }); + _ = try func.addInst(.{ + .tag = .slli, + .data = .{ .i_type = .{ + .imm12 = Immediate.s(8), + .rd = dest_reg, + .rs1 = dest_reg, + } }, + }); + _ = try func.addInst(.{ + .tag = .@"or", + .data = .{ .r_type = .{ + .rd = dest_reg, + .rs1 = dest_reg, + .rs2 = temp_reg, + } }, + }); + }, + else => return func.fail("TODO: {d} bits for airByteSwap", .{int_bits}), + } + + break :result dest_mcv; }, - else => return func.fail("TODO: {d} bits for airByteSwap", .{int_bits}), + else => return func.fail("TODO: airByteSwap {}", .{ty.fmt(pt)}), } - - break :result dest_mcv; }; return func.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } @@ -4322,7 +4235,6 @@ fn airUnaryMath(func: *Func, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { .sqrt => { _ = try func.addInst(.{ .tag = if (operand_bit_size == 64) .fsqrtd else .fsqrts, - .ops = .rrr, .data = .{ .r_type = .{ .rd = dst_reg, @@ -4332,6 +4244,7 @@ fn airUnaryMath(func: *Func, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { }, }); }, + else => return func.fail("TODO: airUnaryMath Float {s}", .{@tagName(tag)}), } }, @@ -4538,17 +4451,14 @@ fn structFieldPtr(func: *Func, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde const zcu = pt.zcu; const ptr_field_ty = func.typeOfIndex(inst); const ptr_container_ty = func.typeOf(operand); - const ptr_container_ty_info = ptr_container_ty.ptrInfo(zcu); const container_ty = ptr_container_ty.childType(zcu); - const field_offset: i32 = if (zcu.typeToPackedStruct(container_ty)) |struct_obj| - if (ptr_field_ty.ptrInfo(zcu).packed_offset.host_size == 0) - @divExact(pt.structPackedFieldBitOffset(struct_obj, index) + - ptr_container_ty_info.packed_offset.bit_offset, 8) - else - 0 - else - @intCast(container_ty.structFieldOffset(index, pt)); + const field_offset: i32 = switch (container_ty.containerLayout(zcu)) { + .auto, .@"extern" => @intCast(container_ty.structFieldOffset(index, pt)), + .@"packed" => @divExact(@as(i32, ptr_container_ty.ptrInfo(zcu).packed_offset.bit_offset) + + (if (zcu.typeToStruct(container_ty)) |struct_obj| pt.structPackedFieldBitOffset(struct_obj, index) else 0) - + ptr_field_ty.ptrInfo(zcu).packed_offset.bit_offset, 8), + }; const src_mcv = try func.resolveInst(operand); const dst_mcv = if (switch (src_mcv) { @@ -4600,7 +4510,6 @@ fn airStructFieldVal(func: *Func, inst: Air.Inst.Index) !void { if (field_off > 0) { _ = try func.addInst(.{ .tag = .srli, - .ops = .rri, .data = .{ .i_type = .{ .imm12 = Immediate.u(@intCast(field_off)), .rd = dst_reg, @@ -4720,8 +4629,7 @@ fn airArg(func: *Func, inst: Air.Inst.Index) !void { fn airTrap(func: *Func) !void { _ = try func.addInst(.{ .tag = .unimp, - .ops = .none, - .data = undefined, + .data = .none, }); return func.finishAirBookkeeping(); } @@ -4729,8 +4637,7 @@ fn airTrap(func: *Func) !void { fn airBreakpoint(func: *Func) !void { _ = try func.addInst(.{ .tag = .ebreak, - .ops = .none, - .data = undefined, + .data = .none, }); return func.finishAirBookkeeping(); } @@ -4758,8 +4665,7 @@ fn airFence(func: *Func, inst: Air.Inst.Index) !void { }; _ = try func.addInst(.{ - .tag = .pseudo, - .ops = .pseudo_fence, + .tag = .pseudo_fence, .data = .{ .fence = .{ .pred = pred, @@ -4951,7 +4857,6 @@ fn genCall( try func.genSetReg(Type.u64, .ra, .{ .load_symbol = .{ .sym = sym.esym_index } }); _ = try func.addInst(.{ .tag = .jalr, - .ops = .rri, .data = .{ .i_type = .{ .rd = .ra, .rs1 = .ra, @@ -4967,16 +4872,15 @@ fn genCall( const decl_name = owner_decl.name.toSlice(&zcu.intern_pool); const atom_index = try func.owner.getSymbolIndex(func); - if (func.bin_file.cast(link.File.Elf)) |elf_file| { - _ = try func.addInst(.{ - .tag = .pseudo, - .ops = .pseudo_extern_fn_reloc, - .data = .{ .reloc = .{ - .atom_index = atom_index, - .sym_index = try elf_file.getGlobalSymbol(decl_name, lib_name), - } }, - }); - } else unreachable; // not a valid riscv64 format + const elf_file = func.bin_file.cast(link.File.Elf).?; + _ = try func.addInst(.{ + .tag = .pseudo_extern_fn_reloc, + .data = .{ .reloc = .{ + .register = .ra, + .atom_index = atom_index, + .sym_index = try elf_file.getGlobalSymbol(decl_name, lib_name), + } }, + }); }, else => return func.fail("TODO implement calling bitcasted functions", .{}), } @@ -4988,7 +4892,6 @@ fn genCall( _ = try func.addInst(.{ .tag = .jalr, - .ops = .rri, .data = .{ .i_type = .{ .rd = .ra, .rs1 = addr_reg, @@ -5065,9 +4968,11 @@ fn airRet(func: *Func, inst: Air.Inst.Index, safety: bool) !void { // Just add space for an instruction, reloced this later const index = try func.addInst(.{ - .tag = .pseudo, - .ops = .pseudo_j, - .data = .{ .inst = undefined }, + .tag = .pseudo_j, + .data = .{ .j_type = .{ + .rd = .zero, + .inst = undefined, + } }, }); try func.exitlude_jump_relocs.append(func.gpa, index); @@ -5089,9 +4994,11 @@ fn airRetLoad(func: *Func, inst: Air.Inst.Index) !void { // Just add space for an instruction, reloced this later const index = try func.addInst(.{ - .tag = .pseudo, - .ops = .pseudo_j, - .data = .{ .inst = undefined }, + .tag = .pseudo_j, + .data = .{ .j_type = .{ + .rd = .zero, + .inst = undefined, + } }, }); try func.exitlude_jump_relocs.append(func.gpa, index); @@ -5171,8 +5078,7 @@ fn airDbgStmt(func: *Func, inst: Air.Inst.Index) !void { const dbg_stmt = func.air.instructions.items(.data)[@intFromEnum(inst)].dbg_stmt; _ = try func.addInst(.{ - .tag = .pseudo, - .ops = .pseudo_dbg_line_column, + .tag = .pseudo_dbg_line_column, .data = .{ .pseudo_dbg_line_column = .{ .line = dbg_stmt.line, .column = dbg_stmt.column, @@ -5290,7 +5196,6 @@ fn condBr(func: *Func, cond_ty: Type, condition: MCValue) !Mir.Inst.Index { return try func.addInst(.{ .tag = .beq, - .ops = .rr_inst, .data = .{ .b_type = .{ .rs1 = cond_reg, @@ -5332,8 +5237,7 @@ fn isNull(func: *Func, inst: Air.Inst.Index, opt_ty: Type, opt_mcv: MCValue) !MC .register => |opt_reg| { if (some_info.off == 0) { _ = try func.addInst(.{ - .tag = .pseudo, - .ops = .pseudo_compare, + .tag = .pseudo_compare, .data = .{ .compare = .{ .op = .eq, @@ -5382,8 +5286,7 @@ fn isNull(func: *Func, inst: Air.Inst.Index, opt_ty: Type, opt_mcv: MCValue) !MC defer func.register_manager.unlockReg(opt_reg_lock); _ = try func.addInst(.{ - .tag = .pseudo, - .ops = .pseudo_compare, + .tag = .pseudo_compare, .data = .{ .compare = .{ .op = .eq, @@ -5432,8 +5335,7 @@ fn airIsNonNull(func: *Func, inst: Air.Inst.Index) !void { assert(result == .register); _ = try func.addInst(.{ - .tag = .pseudo, - .ops = .pseudo_not, + .tag = .pseudo_not, .data = .{ .rr = .{ .rd = result.register, @@ -5565,8 +5467,7 @@ fn isNonErr(func: *Func, inst: Air.Inst.Index, eu_ty: Type, eu_mcv: MCValue) !MC switch (is_err_res) { .register => |reg| { _ = try func.addInst(.{ - .tag = .pseudo, - .ops = .pseudo_not, + .tag = .pseudo_not, .data = .{ .rr = .{ .rd = reg, @@ -5633,11 +5534,11 @@ fn airLoop(func: *Func, inst: Air.Inst.Index) !void { /// Send control flow to the `index` of `func.code`. fn jump(func: *Func, index: Mir.Inst.Index) !Mir.Inst.Index { return func.addInst(.{ - .tag = .pseudo, - .ops = .pseudo_j, - .data = .{ + .tag = .pseudo_j, + .data = .{ .j_type = .{ + .rd = .zero, .inst = index, - }, + } }, }); } @@ -5727,8 +5628,7 @@ fn airSwitchBr(func: *Func, inst: Air.Inst.Index) !void { if (!(i < relocs.len - 1)) { _ = try func.addInst(.{ - .tag = .pseudo, - .ops = .pseudo_not, + .tag = .pseudo_not, .data = .{ .rr = .{ .rd = cmp_reg, .rs = cmp_reg, @@ -5775,18 +5675,13 @@ fn airSwitchBr(func: *Func, inst: Air.Inst.Index) !void { fn performReloc(func: *Func, inst: Mir.Inst.Index) void { const tag = func.mir_instructions.items(.tag)[inst]; - const ops = func.mir_instructions.items(.ops)[inst]; const target: Mir.Inst.Index = @intCast(func.mir_instructions.len); switch (tag) { - .bne, .beq, => func.mir_instructions.items(.data)[inst].b_type.inst = target, .jal => func.mir_instructions.items(.data)[inst].j_type.inst = target, - .pseudo => switch (ops) { - .pseudo_j => func.mir_instructions.items(.data)[inst].inst = target, - else => std.debug.panic("TODO: performReloc {s}", .{@tagName(ops)}), - }, + .pseudo_j => func.mir_instructions.items(.data)[inst].j_type.inst = target, else => std.debug.panic("TODO: performReloc {s}", .{@tagName(tag)}), } } @@ -5873,7 +5768,6 @@ fn airBoolOp(func: *Func, inst: Air.Inst.Index) !void { _ = try func.addInst(.{ .tag = if (tag == .bool_or) .@"or" else .@"and", - .ops = .rrr, .data = .{ .r_type = .{ .rd = result_reg, .rs1 = lhs_reg, @@ -5885,7 +5779,6 @@ fn airBoolOp(func: *Func, inst: Air.Inst.Index) !void { if (func.wantSafety()) { _ = try func.addInst(.{ .tag = .andi, - .ops = .rri, .data = .{ .i_type = .{ .rd = result_reg, .rs1 = result_reg, @@ -5970,11 +5863,10 @@ fn airAsm(func: *Func, inst: Air.Inst.Index) !void { const asm_source = std.mem.sliceAsBytes(func.air.extra[extra_i..])[0..extra.data.source_len]; - if (std.meta.stringToEnum(Mir.Inst.Tag, asm_source)) |tag| { + if (std.meta.stringToEnum(Mnemonic, asm_source)) |tag| { _ = try func.addInst(.{ .tag = tag, - .ops = .none, - .data = undefined, + .data = .none, }); } else { return func.fail("TODO: asm_source {s}", .{asm_source}); @@ -6116,7 +6008,6 @@ fn genInlineMemcpy( // if count is 0, there's nothing to copy _ = try func.addInst(.{ .tag = .beq, - .ops = .rr_inst, .data = .{ .b_type = .{ .rs1 = count, .rs2 = .zero, @@ -6127,7 +6018,6 @@ fn genInlineMemcpy( // lb tmp, 0(src) const first_inst = try func.addInst(.{ .tag = .lb, - .ops = .rri, .data = .{ .i_type = .{ .rd = tmp, @@ -6140,7 +6030,6 @@ fn genInlineMemcpy( // sb tmp, 0(dst) _ = try func.addInst(.{ .tag = .sb, - .ops = .rri, .data = .{ .i_type = .{ .rd = dst, @@ -6153,7 +6042,6 @@ fn genInlineMemcpy( // dec count by 1 _ = try func.addInst(.{ .tag = .addi, - .ops = .rri, .data = .{ .i_type = .{ .rd = count, @@ -6166,7 +6054,6 @@ fn genInlineMemcpy( // branch if count is 0 _ = try func.addInst(.{ .tag = .beq, - .ops = .rr_inst, .data = .{ .b_type = .{ .inst = @intCast(func.mir_instructions.len + 4), // points after the last inst @@ -6179,7 +6066,6 @@ fn genInlineMemcpy( // increment the pointers _ = try func.addInst(.{ .tag = .addi, - .ops = .rri, .data = .{ .i_type = .{ .rd = src, @@ -6191,7 +6077,6 @@ fn genInlineMemcpy( _ = try func.addInst(.{ .tag = .addi, - .ops = .rri, .data = .{ .i_type = .{ .rd = dst, @@ -6203,9 +6088,11 @@ fn genInlineMemcpy( // jump back to start of loop _ = try func.addInst(.{ - .tag = .pseudo, - .ops = .pseudo_j, - .data = .{ .inst = first_inst }, + .tag = .pseudo_j, + .data = .{ .j_type = .{ + .rd = .zero, + .inst = first_inst, + } }, }); } @@ -6230,7 +6117,6 @@ fn genInlineMemset( // sb src, 0(dst) const first_inst = try func.addInst(.{ .tag = .sb, - .ops = .rri, .data = .{ .i_type = .{ .rd = dst, @@ -6243,7 +6129,6 @@ fn genInlineMemset( // dec count by 1 _ = try func.addInst(.{ .tag = .addi, - .ops = .rri, .data = .{ .i_type = .{ .rd = count, @@ -6256,7 +6141,6 @@ fn genInlineMemset( // branch if count is 0 _ = try func.addInst(.{ .tag = .beq, - .ops = .rr_inst, .data = .{ .b_type = .{ .inst = @intCast(func.mir_instructions.len + 4), // points after the last inst @@ -6269,7 +6153,6 @@ fn genInlineMemset( // increment the pointers _ = try func.addInst(.{ .tag = .addi, - .ops = .rri, .data = .{ .i_type = .{ .rd = dst, @@ -6281,11 +6164,11 @@ fn genInlineMemset( // jump back to start of loop _ = try func.addInst(.{ - .tag = .pseudo, - .ops = .pseudo_j, - .data = .{ + .tag = .pseudo_j, + .data = .{ .j_type = .{ + .rd = .zero, .inst = first_inst, - }, + } }, }); } @@ -6331,7 +6214,6 @@ fn genSetReg(func: *Func, ty: Type, reg: Register, src_mcv: MCValue) InnerError! if (math.minInt(i12) <= x and x <= math.maxInt(i12)) { _ = try func.addInst(.{ .tag = .addi, - .ops = .rri, .data = .{ .i_type = .{ .rd = reg, .rs1 = .zero, @@ -6345,7 +6227,6 @@ fn genSetReg(func: *Func, ty: Type, reg: Register, src_mcv: MCValue) InnerError! _ = try func.addInst(.{ .tag = .lui, - .ops = .ri, .data = .{ .u_type = .{ .rd = reg, .imm20 = Immediate.s(hi20), @@ -6353,7 +6234,6 @@ fn genSetReg(func: *Func, ty: Type, reg: Register, src_mcv: MCValue) InnerError! }); _ = try func.addInst(.{ .tag = .addi, - .ops = .rri, .data = .{ .i_type = .{ .rd = reg, .rs1 = reg, @@ -6376,7 +6256,6 @@ fn genSetReg(func: *Func, ty: Type, reg: Register, src_mcv: MCValue) InnerError! _ = try func.addInst(.{ .tag = .slli, - .ops = .rri, .data = .{ .i_type = .{ .rd = reg, .rs1 = reg, @@ -6386,7 +6265,6 @@ fn genSetReg(func: *Func, ty: Type, reg: Register, src_mcv: MCValue) InnerError! _ = try func.addInst(.{ .tag = .add, - .ops = .rrr, .data = .{ .r_type = .{ .rd = reg, .rs1 = reg, @@ -6423,8 +6301,7 @@ fn genSetReg(func: *Func, ty: Type, reg: Register, src_mcv: MCValue) InnerError! // mv reg, src_reg _ = try func.addInst(.{ - .tag = .pseudo, - .ops = .pseudo_mv, + .tag = .pseudo_mv, .data = .{ .rr = .{ .rd = reg, .rs = src_reg, @@ -6445,8 +6322,7 @@ fn genSetReg(func: *Func, ty: Type, reg: Register, src_mcv: MCValue) InnerError! try func.genCopy(ty, .{ .register = reg }, .{ .indirect = .{ .reg = addr_reg } }); } else { _ = try func.addInst(.{ - .tag = .pseudo, - .ops = .pseudo_load_rm, + .tag = .pseudo_load_rm, .data = .{ .rm = .{ .r = reg, .m = .{ @@ -6466,7 +6342,6 @@ fn genSetReg(func: *Func, ty: Type, reg: Register, src_mcv: MCValue) InnerError! _ = try func.addInst(.{ .tag = .ld, - .ops = .rri, .data = .{ .i_type = .{ .rd = reg, .rs1 = reg, @@ -6476,8 +6351,7 @@ fn genSetReg(func: *Func, ty: Type, reg: Register, src_mcv: MCValue) InnerError! }, .lea_frame, .register_offset => { _ = try func.addInst(.{ - .tag = .pseudo, - .ops = .pseudo_lea_rm, + .tag = .pseudo_lea_rm, .data = .{ .rm = .{ .r = reg, @@ -6505,7 +6379,7 @@ fn genSetReg(func: *Func, ty: Type, reg: Register, src_mcv: MCValue) InnerError! }); }, .indirect => |reg_off| { - const load_tag: Mir.Inst.Tag = switch (reg.class()) { + const load_tag: Mnemonic = switch (reg.class()) { .float => switch (abi_size) { 1 => unreachable, // Zig does not support 8-bit floats 2 => return func.fail("TODO: genSetReg indirect 16-bit float", .{}), @@ -6544,8 +6418,7 @@ fn genSetReg(func: *Func, ty: Type, reg: Register, src_mcv: MCValue) InnerError! }); _ = try func.addInst(.{ - .tag = .pseudo, - .ops = .pseudo_load_rm, + .tag = .pseudo_load_rm, .data = .{ .rm = .{ .r = reg, .m = .{ @@ -6565,7 +6438,6 @@ fn genSetReg(func: *Func, ty: Type, reg: Register, src_mcv: MCValue) InnerError! _ = try func.addInst(.{ .tag = load_tag, - .ops = .rri, .data = .{ .i_type = .{ .rd = reg, .rs1 = reg_off.reg, @@ -6578,13 +6450,12 @@ fn genSetReg(func: *Func, ty: Type, reg: Register, src_mcv: MCValue) InnerError! const atom_index = try func.owner.getSymbolIndex(func); _ = try func.addInst(.{ - .tag = .pseudo, - .ops = .pseudo_load_symbol, - .data = .{ .payload = try func.addExtra(Mir.LoadSymbolPayload{ - .register = reg.encodeId(), + .tag = .pseudo_load_symbol, + .data = .{ .reloc = .{ + .register = reg, .atom_index = atom_index, .sym_index = sym_off.sym, - }) }, + } }, }); }, .load_symbol => { @@ -6676,8 +6547,7 @@ fn genSetMem( }); _ = try func.addInst(.{ - .tag = .pseudo, - .ops = .pseudo_store_rm, + .tag = .pseudo_store_rm, .data = .{ .rm = .{ .r = reg, .m = .{ @@ -6716,8 +6586,7 @@ fn genSetMem( })); const frame_mcv: MCValue = .{ .load_frame = .{ .index = frame_index } }; _ = try func.addInst(.{ - .tag = .pseudo, - .ops = .pseudo_store_rm, + .tag = .pseudo_store_rm, .data = .{ .rm = .{ .r = reg, .m = .{ @@ -6732,8 +6601,7 @@ fn genSetMem( try func.genSetMem(base, disp, ty, frame_mcv); try func.freeValue(frame_mcv); } else _ = try func.addInst(.{ - .tag = .pseudo, - .ops = .pseudo_store_rm, + .tag = .pseudo_store_rm, .data = .{ .rm = .{ .r = reg, .m = .{ @@ -6852,9 +6720,59 @@ fn airFloatFromInt(func: *Func, inst: Air.Inst.Index) !void { fn airIntFromFloat(func: *Func, inst: Air.Inst.Index) !void { const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else return func.fail("TODO implement airIntFromFloat for {}", .{ - func.target.cpu.arch, - }); + const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else result: { + const pt = func.pt; + const zcu = pt.zcu; + + const operand = try func.resolveInst(ty_op.operand); + const src_ty = func.typeOf(ty_op.operand); + const dst_ty = func.typeOfIndex(inst); + + const is_unsigned = dst_ty.isUnsignedInt(zcu); + const src_bits = src_ty.bitSize(pt); + const dst_bits = dst_ty.bitSize(pt); + + const float_mod: enum { s, d } = switch (src_bits) { + 32 => .s, + 64 => .d, + else => return func.fail("TODO: airIntFromFloat src size {d}", .{src_bits}), + }; + + const int_mod: Mir.FcvtOp = switch (dst_bits) { + 32 => if (is_unsigned) .wu else .w, + 64 => if (is_unsigned) .lu else .l, + else => return func.fail("TODO: airIntFromFloat dst size: {d}", .{dst_bits}), + }; + + const src_reg, const src_lock = try func.promoteReg(src_ty, operand); + defer if (src_lock) |lock| func.register_manager.unlockReg(lock); + + const dst_reg, const dst_lock = try func.allocReg(.int); + defer func.register_manager.unlockReg(dst_lock); + + _ = try func.addInst(.{ + .tag = switch (float_mod) { + .s => switch (int_mod) { + .l => .fcvtls, + .lu => .fcvtlus, + .w => .fcvtws, + .wu => .fcvtwus, + }, + .d => switch (int_mod) { + .l => .fcvtld, + .lu => .fcvtlud, + .w => .fcvtwd, + .wu => .fcvtwud, + }, + }, + .data = .{ .rr = .{ + .rd = dst_reg, + .rs = src_reg, + } }, + }); + + break :result .{ .register = dst_reg }; + }; return func.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } @@ -6917,8 +6835,7 @@ fn airAtomicRmw(func: *Func, inst: Air.Inst.Index) !void { }; _ = try func.addInst(.{ - .tag = .pseudo, - .ops = .pseudo_amo, + .tag = .pseudo_amo, .data = .{ .amo = .{ .rd = result_mcv.register, .rs1 = ptr_register, @@ -6961,15 +6878,12 @@ fn airAtomicLoad(func: *Func, inst: Air.Inst.Index) !void { if (order == .seq_cst) { _ = try func.addInst(.{ - .tag = .pseudo, - .ops = .pseudo_fence, - .data = .{ - .fence = .{ - .pred = .rw, - .succ = .rw, - .fm = .none, - }, - }, + .tag = .pseudo_fence, + .data = .{ .fence = .{ + .pred = .rw, + .succ = .rw, + .fm = .none, + } }, }); } @@ -6982,8 +6896,7 @@ fn airAtomicLoad(func: *Func, inst: Air.Inst.Index) !void { // Make sure all previous reads happen before any reading or writing accurs. .seq_cst, .acquire => { _ = try func.addInst(.{ - .tag = .pseudo, - .ops = .pseudo_fence, + .tag = .pseudo_fence, .data = .{ .fence = .{ .pred = .r, @@ -7015,8 +6928,7 @@ fn airAtomicStore(func: *Func, inst: Air.Inst.Index, order: std.builtin.AtomicOr .unordered, .monotonic => {}, .release, .seq_cst => { _ = try func.addInst(.{ - .tag = .pseudo, - .ops = .pseudo_fence, + .tag = .pseudo_fence, .data = .{ .fence = .{ .pred = .rw, @@ -7183,7 +7095,6 @@ fn airTagName(func: *Func, inst: Air.Inst.Index) !void { try func.genSetReg(Type.u64, .ra, .{ .load_symbol = .{ .sym = sym.esym_index } }); _ = try func.addInst(.{ .tag = .jalr, - .ops = .rri, .data = .{ .i_type = .{ .rd = .ra, .rs1 = .ra, diff --git a/src/arch/riscv64/Emit.zig b/src/arch/riscv64/Emit.zig index bc972e86b9..3fd5c405f8 100644 --- a/src/arch/riscv64/Emit.zig +++ b/src/arch/riscv64/Emit.zig @@ -40,7 +40,7 @@ pub fn emitMir(emit: *Emit) Error!void { .source = start_offset, .target = target, .offset = 0, - .enc = std.meta.activeTag(lowered_inst.encoding.data), + .fmt = std.meta.activeTag(lowered_inst), }), .load_symbol_reloc => |symbol| { const is_obj_or_static_lib = switch (emit.lower.output_mode) { @@ -49,46 +49,45 @@ pub fn emitMir(emit: *Emit) Error!void { .Lib => emit.lower.link_mode == .static, }; - if (emit.bin_file.cast(link.File.Elf)) |elf_file| { - const atom_ptr = elf_file.symbol(symbol.atom_index).atom(elf_file).?; - const sym_index = elf_file.zigObjectPtr().?.symbol(symbol.sym_index); - const sym = elf_file.symbol(sym_index); + const elf_file = emit.bin_file.cast(link.File.Elf).?; - var hi_r_type: u32 = @intFromEnum(std.elf.R_RISCV.HI20); - var lo_r_type: u32 = @intFromEnum(std.elf.R_RISCV.LO12_I); + const atom_ptr = elf_file.symbol(symbol.atom_index).atom(elf_file).?; + const sym_index = elf_file.zigObjectPtr().?.symbol(symbol.sym_index); + const sym = elf_file.symbol(sym_index); - if (sym.flags.needs_zig_got and !is_obj_or_static_lib) { - _ = try sym.getOrCreateZigGotEntry(sym_index, elf_file); + var hi_r_type: u32 = @intFromEnum(std.elf.R_RISCV.HI20); + var lo_r_type: u32 = @intFromEnum(std.elf.R_RISCV.LO12_I); - hi_r_type = Elf.R_ZIG_GOT_HI20; - lo_r_type = Elf.R_ZIG_GOT_LO12; - } + if (sym.flags.needs_zig_got and !is_obj_or_static_lib) { + _ = try sym.getOrCreateZigGotEntry(sym_index, elf_file); - try atom_ptr.addReloc(elf_file, .{ - .r_offset = start_offset, - .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | hi_r_type, - .r_addend = 0, - }); + hi_r_type = Elf.R_ZIG_GOT_HI20; + lo_r_type = Elf.R_ZIG_GOT_LO12; + } - try atom_ptr.addReloc(elf_file, .{ - .r_offset = start_offset + 4, - .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | lo_r_type, - .r_addend = 0, - }); - } else unreachable; + try atom_ptr.addReloc(elf_file, .{ + .r_offset = start_offset, + .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | hi_r_type, + .r_addend = 0, + }); + + try atom_ptr.addReloc(elf_file, .{ + .r_offset = start_offset + 4, + .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | lo_r_type, + .r_addend = 0, + }); }, .call_extern_fn_reloc => |symbol| { - if (emit.bin_file.cast(link.File.Elf)) |elf_file| { - const atom_ptr = elf_file.symbol(symbol.atom_index).atom(elf_file).?; + const elf_file = emit.bin_file.cast(link.File.Elf).?; + const atom_ptr = elf_file.symbol(symbol.atom_index).atom(elf_file).?; - const r_type: u32 = @intFromEnum(std.elf.R_RISCV.CALL_PLT); + const r_type: u32 = @intFromEnum(std.elf.R_RISCV.CALL_PLT); - try atom_ptr.addReloc(elf_file, .{ - .r_offset = start_offset, - .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | r_type, - .r_addend = 0, - }); - } else return emit.fail("TODO: call_extern_fn_reloc non-ELF", .{}); + try atom_ptr.addReloc(elf_file, .{ + .r_offset = start_offset, + .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | r_type, + .r_addend = 0, + }); }, }; } @@ -98,40 +97,37 @@ pub fn emitMir(emit: *Emit) Error!void { const mir_inst = emit.lower.mir.instructions.get(mir_index); switch (mir_inst.tag) { else => unreachable, - .pseudo => switch (mir_inst.ops) { - else => unreachable, - .pseudo_dbg_prologue_end => { - switch (emit.debug_output) { - .dwarf => |dw| { - try dw.setPrologueEnd(); - log.debug("mirDbgPrologueEnd (line={d}, col={d})", .{ - emit.prev_di_line, emit.prev_di_column, - }); - try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column); - }, - .plan9 => {}, - .none => {}, - } - }, - .pseudo_dbg_line_column => try emit.dbgAdvancePCAndLine( - mir_inst.data.pseudo_dbg_line_column.line, - mir_inst.data.pseudo_dbg_line_column.column, - ), - .pseudo_dbg_epilogue_begin => { - switch (emit.debug_output) { - .dwarf => |dw| { - try dw.setEpilogueBegin(); - log.debug("mirDbgEpilogueBegin (line={d}, col={d})", .{ - emit.prev_di_line, emit.prev_di_column, - }); - try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column); - }, - .plan9 => {}, - .none => {}, - } - }, - .pseudo_dead => {}, + .pseudo_dbg_prologue_end => { + switch (emit.debug_output) { + .dwarf => |dw| { + try dw.setPrologueEnd(); + log.debug("mirDbgPrologueEnd (line={d}, col={d})", .{ + emit.prev_di_line, emit.prev_di_column, + }); + try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column); + }, + .plan9 => {}, + .none => {}, + } }, + .pseudo_dbg_line_column => try emit.dbgAdvancePCAndLine( + mir_inst.data.pseudo_dbg_line_column.line, + mir_inst.data.pseudo_dbg_line_column.column, + ), + .pseudo_dbg_epilogue_begin => { + switch (emit.debug_output) { + .dwarf => |dw| { + try dw.setEpilogueBegin(); + log.debug("mirDbgEpilogueBegin (line={d}, col={d})", .{ + emit.prev_di_line, emit.prev_di_column, + }); + try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column); + }, + .plan9 => {}, + .none => {}, + } + }, + .pseudo_dead => {}, } } } @@ -151,8 +147,8 @@ const Reloc = struct { target: Mir.Inst.Index, /// Offset of the relocation within the instruction. offset: u32, - /// Encoding of the instruction, used to determine how to modify it. - enc: Encoding.InstEnc, + /// Format of the instruction, used to determine how to modify it. + fmt: encoding.Lir.Format, }; fn fixupRelocs(emit: *Emit) Error!void { @@ -164,12 +160,10 @@ fn fixupRelocs(emit: *Emit) Error!void { const disp = @as(i32, @intCast(target)) - @as(i32, @intCast(reloc.source)); const code: *[4]u8 = emit.code.items[reloc.source + reloc.offset ..][0..4]; - log.debug("disp: {x}", .{disp}); - - switch (reloc.enc) { + switch (reloc.fmt) { .J => riscv_util.writeInstJ(code, @bitCast(disp)), .B => riscv_util.writeInstB(code, @bitCast(disp)), - else => return emit.fail("tried to reloc encoding type {s}", .{@tagName(reloc.enc)}), + else => return emit.fail("tried to reloc format type {s}", .{@tagName(reloc.fmt)}), } } } @@ -209,5 +203,5 @@ const Emit = @This(); const Lower = @import("Lower.zig"); const Mir = @import("Mir.zig"); const riscv_util = @import("../../link/riscv.zig"); -const Encoding = @import("Encoding.zig"); const Elf = @import("../../link/Elf.zig"); +const encoding = @import("encoding.zig"); diff --git a/src/arch/riscv64/Encoding.zig b/src/arch/riscv64/Encoding.zig deleted file mode 100644 index b1b0712779..0000000000 --- a/src/arch/riscv64/Encoding.zig +++ /dev/null @@ -1,1136 +0,0 @@ -mnemonic: Mnemonic, -data: Data, - -const OpCode = enum(u7) { - LOAD = 0b0000011, - LOAD_FP = 0b0000111, - MISC_MEM = 0b0001111, - OP_IMM = 0b0010011, - AUIPC = 0b0010111, - OP_IMM_32 = 0b0011011, - STORE = 0b0100011, - STORE_FP = 0b0100111, - AMO = 0b0101111, - OP_V = 0b1010111, - OP = 0b0110011, - OP_32 = 0b0111011, - LUI = 0b0110111, - MADD = 0b1000011, - MSUB = 0b1000111, - NMSUB = 0b1001011, - NMADD = 0b1001111, - OP_FP = 0b1010011, - OP_IMM_64 = 0b1011011, - BRANCH = 0b1100011, - JALR = 0b1100111, - JAL = 0b1101111, - SYSTEM = 0b1110011, - OP_64 = 0b1111011, - NONE = 0b00000000, -}; - -const FpFmt = enum(u2) { - /// 32-bit single-precision - S = 0b00, - /// 64-bit double-precision - D = 0b01, - - // H = 0b10, unused in the G extension - - /// 128-bit quad-precision - Q = 0b11, -}; - -const AmoWidth = enum(u3) { - W = 0b010, - D = 0b011, -}; - -const FenceMode = enum(u4) { - none = 0b0000, - tso = 0b1000, -}; - -const Enc = struct { - opcode: OpCode, - - data: union(enum) { - /// funct3 + funct7 - ff: struct { - funct3: u3, - funct7: u7, - }, - amo: struct { - funct5: u5, - width: AmoWidth, - }, - fence: struct { - funct3: u3, - fm: FenceMode, - }, - /// funct5 + rm + fmt - fmt: struct { - funct5: u5, - rm: u3, - fmt: FpFmt, - }, - /// funct3 - f: struct { - funct3: u3, - }, - /// typ + funct3 + has_5 - sh: struct { - typ: u6, - 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) { - // zig fmt: off - @"8" = 0b000, - @"16" = 0b101, - @"32" = 0b110, - @"64" = 0b111, - // zig fmt: on - }; - - const VecType = enum(u3) { - OPIVV = 0b000, - OPFVV = 0b001, - OPMVV = 0b010, - OPIVI = 0b011, - OPIVX = 0b100, - OPFVF = 0b101, - OPMVX = 0b110, - }; -}; - -// TODO: this is basically a copy of the MIR table, we should be able to de-dupe them somehow. -pub const Mnemonic = enum { - // base mnemonics - - // I Type - ld, - lw, - lwu, - lh, - lhu, - lb, - lbu, - - sltiu, - xori, - andi, - - slli, - srli, - srai, - - slliw, - srliw, - sraiw, - - addi, - jalr, - - vsetivli, - vsetvli, - - // U Type - lui, - auipc, - - // S Type - sd, - sw, - sh, - sb, - - // J Type - jal, - - // B Type - beq, - - // R Type - add, - addw, - sub, - subw, - @"and", - @"or", - slt, - sltu, - xor, - - sll, - srl, - sra, - - sllw, - srlw, - sraw, - - // System - ecall, - ebreak, - unimp, - - csrrs, - - // M extension - mul, - mulw, - - mulh, - mulhu, - mulhsu, - - div, - divu, - - divw, - divuw, - - rem, - remu, - - remw, - remuw, - - // F extension (32-bit float) - fadds, - fsubs, - fmuls, - fdivs, - - fmins, - fmaxs, - - fsqrts, - - flw, - fsw, - - feqs, - flts, - fles, - - fsgnjns, - fsgnjxs, - - // D extension (64-bit float) - faddd, - fsubd, - fmuld, - fdivd, - - fmind, - fmaxd, - - fsqrtd, - - fld, - fsd, - - feqd, - fltd, - fled, - - fsgnjnd, - fsgnjxd, - - // V Extension - vle8v, - vle16v, - vle32v, - vle64v, - - vse8v, - vse16v, - vse32v, - vse64v, - - vsoxei8v, - - vaddvv, - vsubvv, - - vfaddvv, - vfsubvv, - - vmulvv, - vfmulvv, - - vadcvv, - - vmvvx, - - vslidedownvx, - - // MISC - fence, - fencetso, - - // AMO - amoswapw, - amoaddw, - amoandw, - amoorw, - amoxorw, - amomaxw, - amominw, - amomaxuw, - amominuw, - - amoswapd, - amoaddd, - amoandd, - amoord, - amoxord, - amomaxd, - amomind, - amomaxud, - amominud, - - // TODO: Q extension - - // Zbb Extension - clz, - clzw, - - pub fn encoding(mnem: Mnemonic) Enc { - return switch (mnem) { - // zig fmt: off - - // OP - - .add => .{ .opcode = .OP, .data = .{ .ff = .{ .funct3 = 0b000, .funct7 = 0b0000000 } } }, - .sub => .{ .opcode = .OP, .data = .{ .ff = .{ .funct3 = 0b000, .funct7 = 0b0100000 } } }, - - .@"and" => .{ .opcode = .OP, .data = .{ .ff = .{ .funct3 = 0b111, .funct7 = 0b0000000 } } }, - .@"or" => .{ .opcode = .OP, .data = .{ .ff = .{ .funct3 = 0b110, .funct7 = 0b0000000 } } }, - .xor => .{ .opcode = .OP, .data = .{ .ff = .{ .funct3 = 0b100, .funct7 = 0b0000000 } } }, - - .sltu => .{ .opcode = .OP, .data = .{ .ff = .{ .funct3 = 0b011, .funct7 = 0b0000000 } } }, - .slt => .{ .opcode = .OP, .data = .{ .ff = .{ .funct3 = 0b010, .funct7 = 0b0000000 } } }, - - .mul => .{ .opcode = .OP, .data = .{ .ff = .{ .funct3 = 0b000, .funct7 = 0b0000001 } } }, - .mulh => .{ .opcode = .OP, .data = .{ .ff = .{ .funct3 = 0b001, .funct7 = 0b0000001 } } }, - .mulhsu => .{ .opcode = .OP, .data = .{ .ff = .{ .funct3 = 0b010, .funct7 = 0b0000001 } } }, - .mulhu => .{ .opcode = .OP, .data = .{ .ff = .{ .funct3 = 0b011, .funct7 = 0b0000001 } } }, - - .div => .{ .opcode = .OP, .data = .{ .ff = .{ .funct3 = 0b100, .funct7 = 0b0000001 } } }, - .divu => .{ .opcode = .OP, .data = .{ .ff = .{ .funct3 = 0b101, .funct7 = 0b0000001 } } }, - - .rem => .{ .opcode = .OP, .data = .{ .ff = .{ .funct3 = 0b110, .funct7 = 0b0000001 } } }, - .remu => .{ .opcode = .OP, .data = .{ .ff = .{ .funct3 = 0b111, .funct7 = 0b0000001 } } }, - - .sll => .{ .opcode = .OP, .data = .{ .ff = .{ .funct3 = 0b001, .funct7 = 0b0000000 } } }, - .srl => .{ .opcode = .OP, .data = .{ .ff = .{ .funct3 = 0b101, .funct7 = 0b0000000 } } }, - .sra => .{ .opcode = .OP, .data = .{ .ff = .{ .funct3 = 0b101, .funct7 = 0b0100000 } } }, - - - // OP_IMM - - .addi => .{ .opcode = .OP_IMM, .data = .{ .f = .{ .funct3 = 0b000 } } }, - .andi => .{ .opcode = .OP_IMM, .data = .{ .f = .{ .funct3 = 0b111 } } }, - .xori => .{ .opcode = .OP_IMM, .data = .{ .f = .{ .funct3 = 0b100 } } }, - - .sltiu => .{ .opcode = .OP_IMM, .data = .{ .f = .{ .funct3 = 0b011 } } }, - - .slli => .{ .opcode = .OP_IMM, .data = .{ .sh = .{ .typ = 0b000000, .funct3 = 0b001, .has_5 = true } } }, - .srli => .{ .opcode = .OP_IMM, .data = .{ .sh = .{ .typ = 0b000000, .funct3 = 0b101, .has_5 = true } } }, - .srai => .{ .opcode = .OP_IMM, .data = .{ .sh = .{ .typ = 0b010000, .funct3 = 0b101, .has_5 = true } } }, - - .clz => .{ .opcode = .OP_IMM, .data = .{ .ff = .{ .funct3 = 0b001, .funct7 = 0b0110000 } } }, - - // OP_IMM_32 - - .slliw => .{ .opcode = .OP_IMM_32, .data = .{ .sh = .{ .typ = 0b000000, .funct3 = 0b001, .has_5 = false } } }, - .srliw => .{ .opcode = .OP_IMM_32, .data = .{ .sh = .{ .typ = 0b000000, .funct3 = 0b101, .has_5 = false } } }, - .sraiw => .{ .opcode = .OP_IMM_32, .data = .{ .sh = .{ .typ = 0b010000, .funct3 = 0b101, .has_5 = false } } }, - - .clzw => .{ .opcode = .OP_IMM_32, .data = .{ .ff = .{ .funct3 = 0b001, .funct7 = 0b0110000 } } }, - - // OP_32 - - .addw => .{ .opcode = .OP_32, .data = .{ .ff = .{ .funct3 = 0b000, .funct7 = 0b0000000 } } }, - .subw => .{ .opcode = .OP_32, .data = .{ .ff = .{ .funct3 = 0b000, .funct7 = 0b0100000 } } }, - .mulw => .{ .opcode = .OP_32, .data = .{ .ff = .{ .funct3 = 0b000, .funct7 = 0b0000001 } } }, - - .divw => .{ .opcode = .OP_32, .data = .{ .ff = .{ .funct3 = 0b100, .funct7 = 0b0000001 } } }, - .divuw => .{ .opcode = .OP_32, .data = .{ .ff = .{ .funct3 = 0b101, .funct7 = 0b0000001 } } }, - - .remw => .{ .opcode = .OP_32, .data = .{ .ff = .{ .funct3 = 0b110, .funct7 = 0b0000001 } } }, - .remuw => .{ .opcode = .OP_32, .data = .{ .ff = .{ .funct3 = 0b111, .funct7 = 0b0000001 } } }, - - .sllw => .{ .opcode = .OP_32, .data = .{ .ff = .{ .funct3 = 0b001, .funct7 = 0b0000000 } } }, - .srlw => .{ .opcode = .OP_32, .data = .{ .ff = .{ .funct3 = 0b101, .funct7 = 0b0000000 } } }, - .sraw => .{ .opcode = .OP_32, .data = .{ .ff = .{ .funct3 = 0b101, .funct7 = 0b0100000 } } }, - - - // OP_FP - - .fadds => .{ .opcode = .OP_FP, .data = .{ .fmt = .{ .funct5 = 0b00000, .fmt = .S, .rm = 0b111 } } }, - .faddd => .{ .opcode = .OP_FP, .data = .{ .fmt = .{ .funct5 = 0b00000, .fmt = .D, .rm = 0b111 } } }, - - .fsubs => .{ .opcode = .OP_FP, .data = .{ .fmt = .{ .funct5 = 0b00001, .fmt = .S, .rm = 0b111 } } }, - .fsubd => .{ .opcode = .OP_FP, .data = .{ .fmt = .{ .funct5 = 0b00001, .fmt = .D, .rm = 0b111 } } }, - - .fmuls => .{ .opcode = .OP_FP, .data = .{ .fmt = .{ .funct5 = 0b00010, .fmt = .S, .rm = 0b111 } } }, - .fmuld => .{ .opcode = .OP_FP, .data = .{ .fmt = .{ .funct5 = 0b00010, .fmt = .D, .rm = 0b111 } } }, - - .fdivs => .{ .opcode = .OP_FP, .data = .{ .fmt = .{ .funct5 = 0b00011, .fmt = .S, .rm = 0b111 } } }, - .fdivd => .{ .opcode = .OP_FP, .data = .{ .fmt = .{ .funct5 = 0b00011, .fmt = .D, .rm = 0b111 } } }, - - .fmins => .{ .opcode = .OP_FP, .data = .{ .fmt = .{ .funct5 = 0b00101, .fmt = .S, .rm = 0b000 } } }, - .fmind => .{ .opcode = .OP_FP, .data = .{ .fmt = .{ .funct5 = 0b00101, .fmt = .D, .rm = 0b000 } } }, - - .fmaxs => .{ .opcode = .OP_FP, .data = .{ .fmt = .{ .funct5 = 0b00101, .fmt = .S, .rm = 0b001 } } }, - .fmaxd => .{ .opcode = .OP_FP, .data = .{ .fmt = .{ .funct5 = 0b00101, .fmt = .D, .rm = 0b001 } } }, - - .fsqrts => .{ .opcode = .OP_FP, .data = .{ .fmt = .{ .funct5 = 0b01011, .fmt = .S, .rm = 0b111 } } }, - .fsqrtd => .{ .opcode = .OP_FP, .data = .{ .fmt = .{ .funct5 = 0b01011, .fmt = .D, .rm = 0b111 } } }, - - .fles => .{ .opcode = .OP_FP, .data = .{ .fmt = .{ .funct5 = 0b10100, .fmt = .S, .rm = 0b000 } } }, - .fled => .{ .opcode = .OP_FP, .data = .{ .fmt = .{ .funct5 = 0b10100, .fmt = .D, .rm = 0b000 } } }, - - .flts => .{ .opcode = .OP_FP, .data = .{ .fmt = .{ .funct5 = 0b10100, .fmt = .S, .rm = 0b001 } } }, - .fltd => .{ .opcode = .OP_FP, .data = .{ .fmt = .{ .funct5 = 0b10100, .fmt = .D, .rm = 0b001 } } }, - - .feqs => .{ .opcode = .OP_FP, .data = .{ .fmt = .{ .funct5 = 0b10100, .fmt = .S, .rm = 0b010 } } }, - .feqd => .{ .opcode = .OP_FP, .data = .{ .fmt = .{ .funct5 = 0b10100, .fmt = .D, .rm = 0b010 } } }, - - .fsgnjns => .{ .opcode = .OP_FP, .data = .{ .fmt = .{ .funct5 = 0b00100, .fmt = .S, .rm = 0b000 } } }, - .fsgnjnd => .{ .opcode = .OP_FP, .data = .{ .fmt = .{ .funct5 = 0b00100, .fmt = .D, .rm = 0b000 } } }, - - .fsgnjxs => .{ .opcode = .OP_FP, .data = .{ .fmt = .{ .funct5 = 0b00100, .fmt = .S, .rm = 0b0010} } }, - .fsgnjxd => .{ .opcode = .OP_FP, .data = .{ .fmt = .{ .funct5 = 0b00100, .fmt = .D, .rm = 0b0010} } }, - - - // LOAD - - .lb => .{ .opcode = .LOAD, .data = .{ .f = .{ .funct3 = 0b000 } } }, - .lh => .{ .opcode = .LOAD, .data = .{ .f = .{ .funct3 = 0b001 } } }, - .lw => .{ .opcode = .LOAD, .data = .{ .f = .{ .funct3 = 0b010 } } }, - .ld => .{ .opcode = .LOAD, .data = .{ .f = .{ .funct3 = 0b011 } } }, - .lbu => .{ .opcode = .LOAD, .data = .{ .f = .{ .funct3 = 0b100 } } }, - .lhu => .{ .opcode = .LOAD, .data = .{ .f = .{ .funct3 = 0b101 } } }, - .lwu => .{ .opcode = .LOAD, .data = .{ .f = .{ .funct3 = 0b110 } } }, - - - // STORE - - .sb => .{ .opcode = .STORE, .data = .{ .f = .{ .funct3 = 0b000 } } }, - .sh => .{ .opcode = .STORE, .data = .{ .f = .{ .funct3 = 0b001 } } }, - .sw => .{ .opcode = .STORE, .data = .{ .f = .{ .funct3 = 0b010 } } }, - .sd => .{ .opcode = .STORE, .data = .{ .f = .{ .funct3 = 0b011 } } }, - - - // LOAD_FP - - .flw => .{ .opcode = .LOAD_FP, .data = .{ .f = .{ .funct3 = 0b010 } } }, - .fld => .{ .opcode = .LOAD_FP, .data = .{ .f = .{ .funct3 = 0b011 } } }, - - .vle8v => .{ .opcode = .LOAD_FP, .data = .{ .vecls = .{ .width = .@"8", .umop = .unit, .vm = true, .mop = .unit, .mew = false, .nf = 0b000 } } }, - .vle16v => .{ .opcode = .LOAD_FP, .data = .{ .vecls = .{ .width = .@"16", .umop = .unit, .vm = true, .mop = .unit, .mew = false, .nf = 0b000 } } }, - .vle32v => .{ .opcode = .LOAD_FP, .data = .{ .vecls = .{ .width = .@"32", .umop = .unit, .vm = true, .mop = .unit, .mew = false, .nf = 0b000 } } }, - .vle64v => .{ .opcode = .LOAD_FP, .data = .{ .vecls = .{ .width = .@"64", .umop = .unit, .vm = true, .mop = .unit, .mew = false, .nf = 0b000 } } }, - - - // STORE_FP - - .fsw => .{ .opcode = .STORE_FP, .data = .{ .f = .{ .funct3 = 0b010 } } }, - .fsd => .{ .opcode = .STORE_FP, .data = .{ .f = .{ .funct3 = 0b011 } } }, - - .vse8v => .{ .opcode = .STORE_FP, .data = .{ .vecls = .{ .width = .@"8", .umop = .unit, .vm = true, .mop = .unit, .mew = false, .nf = 0b000 } } }, - .vse16v => .{ .opcode = .STORE_FP, .data = .{ .vecls = .{ .width = .@"16", .umop = .unit, .vm = true, .mop = .unit, .mew = false, .nf = 0b000 } } }, - .vse32v => .{ .opcode = .STORE_FP, .data = .{ .vecls = .{ .width = .@"32", .umop = .unit, .vm = true, .mop = .unit, .mew = false, .nf = 0b000 } } }, - .vse64v => .{ .opcode = .STORE_FP, .data = .{ .vecls = .{ .width = .@"64", .umop = .unit, .vm = true, .mop = .unit, .mew = false, .nf = 0b000 } } }, - - .vsoxei8v => .{ .opcode = .STORE_FP, .data = .{ .vecls = .{ .width = .@"8", .umop = .unit, .vm = true, .mop = .ord, .mew = false, .nf = 0b000 } } }, - - // JALR - - .jalr => .{ .opcode = .JALR, .data = .{ .f = .{ .funct3 = 0b000 } } }, - - - // LUI - - .lui => .{ .opcode = .LUI, .data = .{ .none = {} } }, - - - // AUIPC - - .auipc => .{ .opcode = .AUIPC, .data = .{ .none = {} } }, - - - // JAL - - .jal => .{ .opcode = .JAL, .data = .{ .none = {} } }, - - - // BRANCH - - .beq => .{ .opcode = .BRANCH, .data = .{ .f = .{ .funct3 = 0b000 } } }, - - - // SYSTEM - - .ecall => .{ .opcode = .SYSTEM, .data = .{ .f = .{ .funct3 = 0b000 } } }, - .ebreak => .{ .opcode = .SYSTEM, .data = .{ .f = .{ .funct3 = 0b000 } } }, - - .csrrs => .{ .opcode = .SYSTEM, .data = .{ .f = .{ .funct3 = 0b010 } } }, - - - // NONE - - .unimp => .{ .opcode = .NONE, .data = .{ .f = .{ .funct3 = 0b000 } } }, - - - // MISC_MEM - - .fence => .{ .opcode = .MISC_MEM, .data = .{ .fence = .{ .funct3 = 0b000, .fm = .none } } }, - .fencetso => .{ .opcode = .MISC_MEM, .data = .{ .fence = .{ .funct3 = 0b000, .fm = .tso } } }, - - - // AMO - - .amoaddw => .{ .opcode = .AMO, .data = .{ .amo = .{ .width = .W, .funct5 = 0b00000 } } }, - .amoswapw => .{ .opcode = .AMO, .data = .{ .amo = .{ .width = .W, .funct5 = 0b00001 } } }, - // LR.W - // SC.W - .amoxorw => .{ .opcode = .AMO, .data = .{ .amo = .{ .width = .W, .funct5 = 0b00100 } } }, - .amoandw => .{ .opcode = .AMO, .data = .{ .amo = .{ .width = .W, .funct5 = 0b01100 } } }, - .amoorw => .{ .opcode = .AMO, .data = .{ .amo = .{ .width = .W, .funct5 = 0b01000 } } }, - .amominw => .{ .opcode = .AMO, .data = .{ .amo = .{ .width = .W, .funct5 = 0b10000 } } }, - .amomaxw => .{ .opcode = .AMO, .data = .{ .amo = .{ .width = .W, .funct5 = 0b10100 } } }, - .amominuw => .{ .opcode = .AMO, .data = .{ .amo = .{ .width = .W, .funct5 = 0b11000 } } }, - .amomaxuw => .{ .opcode = .AMO, .data = .{ .amo = .{ .width = .W, .funct5 = 0b11100 } } }, - - .amoaddd => .{ .opcode = .AMO, .data = .{ .amo = .{ .width = .D, .funct5 = 0b00000 } } }, - .amoswapd => .{ .opcode = .AMO, .data = .{ .amo = .{ .width = .D, .funct5 = 0b00001 } } }, - // LR.D - // SC.D - .amoxord => .{ .opcode = .AMO, .data = .{ .amo = .{ .width = .D, .funct5 = 0b00100 } } }, - .amoandd => .{ .opcode = .AMO, .data = .{ .amo = .{ .width = .D, .funct5 = 0b01100 } } }, - .amoord => .{ .opcode = .AMO, .data = .{ .amo = .{ .width = .D, .funct5 = 0b01000 } } }, - .amomind => .{ .opcode = .AMO, .data = .{ .amo = .{ .width = .D, .funct5 = 0b10000 } } }, - .amomaxd => .{ .opcode = .AMO, .data = .{ .amo = .{ .width = .D, .funct5 = 0b10100 } } }, - .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 } } }, - .vsubvv => .{ .opcode = .OP_V, .data = .{ .vecmath = .{ .vm = true, .funct6 = 0b000010, .funct3 = .OPIVV } } }, - .vmulvv => .{ .opcode = .OP_V, .data = .{ .vecmath = .{ .vm = true, .funct6 = 0b100101, .funct3 = .OPIVV } } }, - - .vfaddvv => .{ .opcode = .OP_V, .data = .{ .vecmath = .{ .vm = true, .funct6 = 0b000000, .funct3 = .OPFVV } } }, - .vfsubvv => .{ .opcode = .OP_V, .data = .{ .vecmath = .{ .vm = true, .funct6 = 0b000010, .funct3 = .OPFVV } } }, - .vfmulvv => .{ .opcode = .OP_V, .data = .{ .vecmath = .{ .vm = true, .funct6 = 0b100100, .funct3 = .OPFVV } } }, - - .vadcvv => .{ .opcode = .OP_V, .data = .{ .vecmath = .{ .vm = true, .funct6 = 0b010000, .funct3 = .OPMVV } } }, - .vmvvx => .{ .opcode = .OP_V, .data = .{ .vecmath = .{ .vm = true, .funct6 = 0b010111, .funct3 = .OPIVX } } }, - - .vslidedownvx => .{ .opcode = .OP_V, .data = .{ .vecmath = .{ .vm = true, .funct6 = 0b001111, .funct3 = .OPIVX } } }, - - // zig fmt: on - }; - } -}; - -pub const InstEnc = enum { - R, - R4, - I, - S, - B, - U, - J, - fence, - amo, - system, - - pub fn fromMnemonic(mnem: Mnemonic) InstEnc { - return switch (mnem) { - .addi, - .jalr, - .sltiu, - .xori, - .andi, - - .slli, - .srli, - .srai, - - .slliw, - .srliw, - .sraiw, - - .ld, - .lw, - .lwu, - .lh, - .lhu, - .lb, - .lbu, - - .flw, - .fld, - - .csrrs, - .vsetivli, - .vsetvli, - => .I, - - .lui, - .auipc, - => .U, - - .sd, - .sw, - .sh, - .sb, - - .fsd, - .fsw, - => .S, - - .jal, - => .J, - - .beq, - => .B, - - .slt, - .sltu, - - .sll, - .srl, - .sra, - - .sllw, - .srlw, - .sraw, - - .div, - .divu, - .divw, - .divuw, - - .rem, - .remu, - .remw, - .remuw, - - .xor, - .@"and", - .@"or", - - .add, - .addw, - - .sub, - .subw, - - .mul, - .mulw, - .mulh, - .mulhu, - .mulhsu, - - .fadds, - .faddd, - - .fsubs, - .fsubd, - - .fmuls, - .fmuld, - - .fdivs, - .fdivd, - - .fmins, - .fmind, - - .fmaxs, - .fmaxd, - - .fsqrts, - .fsqrtd, - - .fles, - .fled, - - .flts, - .fltd, - - .feqs, - .feqd, - - .fsgnjns, - .fsgnjnd, - - .fsgnjxs, - .fsgnjxd, - - .vle8v, - .vle16v, - .vle32v, - .vle64v, - - .vse8v, - .vse16v, - .vse32v, - .vse64v, - - .vsoxei8v, - - .vaddvv, - .vsubvv, - .vmulvv, - .vfaddvv, - .vfsubvv, - .vfmulvv, - .vadcvv, - .vmvvx, - .vslidedownvx, - - .clz, - .clzw, - => .R, - - .ecall, - .ebreak, - .unimp, - => .system, - - .fence, - .fencetso, - => .fence, - - .amoswapw, - .amoaddw, - .amoandw, - .amoorw, - .amoxorw, - .amomaxw, - .amominw, - .amomaxuw, - .amominuw, - - .amoswapd, - .amoaddd, - .amoandd, - .amoord, - .amoxord, - .amomaxd, - .amomind, - .amomaxud, - .amominud, - => .amo, - }; - } - - pub fn opsList(enc: InstEnc) [5]std.meta.FieldEnum(Operand) { - return switch (enc) { - // zig fmt: off - .R => .{ .reg, .reg, .reg, .none, .none, }, - .R4 => .{ .reg, .reg, .reg, .reg, .none, }, - .I => .{ .reg, .reg, .imm, .none, .none, }, - .S => .{ .reg, .reg, .imm, .none, .none, }, - .B => .{ .reg, .reg, .imm, .none, .none, }, - .U => .{ .reg, .imm, .none, .none, .none, }, - .J => .{ .reg, .imm, .none, .none, .none, }, - .system => .{ .none, .none, .none, .none, .none, }, - .fence => .{ .barrier, .barrier, .none, .none, .none, }, - .amo => .{ .reg, .reg, .reg, .barrier, .barrier }, - // zig fmt: on - }; - } -}; - -pub const Data = union(InstEnc) { - R: packed struct { - opcode: u7, - rd: u5, - funct3: u3, - rs1: u5, - rs2: u5, - funct7: u7, - }, - R4: packed struct { - opcode: u7, - rd: u5, - funct3: u3, - rs1: u5, - rs2: u5, - funct2: u2, - rs3: u5, - }, - I: packed struct { - opcode: u7, - rd: u5, - funct3: u3, - rs1: u5, - imm0_11: u12, - }, - S: packed struct { - opcode: u7, - imm0_4: u5, - funct3: u3, - rs1: u5, - rs2: u5, - imm5_11: u7, - }, - B: packed struct { - opcode: u7, - imm11: u1, - imm1_4: u4, - funct3: u3, - rs1: u5, - rs2: u5, - imm5_10: u6, - imm12: u1, - }, - U: packed struct { - opcode: u7, - rd: u5, - imm12_31: u20, - }, - J: packed struct { - opcode: u7, - rd: u5, - imm12_19: u8, - imm11: u1, - imm1_10: u10, - imm20: u1, - }, - fence: packed struct { - opcode: u7, - rd: u5 = 0, - funct3: u3, - rs1: u5 = 0, - succ: u4, - pred: u4, - fm: u4, - }, - amo: packed struct { - opcode: u7, - rd: u5, - funct3: u3, - rs1: u5, - rs2: u5, - rl: bool, - aq: bool, - funct5: u5, - }, - system: u32, - - comptime { - for (std.meta.fields(Data)) |field| { - assert(@bitSizeOf(field.type) == 32); - } - } - - pub fn toU32(self: Data) u32 { - return switch (self) { - .fence => |v| @as(u32, @intCast(v.opcode)) + (@as(u32, @intCast(v.rd)) << 7) + (@as(u32, @intCast(v.funct3)) << 12) + (@as(u32, @intCast(v.rs1)) << 15) + (@as(u32, @intCast(v.succ)) << 20) + (@as(u32, @intCast(v.pred)) << 24) + (@as(u32, @intCast(v.fm)) << 28), - inline else => |v| @bitCast(v), - .system => unreachable, - }; - } - - pub fn construct(mnem: Mnemonic, ops: []const Operand) !Data { - const inst_enc = InstEnc.fromMnemonic(mnem); - const enc = mnem.encoding(); - - // special mnemonics - switch (mnem) { - .ecall, - .ebreak, - .unimp, - => { - assert(ops.len == 0); - return .{ - .I = .{ - .rd = Register.zero.encodeId(), - .rs1 = Register.zero.encodeId(), - .imm0_11 = switch (mnem) { - .ecall => 0x000, - .ebreak => 0x001, - .unimp => 0x000, - else => unreachable, - }, - - .opcode = @intFromEnum(enc.opcode), - .funct3 = enc.data.f.funct3, - }, - }; - }, - .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 => {}, - } - - switch (inst_enc) { - .R => { - assert(ops.len == 3); - return .{ - .R = switch (enc.data) { - .ff => |ff| .{ - .rd = ops[0].reg.encodeId(), - .rs1 = ops[1].reg.encodeId(), - .rs2 = ops[2].reg.encodeId(), - - .opcode = @intFromEnum(enc.opcode), - .funct3 = ff.funct3, - .funct7 = ff.funct7, - }, - .fmt => |fmt| .{ - .rd = ops[0].reg.encodeId(), - .rs1 = ops[1].reg.encodeId(), - .rs2 = ops[2].reg.encodeId(), - - .opcode = @intFromEnum(enc.opcode), - .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, - }, - }; - }, - .S => { - assert(ops.len == 3); - const umm = ops[2].imm.asBits(u12); - - return .{ - .S = .{ - .imm0_4 = @truncate(umm), - .rs1 = ops[0].reg.encodeId(), - .rs2 = ops[1].reg.encodeId(), - .imm5_11 = @truncate(umm >> 5), - - .opcode = @intFromEnum(enc.opcode), - .funct3 = enc.data.f.funct3, - }, - }; - }, - .I => { - assert(ops.len == 3); - return .{ - .I = switch (enc.data) { - .f => |f| .{ - .rd = ops[0].reg.encodeId(), - .rs1 = ops[1].reg.encodeId(), - .imm0_11 = ops[2].imm.asBits(u12), - - .opcode = @intFromEnum(enc.opcode), - .funct3 = f.funct3, - }, - .sh => |sh| .{ - .rd = ops[0].reg.encodeId(), - .rs1 = ops[1].reg.encodeId(), - .imm0_11 = (@as(u12, sh.typ) << 6) | - if (sh.has_5) ops[2].imm.asBits(u6) else (@as(u6, 0) | ops[2].imm.asBits(u5)), - - .opcode = @intFromEnum(enc.opcode), - .funct3 = sh.funct3, - }, - else => unreachable, - }, - }; - }, - .U => { - assert(ops.len == 2); - return .{ - .U = .{ - .rd = ops[0].reg.encodeId(), - .imm12_31 = ops[1].imm.asBits(u20), - - .opcode = @intFromEnum(enc.opcode), - }, - }; - }, - .J => { - assert(ops.len == 2); - - const umm = ops[1].imm.asBits(u21); - assert(umm % 4 == 0); // misaligned jump target - - return .{ - .J = .{ - .rd = ops[0].reg.encodeId(), - .imm1_10 = @truncate(umm >> 1), - .imm11 = @truncate(umm >> 11), - .imm12_19 = @truncate(umm >> 12), - .imm20 = @truncate(umm >> 20), - - .opcode = @intFromEnum(enc.opcode), - }, - }; - }, - .B => { - assert(ops.len == 3); - - const umm = ops[2].imm.asBits(u13); - assert(umm % 4 == 0); // misaligned branch target - - return .{ - .B = .{ - .rs1 = ops[0].reg.encodeId(), - .rs2 = ops[1].reg.encodeId(), - .imm1_4 = @truncate(umm >> 1), - .imm5_10 = @truncate(umm >> 5), - .imm11 = @truncate(umm >> 11), - .imm12 = @truncate(umm >> 12), - - .opcode = @intFromEnum(enc.opcode), - .funct3 = enc.data.f.funct3, - }, - }; - }, - .fence => { - assert(ops.len == 2); - - const succ = ops[0].barrier; - const pred = ops[1].barrier; - - return .{ - .fence = .{ - .succ = @intFromEnum(succ), - .pred = @intFromEnum(pred), - - .opcode = @intFromEnum(enc.opcode), - .funct3 = enc.data.fence.funct3, - .fm = @intFromEnum(enc.data.fence.fm), - }, - }; - }, - .amo => { - assert(ops.len == 5); - - 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.encodeId(), - .rs1 = rs1.encodeId(), - .rs2 = rs2.encodeId(), - - // TODO: https://github.com/ziglang/zig/issues/20113 - .rl = if (rl == .rl) true else false, - .aq = if (aq == .aq) true else false, - - .opcode = @intFromEnum(enc.opcode), - .funct3 = @intFromEnum(enc.data.amo.width), - .funct5 = enc.data.amo.funct5, - }, - }; - }, - else => std.debug.panic("TODO: construct {s}", .{@tagName(inst_enc)}), - } - } -}; - -pub fn findByMnemonic(mnem: Mnemonic, ops: []const Operand) !?Encoding { - if (!verifyOps(mnem, ops)) return null; - - return .{ - .mnemonic = mnem, - .data = try Data.construct(mnem, ops), - }; -} - -fn verifyOps(mnem: Mnemonic, ops: []const Operand) bool { - const inst_enc = InstEnc.fromMnemonic(mnem); - const list = std.mem.sliceTo(&inst_enc.opsList(), .none); - for (list, ops) |l, o| if (l != std.meta.activeTag(o)) return false; - return true; -} - -const std = @import("std"); -const assert = std.debug.assert; -const log = std.log.scoped(.encoding); - -const Encoding = @This(); -const bits = @import("bits.zig"); -const Register = bits.Register; -const encoder = @import("encoder.zig"); -const Instruction = encoder.Instruction; -const Operand = Instruction.Operand; -const OperandEnum = std.meta.FieldEnum(Operand); diff --git a/src/arch/riscv64/Lower.zig b/src/arch/riscv64/Lower.zig index f33fe68fae..c8a4ec942c 100644 --- a/src/arch/riscv64/Lower.zig +++ b/src/arch/riscv64/Lower.zig @@ -61,451 +61,427 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index, options: struct { log.debug("lowerMir {}", .{inst}); switch (inst.tag) { else => try lower.generic(inst), - .pseudo => switch (inst.ops) { - .pseudo_dbg_line_column, - .pseudo_dbg_epilogue_begin, - .pseudo_dbg_prologue_end, - .pseudo_dead, - => {}, + .pseudo_dbg_line_column, + .pseudo_dbg_epilogue_begin, + .pseudo_dbg_prologue_end, + .pseudo_dead, + => {}, - .pseudo_load_rm, .pseudo_store_rm => { - const rm = inst.data.rm; + .pseudo_load_rm, .pseudo_store_rm => { + const rm = inst.data.rm; - const frame_loc: Mir.FrameLoc = if (options.allow_frame_locs) - rm.m.toFrameLoc(lower.mir) - else - .{ .base = .s0, .disp = 0 }; + const frame_loc: Mir.FrameLoc = if (options.allow_frame_locs) + rm.m.toFrameLoc(lower.mir) + else + .{ .base = .s0, .disp = 0 }; - switch (inst.ops) { - .pseudo_load_rm => { - const dest_reg = rm.r; - const dest_reg_class = dest_reg.class(); + switch (inst.tag) { + .pseudo_load_rm => { + const dest_reg = rm.r; + const dest_reg_class = dest_reg.class(); - const src_size = rm.m.mod.size; - const unsigned = rm.m.mod.unsigned; + const src_size = rm.m.mod.size; + const unsigned = rm.m.mod.unsigned; - 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, - }, - .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 => .vle8v, - .hword => .vle32v, - .word => .vle32v, - .dword => .vle64v, - }, - }; - - switch (dest_reg_class) { - .int, .float => { - try lower.emit(tag, &.{ - .{ .reg = rm.r }, - .{ .reg = frame_loc.base }, - .{ .imm = Immediate.s(frame_loc.disp) }, - }); - }, - .vector => { - assert(frame_loc.disp == 0); - try lower.emit(tag, &.{ - .{ .reg = rm.r }, - .{ .reg = frame_loc.base }, - .{ .reg = .zero }, - }); - }, - } - }, - .pseudo_store_rm => { - const src_reg = rm.r; - const src_reg_class = src_reg.class(); - - const dest_size = rm.m.mod.size; - - const tag: Encoding.Mnemonic = switch (src_reg_class) { - .int => switch (dest_size) { - .byte => .sb, - .hword => .sh, - .word => .sw, - .dword => .sd, - }, - .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 => .vse8v, - .hword => .vse16v, - .word => .vse32v, - .dword => .vse64v, - }, - }; - - switch (src_reg_class) { - .int, .float => { - try lower.emit(tag, &.{ - .{ .reg = frame_loc.base }, - .{ .reg = rm.r }, - .{ .imm = Immediate.s(frame_loc.disp) }, - }); - }, - .vector => { - assert(frame_loc.disp == 0); - try lower.emit(tag, &.{ - .{ .reg = rm.r }, - .{ .reg = frame_loc.base }, - .{ .reg = .zero }, - }); - }, - } - }, - else => unreachable, - } - }, - - .pseudo_mv => { - const rr = inst.data.rr; - - const dst_class = rr.rd.class(); - const src_class = rr.rs.class(); - - 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 }, - }); + const mnem: 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, }, - .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) }, + .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 => .vle8v, + .hword => .vle32v, + .word => .vle32v, + .dword => .vle64v, + }, + }; + + switch (dest_reg_class) { + .int, .float => { + try lower.emit(mnem, &.{ + .{ .reg = rm.r }, + .{ .reg = frame_loc.base }, + .{ .imm = Immediate.s(frame_loc.disp) }, }); }, .vector => { - try lower.emit(.vmvvx, &.{ - .{ .reg = rr.rd }, - .{ .reg = rr.rs }, - .{ .reg = .x0 }, - }); - }, - .float => return lower.fail("TODO: lowerMir pseudo_mv int -> {s}", .{@tagName(dst_class)}), - }, - .vector => switch (dst_class) { - .int => { - try lower.emit(.vadcvv, &.{ - .{ .reg = rr.rd }, + assert(frame_loc.disp == 0); + try lower.emit(mnem, &.{ + .{ .reg = rm.r }, + .{ .reg = frame_loc.base }, .{ .reg = .zero }, - .{ .reg = rr.rs }, }); }, - .float, .vector => return lower.fail("TODO: lowerMir pseudo_mv vector -> {s}", .{@tagName(dst_class)}), - }, - } - }, + } + }, + .pseudo_store_rm => { + const src_reg = rm.r; + const src_reg_class = src_reg.class(); - .pseudo_j => { - try lower.emit(.jal, &.{ - .{ .reg = .zero }, - .{ .imm = lower.reloc(.{ .inst = inst.data.inst }) }, - }); - }, + const dest_size = rm.m.mod.size; - .pseudo_spill_regs => try lower.pushPopRegList(true, inst.data.reg_list), - .pseudo_restore_regs => try lower.pushPopRegList(false, inst.data.reg_list), - - .pseudo_load_symbol => { - const payload = inst.data.payload; - const data = lower.mir.extraData(Mir.LoadSymbolPayload, payload).data; - const dst_reg: bits.Register = @enumFromInt(data.register); - assert(dst_reg.class() == .int); - - try lower.emit(.lui, &.{ - .{ .reg = dst_reg }, - .{ .imm = lower.reloc(.{ - .load_symbol_reloc = .{ - .atom_index = data.atom_index, - .sym_index = data.sym_index, + const mnem: Mnemonic = switch (src_reg_class) { + .int => switch (dest_size) { + .byte => .sb, + .hword => .sh, + .word => .sw, + .dword => .sd, }, - }) }, - }); + .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 => .vse8v, + .hword => .vse16v, + .word => .vse32v, + .dword => .vse64v, + }, + }; - // the above reloc implies this one - try lower.emit(.addi, &.{ - .{ .reg = dst_reg }, - .{ .reg = dst_reg }, - .{ .imm = Immediate.s(0) }, - }); - }, - - .pseudo_lea_rm => { - const rm = inst.data.rm; - assert(rm.r.class() == .int); - - 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 }, - .{ .reg = frame.base }, - .{ .imm = Immediate.s(frame.disp) }, - }); - }, - - .pseudo_fabs => { - const fabs = inst.data.fabs; - assert(fabs.rs.class() == .float and fabs.rd.class() == .float); - - const mnem: Encoding.Mnemonic = switch (fabs.bits) { - 16 => return lower.fail("TODO: airAbs Float 16", .{}), - 32 => .fsgnjxs, - 64 => .fsgnjxd, - 80 => return lower.fail("TODO: airAbs Float 80", .{}), - 128 => return lower.fail("TODO: airAbs Float 128", .{}), - else => unreachable, - }; - - try lower.emit(mnem, &.{ - .{ .reg = fabs.rs }, - .{ .reg = fabs.rd }, - .{ .reg = fabs.rd }, - }); - }, - - .pseudo_compare => { - const compare = inst.data.compare; - const op = compare.op; - - const rd = compare.rd; - const rs1 = compare.rs1; - const rs2 = compare.rs2; - - const class = rs1.class(); - const ty = compare.ty; - const size = std.math.ceilPowerOfTwo(u64, ty.bitSize(pt)) catch { - return lower.fail("pseudo_compare size {}", .{ty.bitSize(pt)}); - }; - - const is_unsigned = ty.isUnsignedInt(pt.zcu); - const less_than: Encoding.Mnemonic = if (is_unsigned) .sltu else .slt; - - switch (class) { - .int => switch (op) { - .eq => { - try lower.emit(.xor, &.{ - .{ .reg = rd }, - .{ .reg = rs1 }, - .{ .reg = rs2 }, - }); - - try lower.emit(.sltiu, &.{ - .{ .reg = rd }, - .{ .reg = rd }, - .{ .imm = Immediate.s(1) }, + switch (src_reg_class) { + .int, .float => { + try lower.emit(mnem, &.{ + .{ .reg = frame_loc.base }, + .{ .reg = rm.r }, + .{ .imm = Immediate.s(frame_loc.disp) }, }); }, - .neq => { - try lower.emit(.xor, &.{ - .{ .reg = rd }, - .{ .reg = rs1 }, - .{ .reg = rs2 }, - }); - - try lower.emit(.sltu, &.{ - .{ .reg = rd }, + .vector => { + assert(frame_loc.disp == 0); + try lower.emit(mnem, &.{ + .{ .reg = rm.r }, + .{ .reg = frame_loc.base }, .{ .reg = .zero }, - .{ .reg = rd }, }); }, - .gt => { - try lower.emit(less_than, &.{ - .{ .reg = rd }, - .{ .reg = rs2 }, - .{ .reg = rs1 }, - }); - }, - .gte => { - try lower.emit(less_than, &.{ - .{ .reg = rd }, - .{ .reg = rs1 }, - .{ .reg = rs2 }, - }); - try lower.emit(.xori, &.{ - .{ .reg = rd }, - .{ .reg = rd }, - .{ .imm = Immediate.s(1) }, - }); - }, - .lt => { - try lower.emit(less_than, &.{ - .{ .reg = rd }, - .{ .reg = rs1 }, - .{ .reg = rs2 }, - }); - }, - .lte => { - try lower.emit(less_than, &.{ - .{ .reg = rd }, - .{ .reg = rs2 }, - .{ .reg = rs1 }, - }); + } + }, + else => unreachable, + } + }, - try lower.emit(.xori, &.{ - .{ .reg = rd }, - .{ .reg = rd }, - .{ .imm = Immediate.s(1) }, - }); - }, + .pseudo_mv => { + const rr = inst.data.rr; + + const dst_class = rr.rd.class(); + const src_class = rr.rs.class(); + + 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 }, + }); }, - .float => switch (op) { - // eq - .eq => { - try lower.emit(if (size == 64) .feqd else .feqs, &.{ - .{ .reg = rd }, - .{ .reg = rs1 }, - .{ .reg = rs2 }, - }); - }, - // !(eq) - .neq => { - try lower.emit(if (size == 64) .feqd else .feqs, &.{ - .{ .reg = rd }, - .{ .reg = rs1 }, - .{ .reg = rs2 }, - }); - try lower.emit(.xori, &.{ - .{ .reg = rd }, - .{ .reg = rd }, - .{ .imm = Immediate.s(1) }, - }); - }, - .lt => { - try lower.emit(if (size == 64) .fltd else .flts, &.{ - .{ .reg = rd }, - .{ .reg = rs1 }, - .{ .reg = rs2 }, - }); - }, - .lte => { - try lower.emit(if (size == 64) .fled else .fles, &.{ - .{ .reg = rd }, - .{ .reg = rs1 }, - .{ .reg = rs2 }, - }); - }, - .gt => { - try lower.emit(if (size == 64) .fltd else .flts, &.{ - .{ .reg = rd }, - .{ .reg = rs2 }, - .{ .reg = rs1 }, - }); - }, - .gte => { - try lower.emit(if (size == 64) .fled else .fles, &.{ - .{ .reg = rd }, - .{ .reg = rs2 }, - .{ .reg = rs1 }, - }); - }, + .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 => return lower.fail("TODO: lowerMir pseudo_cmp vector", .{}), - } - }, + .vector => { + try lower.emit(.vmvvx, &.{ + .{ .reg = rr.rd }, + .{ .reg = rr.rs }, + .{ .reg = .x0 }, + }); + }, + .float => return lower.fail("TODO: lowerMir pseudo_mv int -> {s}", .{@tagName(dst_class)}), + }, + .vector => switch (dst_class) { + .int => { + try lower.emit(.vadcvv, &.{ + .{ .reg = rr.rd }, + .{ .reg = .zero }, + .{ .reg = rr.rs }, + }); + }, + .float, .vector => return lower.fail("TODO: lowerMir pseudo_mv vector -> {s}", .{@tagName(dst_class)}), + }, + } + }, - .pseudo_not => { - const rr = inst.data.rr; - assert(rr.rs.class() == .int and rr.rd.class() == .int); + .pseudo_j => { + const j_type = inst.data.j_type; + try lower.emit(.jal, &.{ + .{ .reg = j_type.rd }, + .{ .imm = lower.reloc(.{ .inst = j_type.inst }) }, + }); + }, - // mask out any other bits that aren't the boolean - try lower.emit(.andi, &.{ - .{ .reg = rr.rs }, - .{ .reg = rr.rs }, - .{ .imm = Immediate.s(1) }, - }); + .pseudo_spill_regs => try lower.pushPopRegList(true, inst.data.reg_list), + .pseudo_restore_regs => try lower.pushPopRegList(false, inst.data.reg_list), - try lower.emit(.sltiu, &.{ - .{ .reg = rr.rd }, - .{ .reg = rr.rs }, - .{ .imm = Immediate.s(1) }, - }); - }, + .pseudo_load_symbol => { + const payload = inst.data.reloc; + const dst_reg = payload.register; + assert(dst_reg.class() == .int); - .pseudo_extern_fn_reloc => { - const inst_reloc = inst.data.reloc; + try lower.emit(.lui, &.{ + .{ .reg = dst_reg }, + .{ .imm = lower.reloc(.{ + .load_symbol_reloc = .{ + .atom_index = payload.atom_index, + .sym_index = payload.sym_index, + }, + }) }, + }); - try lower.emit(.auipc, &.{ - .{ .reg = .ra }, - .{ .imm = lower.reloc( - .{ .call_extern_fn_reloc = .{ - .atom_index = inst_reloc.atom_index, - .sym_index = inst_reloc.sym_index, - } }, - ) }, - }); + // the reloc above implies this one + try lower.emit(.addi, &.{ + .{ .reg = dst_reg }, + .{ .reg = dst_reg }, + .{ .imm = Immediate.s(0) }, + }); + }, - try lower.emit(.jalr, &.{ - .{ .reg = .ra }, - .{ .reg = .ra }, - .{ .imm = Immediate.s(0) }, - }); - }, + .pseudo_lea_rm => { + const rm = inst.data.rm; + assert(rm.r.class() == .int); - .pseudo_amo => { - const amo = inst.data.amo; - const is_d = amo.ty.abiSize(pt) == 8; - const is_un = amo.ty.isUnsignedInt(pt.zcu); + const frame: Mir.FrameLoc = if (options.allow_frame_locs) + rm.m.toFrameLoc(lower.mir) + else + .{ .base = .s0, .disp = 0 }; - const mnem: Encoding.Mnemonic = switch (amo.op) { - // zig fmt: off - .SWAP => if (is_d) .amoswapd else .amoswapw, - .ADD => if (is_d) .amoaddd else .amoaddw, - .AND => if (is_d) .amoandd else .amoandw, - .OR => if (is_d) .amoord else .amoorw, - .XOR => if (is_d) .amoxord else .amoxorw, - .MAX => if (is_d) if (is_un) .amomaxud else .amomaxd else if (is_un) .amomaxuw else .amomaxw, - .MIN => if (is_d) if (is_un) .amominud else .amomind else if (is_un) .amominuw else .amominw, - // zig fmt: on - }; + try lower.emit(.addi, &.{ + .{ .reg = rm.r }, + .{ .reg = frame.base }, + .{ .imm = Immediate.s(frame.disp) }, + }); + }, - try lower.emit(mnem, &.{ - .{ .reg = inst.data.amo.rd }, - .{ .reg = inst.data.amo.rs1 }, - .{ .reg = inst.data.amo.rs2 }, - .{ .barrier = inst.data.amo.rl }, - .{ .barrier = inst.data.amo.aq }, - }); - }, + .pseudo_compare => { + const compare = inst.data.compare; + const op = compare.op; - .pseudo_fence => { - const fence = inst.data.fence; + const rd = compare.rd; + const rs1 = compare.rs1; + const rs2 = compare.rs2; - try lower.emit(switch (fence.fm) { - .tso => .fencetso, - .none => .fence, - }, &.{ - .{ .barrier = fence.succ }, - .{ .barrier = fence.pred }, - }); - }, + const class = rs1.class(); + const ty = compare.ty; + const size = std.math.ceilPowerOfTwo(u64, ty.bitSize(pt)) catch { + return lower.fail("pseudo_compare size {}", .{ty.bitSize(pt)}); + }; - else => return lower.fail("TODO lower: psuedo {s}", .{@tagName(inst.ops)}), + const is_unsigned = ty.isUnsignedInt(pt.zcu); + const less_than: Mnemonic = if (is_unsigned) .sltu else .slt; + + switch (class) { + .int => switch (op) { + .eq => { + try lower.emit(.xor, &.{ + .{ .reg = rd }, + .{ .reg = rs1 }, + .{ .reg = rs2 }, + }); + + try lower.emit(.sltiu, &.{ + .{ .reg = rd }, + .{ .reg = rd }, + .{ .imm = Immediate.s(1) }, + }); + }, + .neq => { + try lower.emit(.xor, &.{ + .{ .reg = rd }, + .{ .reg = rs1 }, + .{ .reg = rs2 }, + }); + + try lower.emit(.sltu, &.{ + .{ .reg = rd }, + .{ .reg = .zero }, + .{ .reg = rd }, + }); + }, + .gt => { + try lower.emit(less_than, &.{ + .{ .reg = rd }, + .{ .reg = rs2 }, + .{ .reg = rs1 }, + }); + }, + .gte => { + try lower.emit(less_than, &.{ + .{ .reg = rd }, + .{ .reg = rs1 }, + .{ .reg = rs2 }, + }); + try lower.emit(.xori, &.{ + .{ .reg = rd }, + .{ .reg = rd }, + .{ .imm = Immediate.s(1) }, + }); + }, + .lt => { + try lower.emit(less_than, &.{ + .{ .reg = rd }, + .{ .reg = rs1 }, + .{ .reg = rs2 }, + }); + }, + .lte => { + try lower.emit(less_than, &.{ + .{ .reg = rd }, + .{ .reg = rs2 }, + .{ .reg = rs1 }, + }); + + try lower.emit(.xori, &.{ + .{ .reg = rd }, + .{ .reg = rd }, + .{ .imm = Immediate.s(1) }, + }); + }, + }, + .float => switch (op) { + // eq + .eq => { + try lower.emit(if (size == 64) .feqd else .feqs, &.{ + .{ .reg = rd }, + .{ .reg = rs1 }, + .{ .reg = rs2 }, + }); + }, + // !(eq) + .neq => { + try lower.emit(if (size == 64) .feqd else .feqs, &.{ + .{ .reg = rd }, + .{ .reg = rs1 }, + .{ .reg = rs2 }, + }); + try lower.emit(.xori, &.{ + .{ .reg = rd }, + .{ .reg = rd }, + .{ .imm = Immediate.s(1) }, + }); + }, + .lt => { + try lower.emit(if (size == 64) .fltd else .flts, &.{ + .{ .reg = rd }, + .{ .reg = rs1 }, + .{ .reg = rs2 }, + }); + }, + .lte => { + try lower.emit(if (size == 64) .fled else .fles, &.{ + .{ .reg = rd }, + .{ .reg = rs1 }, + .{ .reg = rs2 }, + }); + }, + .gt => { + try lower.emit(if (size == 64) .fltd else .flts, &.{ + .{ .reg = rd }, + .{ .reg = rs2 }, + .{ .reg = rs1 }, + }); + }, + .gte => { + try lower.emit(if (size == 64) .fled else .fles, &.{ + .{ .reg = rd }, + .{ .reg = rs2 }, + .{ .reg = rs1 }, + }); + }, + }, + .vector => return lower.fail("TODO: lowerMir pseudo_cmp vector", .{}), + } + }, + + .pseudo_not => { + const rr = inst.data.rr; + assert(rr.rs.class() == .int and rr.rd.class() == .int); + + // mask out any other bits that aren't the boolean + try lower.emit(.andi, &.{ + .{ .reg = rr.rs }, + .{ .reg = rr.rs }, + .{ .imm = Immediate.s(1) }, + }); + + try lower.emit(.sltiu, &.{ + .{ .reg = rr.rd }, + .{ .reg = rr.rs }, + .{ .imm = Immediate.s(1) }, + }); + }, + + .pseudo_extern_fn_reloc => { + const inst_reloc = inst.data.reloc; + + try lower.emit(.auipc, &.{ + .{ .reg = .ra }, + .{ .imm = lower.reloc( + .{ .call_extern_fn_reloc = .{ + .atom_index = inst_reloc.atom_index, + .sym_index = inst_reloc.sym_index, + } }, + ) }, + }); + + try lower.emit(.jalr, &.{ + .{ .reg = .ra }, + .{ .reg = .ra }, + .{ .imm = Immediate.s(0) }, + }); + }, + + .pseudo_amo => { + const amo = inst.data.amo; + const is_d = amo.ty.abiSize(pt) == 8; + const is_un = amo.ty.isUnsignedInt(pt.zcu); + + const mnem: Mnemonic = switch (amo.op) { + // zig fmt: off + .SWAP => if (is_d) .amoswapd else .amoswapw, + .ADD => if (is_d) .amoaddd else .amoaddw, + .AND => if (is_d) .amoandd else .amoandw, + .OR => if (is_d) .amoord else .amoorw, + .XOR => if (is_d) .amoxord else .amoxorw, + .MAX => if (is_d) if (is_un) .amomaxud else .amomaxd else if (is_un) .amomaxuw else .amomaxw, + .MIN => if (is_d) if (is_un) .amominud else .amomind else if (is_un) .amominuw else .amominw, + // zig fmt: on + }; + + try lower.emit(mnem, &.{ + .{ .reg = inst.data.amo.rd }, + .{ .reg = inst.data.amo.rs1 }, + .{ .reg = inst.data.amo.rs2 }, + .{ .barrier = inst.data.amo.rl }, + .{ .barrier = inst.data.amo.aq }, + }); + }, + + .pseudo_fence => { + const fence = inst.data.fence; + + try lower.emit(switch (fence.fm) { + .tso => .fencetso, + .none => .fence, + }, &.{ + .{ .barrier = fence.succ }, + .{ .barrier = fence.pred }, + }); }, } @@ -516,49 +492,46 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index, options: struct { } fn generic(lower: *Lower, inst: Mir.Inst) Error!void { - const mnemonic = std.meta.stringToEnum(Encoding.Mnemonic, @tagName(inst.tag)) orelse { - return lower.fail("generic inst name '{s}' with op {s} doesn't match with a mnemonic", .{ - @tagName(inst.tag), - @tagName(inst.ops), - }); - }; - try lower.emit(mnemonic, switch (inst.ops) { + const mnemonic = inst.tag; + try lower.emit(mnemonic, switch (inst.data) { .none => &.{}, - .ri => &.{ - .{ .reg = inst.data.u_type.rd }, - .{ .imm = inst.data.u_type.imm20 }, + .u_type => |u| &.{ + .{ .reg = u.rd }, + .{ .imm = u.imm20 }, }, - .rr => &.{ - .{ .reg = inst.data.rr.rd }, - .{ .reg = inst.data.rr.rs }, + .i_type => |i| &.{ + .{ .reg = i.rd }, + .{ .reg = i.rs1 }, + .{ .imm = i.imm12 }, }, - .rri => &.{ - .{ .reg = inst.data.i_type.rd }, - .{ .reg = inst.data.i_type.rs1 }, - .{ .imm = inst.data.i_type.imm12 }, + .rr => |rr| &.{ + .{ .reg = rr.rd }, + .{ .reg = rr.rs }, }, - .rr_inst => &.{ - .{ .reg = inst.data.b_type.rs1 }, - .{ .reg = inst.data.b_type.rs2 }, - .{ .imm = lower.reloc(.{ .inst = inst.data.b_type.inst }) }, + .b_type => |b| &.{ + .{ .reg = b.rs1 }, + .{ .reg = b.rs2 }, + .{ .imm = lower.reloc(.{ .inst = b.inst }) }, }, - .rrr => &.{ - .{ .reg = inst.data.r_type.rd }, - .{ .reg = inst.data.r_type.rs1 }, - .{ .reg = inst.data.r_type.rs2 }, + .r_type => |r| &.{ + .{ .reg = r.rd }, + .{ .reg = r.rs1 }, + .{ .reg = r.rs2 }, }, - .csr => &.{ - .{ .csr = inst.data.csr.csr }, - .{ .reg = inst.data.csr.rs1 }, - .{ .reg = inst.data.csr.rd }, + .csr => |csr| &.{ + .{ .csr = csr.csr }, + .{ .reg = csr.rs1 }, + .{ .reg = csr.rd }, }, - else => return lower.fail("TODO: generic lower ops {s}", .{@tagName(inst.ops)}), + else => return lower.fail("TODO: generic lower {s}", .{@tagName(mnemonic)}), }); } -fn emit(lower: *Lower, mnemonic: Encoding.Mnemonic, ops: []const Instruction.Operand) !void { - lower.result_insts[lower.result_insts_len] = - try Instruction.new(mnemonic, ops); +fn emit(lower: *Lower, mnemonic: Mnemonic, ops: []const Instruction.Operand) !void { + const lir = encoding.Lir.fromMnem(mnemonic); + const inst = Instruction.fromLir(lir, ops); + + lower.result_insts[lower.result_insts_len] = inst; lower.result_insts_len += 1; } @@ -580,7 +553,7 @@ fn pushPopRegList(lower: *Lower, comptime spilling: bool, reg_list: Mir.Register const reg = abi.Registers.all_preserved[i]; const reg_class = reg.class(); - const load_inst: Encoding.Mnemonic, const store_inst: Encoding.Mnemonic = switch (reg_class) { + const load_inst: Mnemonic, const store_inst: Mnemonic = switch (reg_class) { .int => .{ .ld, .sd }, .float => .{ .fld, .fsd }, .vector => unreachable, @@ -618,20 +591,22 @@ fn hasFeature(lower: *Lower, feature: std.Target.riscv.Feature) bool { } const Lower = @This(); - -const abi = @import("abi.zig"); -const assert = std.debug.assert; -const bits = @import("bits.zig"); -const encoder = @import("encoder.zig"); -const link = @import("../../link.zig"); -const Encoding = @import("Encoding.zig"); const std = @import("std"); +const assert = std.debug.assert; const log = std.log.scoped(.lower); -const Air = @import("../../Air.zig"); const Allocator = std.mem.Allocator; const ErrorMsg = Zcu.ErrorMsg; -const Mir = @import("Mir.zig"); + +const link = @import("../../link.zig"); +const Air = @import("../../Air.zig"); const Zcu = @import("../../Zcu.zig"); -const Instruction = encoder.Instruction; + +const Mir = @import("Mir.zig"); +const abi = @import("abi.zig"); +const bits = @import("bits.zig"); +const encoding = @import("encoding.zig"); + +const Mnemonic = @import("mnem.zig").Mnemonic; const Immediate = bits.Immediate; +const Instruction = encoding.Instruction; diff --git a/src/arch/riscv64/Mir.zig b/src/arch/riscv64/Mir.zig index fe4e24e133..bb4fd28536 100644 --- a/src/arch/riscv64/Mir.zig +++ b/src/arch/riscv64/Mir.zig @@ -1,170 +1,17 @@ //! Machine Intermediate Representation. -//! This data is produced by RISCV64 Codegen or RISCV64 assembly parsing -//! These instructions have a 1:1 correspondence with machine code instructions -//! for the target. MIR can be lowered to source-annotated textual assembly code -//! instructions, or it can be lowered to machine code. -//! The main purpose of MIR is to postpone the assignment of offsets until Isel, -//! so that, for example, the smaller encodings of jump instructions can be used. +//! This data is produced by CodeGen.zig instructions: std.MultiArrayList(Inst).Slice, -/// The meaning of this data is determined by `Inst.Tag` value. -extra: []const u32, frame_locs: std.MultiArrayList(FrameLoc).Slice, pub const Inst = struct { - tag: Tag, + tag: Mnemonic, data: Data, - ops: Ops, - /// The position of an MIR instruction within the `Mir` instructions array. pub const Index = u32; - pub const Tag = enum(u16) { - - // base extension - addi, - addiw, - - jalr, - lui, - - @"and", - andi, - - xori, - xor, - @"or", - - ebreak, - ecall, - unimp, - - add, - addw, - sub, - subw, - - sltu, - slt, - - slli, - srli, - srai, - - slliw, - srliw, - sraiw, - - sll, - srl, - sra, - - sllw, - srlw, - sraw, - - jal, - - beq, - bne, - - nop, - - ld, - lw, - lh, - lb, - - sd, - sw, - sh, - sb, - - // M extension - mul, - mulw, - - div, - divu, - divw, - divuw, - - rem, - remu, - remw, - remuw, - - // F extension (32-bit float) - fadds, - fsubs, - fmuls, - fdivs, - - fabss, - - fmins, - fmaxs, - - fsqrts, - - flw, - fsw, - - feqs, - flts, - fles, - - // D extension (64-bit float) - faddd, - fsubd, - fmuld, - fdivd, - - fabsd, - - fmind, - fmaxd, - - fsqrtd, - - fld, - fsd, - - feqd, - fltd, - fled, - - // Zicsr Extension Instructions - csrrs, - - // V Extension Instructions - vsetvli, - vsetivli, - vsetvl, - vaddvv, - vfaddvv, - vsubvv, - vfsubvv, - vmulvv, - vfmulvv, - vslidedownvx, - - // Zbb Extension Instructions - clz, - clzw, - - /// A pseudo-instruction. Used for anything that isn't 1:1 with an - /// assembly instruction. - pseudo, - }; - - /// All instructions have a 4-byte payload, which is contained within - /// this union. `Ops` determines which union field is active, as well as - /// how to interpret the data within. - pub const Data = union { - nop: void, - inst: Index, - payload: u32, + pub const Data = union(enum) { + none: void, r_type: struct { rd: Register, rs1: Register, @@ -194,10 +41,6 @@ pub const Inst = struct { rd: Register, inst: Inst.Index, }, - pseudo_dbg_line_column: struct { - line: u32, - column: u32, - }, rm: struct { r: Register, m: Memory, @@ -208,11 +51,6 @@ pub const Inst = struct { rd: Register, rs: Register, }, - fabs: struct { - rd: Register, - rs: Register, - bits: u16, - }, compare: struct { rd: Register, rs1: Register, @@ -228,6 +66,7 @@ pub const Inst = struct { ty: Type, }, reloc: struct { + register: Register, atom_index: u32, sym_index: u32, }, @@ -253,115 +92,26 @@ pub const Inst = struct { rs1: Register, rd: Register, }, - }; - - pub const Ops = enum { - /// No data associated with this instruction (only mnemonic is used). - none, - /// Two registers - rr, - /// Three registers - rrr, - - /// Two registers + immediate, uses the i_type payload. - rri, - //extern_fn_reloc/ Two registers + another instruction. - rr_inst, - - /// Register + Memory - rm, - - /// Register + Immediate - ri, - - /// Another instruction. - inst, - - /// Control and Status Register Instruction. - csr, - - /// Pseudo-instruction that will generate a backpatched - /// function prologue. - pseudo_prologue, - /// Pseudo-instruction that will generate a backpatched - /// function epilogue - pseudo_epilogue, - - /// Pseudo-instruction: End of prologue - pseudo_dbg_prologue_end, - /// Pseudo-instruction: Beginning of epilogue - pseudo_dbg_epilogue_begin, - /// Pseudo-instruction: Update debug line - pseudo_dbg_line_column, - - /// Pseudo-instruction that loads from memory into a register. - /// - /// Uses `rm` payload. - pseudo_load_rm, - /// Pseudo-instruction that stores from a register into memory - /// - /// Uses `rm` payload. - pseudo_store_rm, - - /// Pseudo-instruction that loads the address of memory into a register. - /// - /// Uses `rm` payload. - pseudo_lea_rm, - - /// Jumps. Uses `inst` payload. - pseudo_j, - - /// Floating point absolute value. - pseudo_fabs, - - /// Dead inst, ignored by the emitter. - pseudo_dead, - - /// Loads the address of a value that hasn't yet been allocated in memory. - /// - /// uses the Mir.LoadSymbolPayload payload. - pseudo_load_symbol, - - /// Moves the value of rs1 to rd. - /// - /// uses the `rr` payload. - pseudo_mv, - - pseudo_restore_regs, - pseudo_spill_regs, - - pseudo_compare, - - /// NOT operation on booleans. Does an `andi reg, reg, 1` to mask out any other bits from the boolean. - pseudo_not, - - /// Generates an auipc + jalr pair, with a R_RISCV_CALL_PLT reloc - pseudo_extern_fn_reloc, - - /// IORW, IORW - pseudo_fence, - - /// Ordering, Src, Addr, Dest - pseudo_amo, + pseudo_dbg_line_column: struct { + line: u32, + column: u32, + }, }; pub fn format( inst: Inst, comptime fmt: []const u8, - options: std.fmt.FormatOptions, + _: std.fmt.FormatOptions, writer: anytype, ) !void { assert(fmt.len == 0); - _ = options; - - try writer.print("Tag: {s}, Ops: {s}", .{ @tagName(inst.tag), @tagName(inst.ops) }); + try writer.print("Tag: {s}, Data: {s}", .{ @tagName(inst.tag), @tagName(inst.data) }); } }; pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void { mir.instructions.deinit(gpa); mir.frame_locs.deinit(gpa); - gpa.free(mir.extra); mir.* = undefined; } @@ -392,25 +142,12 @@ pub const AmoOp = enum(u5) { MIN, }; -/// Returns the requested data, as well as the new index which is at the start of the -/// trailers for the object. -pub fn extraData(mir: Mir, comptime T: type, index: usize) struct { data: T, end: usize } { - const fields = std.meta.fields(T); - var i: usize = index; - var result: T = undefined; - inline for (fields) |field| { - @field(result, field.name) = switch (field.type) { - u32 => mir.extra[i], - i32 => @as(i32, @bitCast(mir.extra[i])), - else => @compileError("bad field type"), - }; - i += 1; - } - return .{ - .data = result, - .end = i, - }; -} +pub const FcvtOp = enum(u5) { + w = 0b00000, + wu = 0b00001, + l = 0b00010, + lu = 0b00011, +}; pub const LoadSymbolPayload = struct { register: u32, @@ -459,10 +196,10 @@ const Mir = @This(); const std = @import("std"); const builtin = @import("builtin"); const Type = @import("../../Type.zig"); +const bits = @import("bits.zig"); const assert = std.debug.assert; -const bits = @import("bits.zig"); const Register = bits.Register; const CSR = bits.CSR; const Immediate = bits.Immediate; @@ -470,3 +207,4 @@ const Memory = bits.Memory; const FrameIndex = bits.FrameIndex; const FrameAddr = @import("CodeGen.zig").FrameAddr; const IntegerBitSet = std.bit_set.IntegerBitSet; +const Mnemonic = @import("mnem.zig").Mnemonic; diff --git a/src/arch/riscv64/bits.zig b/src/arch/riscv64/bits.zig index 60efa077c5..5928449828 100644 --- a/src/arch/riscv64/bits.zig +++ b/src/arch/riscv64/bits.zig @@ -5,7 +5,6 @@ const testing = std.testing; const Target = std.Target; const Zcu = @import("../../Zcu.zig"); -const Encoding = @import("Encoding.zig"); const Mir = @import("Mir.zig"); const abi = @import("abi.zig"); @@ -193,7 +192,7 @@ 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) u8 { + pub fn id(reg: Register) std.math.IntFittingRange(0, @typeInfo(Register).Enum.fields.len) { const base = switch (@intFromEnum(reg)) { // zig fmt: off @intFromEnum(Register.zero) ... @intFromEnum(Register.x31) => @intFromEnum(Register.zero), diff --git a/src/arch/riscv64/encoder.zig b/src/arch/riscv64/encoder.zig deleted file mode 100644 index 2ef5ba03ec..0000000000 --- a/src/arch/riscv64/encoder.zig +++ /dev/null @@ -1,80 +0,0 @@ -pub const Instruction = struct { - encoding: Encoding, - ops: [5]Operand = .{.none} ** 5, - - pub const Operand = union(enum) { - none, - reg: Register, - csr: CSR, - mem: Memory, - imm: Immediate, - barrier: Mir.Barrier, - }; - - pub fn new(mnemonic: Encoding.Mnemonic, ops: []const Operand) !Instruction { - const encoding = (try Encoding.findByMnemonic(mnemonic, ops)) orelse { - std.log.err("no encoding found for: {s} [{s} {s} {s} {s} {s}]", .{ - @tagName(mnemonic), - @tagName(if (ops.len > 0) ops[0] else .none), - @tagName(if (ops.len > 1) ops[1] else .none), - @tagName(if (ops.len > 2) ops[2] else .none), - @tagName(if (ops.len > 3) ops[3] else .none), - @tagName(if (ops.len > 4) ops[4] else .none), - }); - return error.InvalidInstruction; - }; - - var result_ops: [5]Operand = .{.none} ** 5; - @memcpy(result_ops[0..ops.len], ops); - - return .{ - .encoding = encoding, - .ops = result_ops, - }; - } - - pub fn encode(inst: Instruction, writer: anytype) !void { - try writer.writeInt(u32, inst.encoding.data.toU32(), .little); - } - - pub fn format( - inst: Instruction, - comptime fmt: []const u8, - _: std.fmt.FormatOptions, - writer: anytype, - ) !void { - std.debug.assert(fmt.len == 0); - - const encoding = inst.encoding; - - try writer.print("{s} ", .{@tagName(encoding.mnemonic)}); - - var i: u32 = 0; - while (i < inst.ops.len and inst.ops[i] != .none) : (i += 1) { - if (i != inst.ops.len and i != 0) try writer.writeAll(", "); - - switch (@as(Instruction.Operand, inst.ops[i])) { - .none => unreachable, // it's sliced out above - .reg => |reg| try writer.writeAll(@tagName(reg)), - .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)), - } - } - } -}; - -const std = @import("std"); - -const Lower = @import("Lower.zig"); -const Mir = @import("Mir.zig"); -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; - -const log = std.log.scoped(.encode); diff --git a/src/arch/riscv64/encoding.zig b/src/arch/riscv64/encoding.zig new file mode 100644 index 0000000000..a3e20698b6 --- /dev/null +++ b/src/arch/riscv64/encoding.zig @@ -0,0 +1,716 @@ +//! This file is responsible for going from MIR, which is emitted by CodeGen +//! and converting it into Instructions, which can be used as needed. +//! +//! Here we encode how mnemonics relate to opcodes and where their operands go. + +/// Lower Instruction Representation +/// +/// This format encodes a specific instruction, however it's still abstracted +/// away from the true encoding it'll be in. It's meant to make the process of +/// indicating unique encoding data easier. +pub const Lir = struct { + opcode: OpCode, + format: Format, + data: Data, + + pub const Format = enum { + R, + I, + S, + B, + U, + J, + extra, + }; + + const Data = union(enum) { + none, + f: struct { funct3: u3 }, + ff: struct { + funct3: u3, + funct7: u7, + }, + sh: struct { + typ: u6, + funct3: u3, + has_5: bool, + }, + + fmt: struct { + funct5: u5, + rm: u3, + fmt: FpFmt, + }, + fcvt: struct { + funct5: u5, + rm: u3, + fmt: FpFmt, + width: Mir.FcvtOp, + }, + + vecls: struct { + width: VecWidth, + umop: Umop, + vm: bool, + mop: Mop, + mew: bool, + nf: u3, + }, + vecmath: struct { + vm: bool, + funct6: u6, + funct3: VecType, + }, + + amo: struct { + funct5: u5, + width: AmoWidth, + }, + fence: struct { + funct3: u3, + fm: FenceMode, + }, + + /// the mnemonic has some special properities that can't be handled in a generic fashion + extra: Mnemonic, + }; + + const OpCode = enum(u7) { + LOAD = 0b0000011, + LOAD_FP = 0b0000111, + MISC_MEM = 0b0001111, + OP_IMM = 0b0010011, + AUIPC = 0b0010111, + OP_IMM_32 = 0b0011011, + STORE = 0b0100011, + STORE_FP = 0b0100111, + AMO = 0b0101111, + OP_V = 0b1010111, + OP = 0b0110011, + OP_32 = 0b0111011, + LUI = 0b0110111, + MADD = 0b1000011, + MSUB = 0b1000111, + NMSUB = 0b1001011, + NMADD = 0b1001111, + OP_FP = 0b1010011, + OP_IMM_64 = 0b1011011, + BRANCH = 0b1100011, + JALR = 0b1100111, + JAL = 0b1101111, + SYSTEM = 0b1110011, + OP_64 = 0b1111011, + NONE = 0b00000000, + }; + + const FpFmt = enum(u2) { + /// 32-bit single-precision + S = 0b00, + /// 64-bit double-precision + D = 0b01, + + // H = 0b10, unused in the G extension + + /// 128-bit quad-precision + Q = 0b11, + }; + + const AmoWidth = enum(u3) { + W = 0b010, + D = 0b011, + }; + + const FenceMode = enum(u4) { + none = 0b0000, + tso = 0b1000, + }; + + const Mop = enum(u2) { + // zig fmt: off + unit = 0b00, + unord = 0b01, + stride = 0b10, + ord = 0b11, + // zig fmt: on + }; + + const Umop = enum(u5) { + // zig fmt: off + unit = 0b00000, + whole = 0b01000, + mask = 0b01011, + fault = 0b10000, + // zig fmt: on + }; + + const VecWidth = enum(u3) { + // zig fmt: off + @"8" = 0b000, + @"16" = 0b101, + @"32" = 0b110, + @"64" = 0b111, + // zig fmt: on + }; + + const VecType = enum(u3) { + OPIVV = 0b000, + OPFVV = 0b001, + OPMVV = 0b010, + OPIVI = 0b011, + OPIVX = 0b100, + OPFVF = 0b101, + OPMVX = 0b110, + }; + + pub fn fromMnem(mnem: Mnemonic) Lir { + return switch (mnem) { + // zig fmt: off + + // OP + .add => .{ .opcode = .OP, .format = .R, .data = .{ .ff = .{ .funct3 = 0b000, .funct7 = 0b0000000 } } }, + .sub => .{ .opcode = .OP, .format = .R, .data = .{ .ff = .{ .funct3 = 0b000, .funct7 = 0b0100000 } } }, + + .@"and" => .{ .opcode = .OP, .format = .R, .data = .{ .ff = .{ .funct3 = 0b111, .funct7 = 0b0000000 } } }, + .@"or" => .{ .opcode = .OP, .format = .R, .data = .{ .ff = .{ .funct3 = 0b110, .funct7 = 0b0000000 } } }, + .xor => .{ .opcode = .OP, .format = .R, .data = .{ .ff = .{ .funct3 = 0b100, .funct7 = 0b0000000 } } }, + + .sltu => .{ .opcode = .OP, .format = .R, .data = .{ .ff = .{ .funct3 = 0b011, .funct7 = 0b0000000 } } }, + .slt => .{ .opcode = .OP, .format = .R, .data = .{ .ff = .{ .funct3 = 0b010, .funct7 = 0b0000000 } } }, + + .mul => .{ .opcode = .OP, .format = .R, .data = .{ .ff = .{ .funct3 = 0b000, .funct7 = 0b0000001 } } }, + .mulh => .{ .opcode = .OP, .format = .R, .data = .{ .ff = .{ .funct3 = 0b001, .funct7 = 0b0000001 } } }, + .mulhsu => .{ .opcode = .OP, .format = .R, .data = .{ .ff = .{ .funct3 = 0b010, .funct7 = 0b0000001 } } }, + .mulhu => .{ .opcode = .OP, .format = .R, .data = .{ .ff = .{ .funct3 = 0b011, .funct7 = 0b0000001 } } }, + + .div => .{ .opcode = .OP, .format = .R, .data = .{ .ff = .{ .funct3 = 0b100, .funct7 = 0b0000001 } } }, + .divu => .{ .opcode = .OP, .format = .R, .data = .{ .ff = .{ .funct3 = 0b101, .funct7 = 0b0000001 } } }, + + .rem => .{ .opcode = .OP, .format = .R, .data = .{ .ff = .{ .funct3 = 0b110, .funct7 = 0b0000001 } } }, + .remu => .{ .opcode = .OP, .format = .R, .data = .{ .ff = .{ .funct3 = 0b111, .funct7 = 0b0000001 } } }, + + .sll => .{ .opcode = .OP, .format = .R, .data = .{ .ff = .{ .funct3 = 0b001, .funct7 = 0b0000000 } } }, + .srl => .{ .opcode = .OP, .format = .R, .data = .{ .ff = .{ .funct3 = 0b101, .funct7 = 0b0000000 } } }, + .sra => .{ .opcode = .OP, .format = .R, .data = .{ .ff = .{ .funct3 = 0b101, .funct7 = 0b0100000 } } }, + + + // OP_IMM + + .addi => .{ .opcode = .OP_IMM, .format = .I, .data = .{ .f = .{ .funct3 = 0b000 } } }, + .andi => .{ .opcode = .OP_IMM, .format = .I, .data = .{ .f = .{ .funct3 = 0b111 } } }, + .xori => .{ .opcode = .OP_IMM, .format = .I, .data = .{ .f = .{ .funct3 = 0b100 } } }, + + .sltiu => .{ .opcode = .OP_IMM, .format = .I, .data = .{ .f = .{ .funct3 = 0b011 } } }, + + .slli => .{ .opcode = .OP_IMM, .format = .I, .data = .{ .sh = .{ .typ = 0b000000, .funct3 = 0b001, .has_5 = true } } }, + .srli => .{ .opcode = .OP_IMM, .format = .I, .data = .{ .sh = .{ .typ = 0b000000, .funct3 = 0b101, .has_5 = true } } }, + .srai => .{ .opcode = .OP_IMM, .format = .I, .data = .{ .sh = .{ .typ = 0b010000, .funct3 = 0b101, .has_5 = true } } }, + + .clz => .{ .opcode = .OP_IMM, .format = .R, .data = .{ .ff = .{ .funct3 = 0b001, .funct7 = 0b0110000 } } }, + + // OP_IMM_32 + + .slliw => .{ .opcode = .OP_IMM_32, .format = .I, .data = .{ .sh = .{ .typ = 0b000000, .funct3 = 0b001, .has_5 = false } } }, + .srliw => .{ .opcode = .OP_IMM_32, .format = .I, .data = .{ .sh = .{ .typ = 0b000000, .funct3 = 0b101, .has_5 = false } } }, + .sraiw => .{ .opcode = .OP_IMM_32, .format = .I, .data = .{ .sh = .{ .typ = 0b010000, .funct3 = 0b101, .has_5 = false } } }, + + .clzw => .{ .opcode = .OP_IMM_32, .format = .R, .data = .{ .ff = .{ .funct3 = 0b001, .funct7 = 0b0110000 } } }, + + // OP_32 + + .addw => .{ .opcode = .OP_32, .format = .R, .data = .{ .ff = .{ .funct3 = 0b000, .funct7 = 0b0000000 } } }, + .subw => .{ .opcode = .OP_32, .format = .R, .data = .{ .ff = .{ .funct3 = 0b000, .funct7 = 0b0100000 } } }, + .mulw => .{ .opcode = .OP_32, .format = .R, .data = .{ .ff = .{ .funct3 = 0b000, .funct7 = 0b0000001 } } }, + + .divw => .{ .opcode = .OP_32, .format = .R, .data = .{ .ff = .{ .funct3 = 0b100, .funct7 = 0b0000001 } } }, + .divuw => .{ .opcode = .OP_32, .format = .R, .data = .{ .ff = .{ .funct3 = 0b101, .funct7 = 0b0000001 } } }, + + .remw => .{ .opcode = .OP_32, .format = .R, .data = .{ .ff = .{ .funct3 = 0b110, .funct7 = 0b0000001 } } }, + .remuw => .{ .opcode = .OP_32, .format = .R, .data = .{ .ff = .{ .funct3 = 0b111, .funct7 = 0b0000001 } } }, + + .sllw => .{ .opcode = .OP_32, .format = .R, .data = .{ .ff = .{ .funct3 = 0b001, .funct7 = 0b0000000 } } }, + .srlw => .{ .opcode = .OP_32, .format = .R, .data = .{ .ff = .{ .funct3 = 0b101, .funct7 = 0b0000000 } } }, + .sraw => .{ .opcode = .OP_32, .format = .R, .data = .{ .ff = .{ .funct3 = 0b101, .funct7 = 0b0100000 } } }, + + + // OP_FP + + .fadds => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b00000, .fmt = .S, .rm = 0b111 } } }, + .faddd => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b00000, .fmt = .D, .rm = 0b111 } } }, + + .fsubs => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b00001, .fmt = .S, .rm = 0b111 } } }, + .fsubd => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b00001, .fmt = .D, .rm = 0b111 } } }, + + .fmuls => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b00010, .fmt = .S, .rm = 0b111 } } }, + .fmuld => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b00010, .fmt = .D, .rm = 0b111 } } }, + + .fdivs => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b00011, .fmt = .S, .rm = 0b111 } } }, + .fdivd => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b00011, .fmt = .D, .rm = 0b111 } } }, + + .fmins => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b00101, .fmt = .S, .rm = 0b000 } } }, + .fmind => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b00101, .fmt = .D, .rm = 0b000 } } }, + + .fmaxs => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b00101, .fmt = .S, .rm = 0b001 } } }, + .fmaxd => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b00101, .fmt = .D, .rm = 0b001 } } }, + + .fsqrts => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b01011, .fmt = .S, .rm = 0b111 } } }, + .fsqrtd => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b01011, .fmt = .D, .rm = 0b111 } } }, + + .fles => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b10100, .fmt = .S, .rm = 0b000 } } }, + .fled => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b10100, .fmt = .D, .rm = 0b000 } } }, + + .flts => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b10100, .fmt = .S, .rm = 0b001 } } }, + .fltd => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b10100, .fmt = .D, .rm = 0b001 } } }, + + .feqs => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b10100, .fmt = .S, .rm = 0b010 } } }, + .feqd => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b10100, .fmt = .D, .rm = 0b010 } } }, + + .fsgnjns => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b00100, .fmt = .S, .rm = 0b000 } } }, + .fsgnjnd => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b00100, .fmt = .D, .rm = 0b000 } } }, + + .fsgnjxs => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b00100, .fmt = .S, .rm = 0b010 } } }, + .fsgnjxd => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b00100, .fmt = .D, .rm = 0b010 } } }, + + .fcvtws => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fcvt = .{ .funct5 = 0b11000, .fmt = .S, .rm = 0b111, .width = .w } } }, + .fcvtwus => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fcvt = .{ .funct5 = 0b11000, .fmt = .S, .rm = 0b111, .width = .wu } } }, + .fcvtls => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fcvt = .{ .funct5 = 0b11000, .fmt = .S, .rm = 0b111, .width = .l } } }, + .fcvtlus => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fcvt = .{ .funct5 = 0b11000, .fmt = .S, .rm = 0b111, .width = .lu } } }, + + .fcvtwd => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fcvt = .{ .funct5 = 0b11000, .fmt = .D, .rm = 0b111, .width = .w } } }, + .fcvtwud => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fcvt = .{ .funct5 = 0b11000, .fmt = .D, .rm = 0b111, .width = .wu } } }, + .fcvtld => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fcvt = .{ .funct5 = 0b11000, .fmt = .D, .rm = 0b111, .width = .l } } }, + .fcvtlud => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fcvt = .{ .funct5 = 0b11000, .fmt = .D, .rm = 0b111, .width = .lu } } }, + + // LOAD + + .lb => .{ .opcode = .LOAD, .format = .I, .data = .{ .f = .{ .funct3 = 0b000 } } }, + .lh => .{ .opcode = .LOAD, .format = .I, .data = .{ .f = .{ .funct3 = 0b001 } } }, + .lw => .{ .opcode = .LOAD, .format = .I, .data = .{ .f = .{ .funct3 = 0b010 } } }, + .ld => .{ .opcode = .LOAD, .format = .I, .data = .{ .f = .{ .funct3 = 0b011 } } }, + .lbu => .{ .opcode = .LOAD, .format = .I, .data = .{ .f = .{ .funct3 = 0b100 } } }, + .lhu => .{ .opcode = .LOAD, .format = .I, .data = .{ .f = .{ .funct3 = 0b101 } } }, + .lwu => .{ .opcode = .LOAD, .format = .I, .data = .{ .f = .{ .funct3 = 0b110 } } }, + + + // STORE + + .sb => .{ .opcode = .STORE, .format = .S, .data = .{ .f = .{ .funct3 = 0b000 } } }, + .sh => .{ .opcode = .STORE, .format = .S, .data = .{ .f = .{ .funct3 = 0b001 } } }, + .sw => .{ .opcode = .STORE, .format = .S, .data = .{ .f = .{ .funct3 = 0b010 } } }, + .sd => .{ .opcode = .STORE, .format = .S, .data = .{ .f = .{ .funct3 = 0b011 } } }, + + + // LOAD_FP + + .flw => .{ .opcode = .LOAD_FP, .format = .I, .data = .{ .f = .{ .funct3 = 0b010 } } }, + .fld => .{ .opcode = .LOAD_FP, .format = .I, .data = .{ .f = .{ .funct3 = 0b011 } } }, + + .vle8v => .{ .opcode = .LOAD_FP, .format = .R, .data = .{ .vecls = .{ .width = .@"8", .umop = .unit, .vm = true, .mop = .unit, .mew = false, .nf = 0b000 } } }, + .vle16v => .{ .opcode = .LOAD_FP, .format = .R, .data = .{ .vecls = .{ .width = .@"16", .umop = .unit, .vm = true, .mop = .unit, .mew = false, .nf = 0b000 } } }, + .vle32v => .{ .opcode = .LOAD_FP, .format = .R, .data = .{ .vecls = .{ .width = .@"32", .umop = .unit, .vm = true, .mop = .unit, .mew = false, .nf = 0b000 } } }, + .vle64v => .{ .opcode = .LOAD_FP, .format = .R, .data = .{ .vecls = .{ .width = .@"64", .umop = .unit, .vm = true, .mop = .unit, .mew = false, .nf = 0b000 } } }, + + + // STORE_FP + + .fsw => .{ .opcode = .STORE_FP, .format = .S, .data = .{ .f = .{ .funct3 = 0b010 } } }, + .fsd => .{ .opcode = .STORE_FP, .format = .S, .data = .{ .f = .{ .funct3 = 0b011 } } }, + + .vse8v => .{ .opcode = .STORE_FP, .format = .R, .data = .{ .vecls = .{ .width = .@"8", .umop = .unit, .vm = true, .mop = .unit, .mew = false, .nf = 0b000 } } }, + .vse16v => .{ .opcode = .STORE_FP, .format = .R, .data = .{ .vecls = .{ .width = .@"16", .umop = .unit, .vm = true, .mop = .unit, .mew = false, .nf = 0b000 } } }, + .vse32v => .{ .opcode = .STORE_FP, .format = .R, .data = .{ .vecls = .{ .width = .@"32", .umop = .unit, .vm = true, .mop = .unit, .mew = false, .nf = 0b000 } } }, + .vse64v => .{ .opcode = .STORE_FP, .format = .R, .data = .{ .vecls = .{ .width = .@"64", .umop = .unit, .vm = true, .mop = .unit, .mew = false, .nf = 0b000 } } }, + + // JALR + + .jalr => .{ .opcode = .JALR, .format = .I, .data = .{ .f = .{ .funct3 = 0b000 } } }, + + + // LUI + + .lui => .{ .opcode = .LUI, .format = .U, .data = .{ .none = {} } }, + + + // AUIPC + + .auipc => .{ .opcode = .AUIPC, .format = .U, .data = .{ .none = {} } }, + + + // JAL + + .jal => .{ .opcode = .JAL, .format = .J, .data = .{ .none = {} } }, + + + // BRANCH + + .beq => .{ .opcode = .BRANCH, .format = .B, .data = .{ .f = .{ .funct3 = 0b000 } } }, + + + // SYSTEM + + .ecall => .{ .opcode = .SYSTEM, .format = .extra, .data = .{ .extra = .ecall } }, + .ebreak => .{ .opcode = .SYSTEM, .format = .extra, .data = .{ .extra = .ebreak } }, + + .csrrs => .{ .opcode = .SYSTEM, .format = .I, .data = .{ .f = .{ .funct3 = 0b010 } } }, + + + // NONE + + .unimp => .{ .opcode = .NONE, .format = .extra, .data = .{ .extra = .unimp } }, + + + // MISC_MEM + + .fence => .{ .opcode = .MISC_MEM, .format = .I, .data = .{ .fence = .{ .funct3 = 0b000, .fm = .none } } }, + .fencetso => .{ .opcode = .MISC_MEM, .format = .I, .data = .{ .fence = .{ .funct3 = 0b000, .fm = .tso } } }, + + + // AMO + + .amoaddw => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .W, .funct5 = 0b00000 } } }, + .amoswapw => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .W, .funct5 = 0b00001 } } }, + // LR.W + // SC.W + .amoxorw => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .W, .funct5 = 0b00100 } } }, + .amoandw => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .W, .funct5 = 0b01100 } } }, + .amoorw => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .W, .funct5 = 0b01000 } } }, + .amominw => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .W, .funct5 = 0b10000 } } }, + .amomaxw => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .W, .funct5 = 0b10100 } } }, + .amominuw => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .W, .funct5 = 0b11000 } } }, + .amomaxuw => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .W, .funct5 = 0b11100 } } }, + + .amoaddd => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .D, .funct5 = 0b00000 } } }, + .amoswapd => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .D, .funct5 = 0b00001 } } }, + // LR.D + // SC.D + .amoxord => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .D, .funct5 = 0b00100 } } }, + .amoandd => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .D, .funct5 = 0b01100 } } }, + .amoord => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .D, .funct5 = 0b01000 } } }, + .amomind => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .D, .funct5 = 0b10000 } } }, + .amomaxd => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .D, .funct5 = 0b10100 } } }, + .amominud => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .D, .funct5 = 0b11000 } } }, + .amomaxud => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .D, .funct5 = 0b11100 } } }, + + // OP_V + .vsetivli => .{ .opcode = .OP_V, .format = .I, .data = .{ .f = .{ .funct3 = 0b111 } } }, + .vsetvli => .{ .opcode = .OP_V, .format = .I, .data = .{ .f = .{ .funct3 = 0b111 } } }, + .vaddvv => .{ .opcode = .OP_V, .format = .R, .data = .{ .vecmath = .{ .vm = true, .funct6 = 0b000000, .funct3 = .OPIVV } } }, + .vsubvv => .{ .opcode = .OP_V, .format = .R, .data = .{ .vecmath = .{ .vm = true, .funct6 = 0b000010, .funct3 = .OPIVV } } }, + .vmulvv => .{ .opcode = .OP_V, .format = .R, .data = .{ .vecmath = .{ .vm = true, .funct6 = 0b100101, .funct3 = .OPIVV } } }, + + .vfaddvv => .{ .opcode = .OP_V, .format = .R, .data = .{ .vecmath = .{ .vm = true, .funct6 = 0b000000, .funct3 = .OPFVV } } }, + .vfsubvv => .{ .opcode = .OP_V, .format = .R, .data = .{ .vecmath = .{ .vm = true, .funct6 = 0b000010, .funct3 = .OPFVV } } }, + .vfmulvv => .{ .opcode = .OP_V, .format = .R, .data = .{ .vecmath = .{ .vm = true, .funct6 = 0b100100, .funct3 = .OPFVV } } }, + + .vadcvv => .{ .opcode = .OP_V, .format = .R, .data = .{ .vecmath = .{ .vm = true, .funct6 = 0b010000, .funct3 = .OPMVV } } }, + .vmvvx => .{ .opcode = .OP_V, .format = .R, .data = .{ .vecmath = .{ .vm = true, .funct6 = 0b010111, .funct3 = .OPIVX } } }, + + .vslidedownvx => .{ .opcode = .OP_V, .format = .R, .data = .{ .vecmath = .{ .vm = true, .funct6 = 0b001111, .funct3 = .OPIVX } } }, + + + .pseudo_prologue, + .pseudo_epilogue, + .pseudo_dbg_prologue_end, + .pseudo_dbg_epilogue_begin, + .pseudo_dbg_line_column, + .pseudo_load_rm, + .pseudo_store_rm, + .pseudo_lea_rm, + .pseudo_j, + .pseudo_dead, + .pseudo_load_symbol, + .pseudo_mv, + .pseudo_restore_regs, + .pseudo_spill_regs, + .pseudo_compare, + .pseudo_not, + .pseudo_extern_fn_reloc, + .pseudo_fence, + .pseudo_amo, + .nop, + => std.debug.panic("lir: didn't catch pseudo {s}", .{@tagName(mnem)}), + // zig fmt: on + }; + } +}; + +/// This is the final form of the instruction. Lir is transformed into +/// this, which is then bitcast into a u32. +pub const Instruction = union(Lir.Format) { + R: packed struct(u32) { + opcode: u7, + rd: u5, + funct3: u3, + rs1: u5, + rs2: u5, + funct7: u7, + }, + I: packed struct(u32) { + opcode: u7, + rd: u5, + funct3: u3, + rs1: u5, + imm0_11: u12, + }, + S: packed struct(u32) { + opcode: u7, + imm0_4: u5, + funct3: u3, + rs1: u5, + rs2: u5, + imm5_11: u7, + }, + B: packed struct(u32) { + opcode: u7, + imm11: u1, + imm1_4: u4, + funct3: u3, + rs1: u5, + rs2: u5, + imm5_10: u6, + imm12: u1, + }, + U: packed struct(u32) { + opcode: u7, + rd: u5, + imm12_31: u20, + }, + J: packed struct(u32) { + opcode: u7, + rd: u5, + imm12_19: u8, + imm11: u1, + imm1_10: u10, + imm20: u1, + }, + extra: u32, + + comptime { + for (std.meta.fields(Instruction)) |field| { + assert(@bitSizeOf(field.type) == 32); + } + } + + pub const Operand = union(enum) { + none, + reg: Register, + csr: CSR, + mem: Memory, + imm: Immediate, + barrier: Mir.Barrier, + }; + + pub fn toU32(inst: Instruction) u32 { + return switch (inst) { + inline else => |v| @bitCast(v), + }; + } + + pub fn encode(inst: Instruction, writer: anytype) !void { + try writer.writeInt(u32, inst.toU32(), .little); + } + + pub fn fromLir(lir: Lir, ops: []const Operand) Instruction { + const opcode: u7 = @intFromEnum(lir.opcode); + + switch (lir.format) { + .R => { + return .{ + .R = switch (lir.data) { + .ff => |ff| .{ + .rd = ops[0].reg.encodeId(), + .rs1 = ops[1].reg.encodeId(), + .rs2 = ops[2].reg.encodeId(), + + .opcode = opcode, + .funct3 = ff.funct3, + .funct7 = ff.funct7, + }, + .fmt => |fmt| .{ + .rd = ops[0].reg.encodeId(), + .rs1 = ops[1].reg.encodeId(), + .rs2 = ops[2].reg.encodeId(), + + .opcode = opcode, + .funct3 = fmt.rm, + .funct7 = (@as(u7, fmt.funct5) << 2) | @intFromEnum(fmt.fmt), + }, + .fcvt => |fcvt| .{ + .rd = ops[0].reg.encodeId(), + .rs1 = ops[1].reg.encodeId(), + .rs2 = @intFromEnum(fcvt.width), + + .opcode = opcode, + .funct3 = fcvt.rm, + .funct7 = (@as(u7, fcvt.funct5) << 2) | @intFromEnum(fcvt.fmt), + }, + .vecls => |vec| .{ + .rd = ops[0].reg.encodeId(), + .rs1 = ops[1].reg.encodeId(), + + .rs2 = @intFromEnum(vec.umop), + + .opcode = 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 = opcode, + .funct3 = @intFromEnum(vec.funct3), + .funct7 = (@as(u7, vec.funct6) << 1) | @intFromBool(vec.vm), + }, + .amo => |amo| .{ + .rd = ops[0].reg.encodeId(), + .rs1 = ops[1].reg.encodeId(), + .rs2 = ops[2].reg.encodeId(), + + .opcode = opcode, + .funct3 = @intFromEnum(amo.width), + .funct7 = @as(u7, amo.funct5) << 2 | + @as(u7, @intFromBool(ops[3].barrier == .rl)) << 1 | + @as(u7, @intFromBool(ops[4].barrier == .aq)), + }, + else => unreachable, + }, + }; + }, + .S => { + assert(ops.len == 3); + const umm = ops[2].imm.asBits(u12); + return .{ + .S = .{ + .imm0_4 = @truncate(umm), + .rs1 = ops[0].reg.encodeId(), + .rs2 = ops[1].reg.encodeId(), + .imm5_11 = @truncate(umm >> 5), + + .opcode = opcode, + .funct3 = lir.data.f.funct3, + }, + }; + }, + .I => { + return .{ + .I = switch (lir.data) { + .f => |f| .{ + .rd = ops[0].reg.encodeId(), + .rs1 = ops[1].reg.encodeId(), + .imm0_11 = ops[2].imm.asBits(u12), + + .opcode = opcode, + .funct3 = f.funct3, + }, + .sh => |sh| .{ + .rd = ops[0].reg.encodeId(), + .rs1 = ops[1].reg.encodeId(), + .imm0_11 = (@as(u12, sh.typ) << 6) | + if (sh.has_5) ops[2].imm.asBits(u6) else (@as(u6, 0) | ops[2].imm.asBits(u5)), + + .opcode = opcode, + .funct3 = sh.funct3, + }, + .fence => |fence| .{ + .rd = 0, + .rs1 = 0, + .funct3 = 0, + .imm0_11 = (@as(u12, @intFromEnum(fence.fm)) << 8) | + (@as(u12, @intFromEnum(ops[1].barrier)) << 4) | + @as(u12, @intFromEnum(ops[0].barrier)), + .opcode = opcode, + }, + else => unreachable, + }, + }; + }, + .U => { + assert(ops.len == 2); + return .{ + .U = .{ + .rd = ops[0].reg.encodeId(), + .imm12_31 = ops[1].imm.asBits(u20), + + .opcode = opcode, + }, + }; + }, + .J => { + assert(ops.len == 2); + + const umm = ops[1].imm.asBits(u21); + // the RISC-V spec says the target index of a jump + // must be a multiple of 2 + assert(umm % 2 == 0); + + return .{ + .J = .{ + .rd = ops[0].reg.encodeId(), + .imm1_10 = @truncate(umm >> 1), + .imm11 = @truncate(umm >> 11), + .imm12_19 = @truncate(umm >> 12), + .imm20 = @truncate(umm >> 20), + + .opcode = opcode, + }, + }; + }, + .B => { + assert(ops.len == 3); + + const umm = ops[2].imm.asBits(u13); + // the RISC-V spec says the target index of a branch + // must be a multiple of 2 + assert(umm % 2 == 0); + + return .{ + .B = .{ + .rs1 = ops[0].reg.encodeId(), + .rs2 = ops[1].reg.encodeId(), + .imm1_4 = @truncate(umm >> 1), + .imm5_10 = @truncate(umm >> 5), + .imm11 = @truncate(umm >> 11), + .imm12 = @truncate(umm >> 12), + + .opcode = opcode, + .funct3 = lir.data.f.funct3, + }, + }; + }, + .extra => { + assert(ops.len == 0); + + return .{ + .I = .{ + .rd = Register.zero.encodeId(), + .rs1 = Register.zero.encodeId(), + .imm0_11 = switch (lir.data.extra) { + .ecall => 0x000, + .ebreak => 0x001, + .unimp => 0x000, + else => unreachable, + }, + + .opcode = opcode, + .funct3 = 0b000, + }, + }; + }, + } + } +}; + +const std = @import("std"); +const assert = std.debug.assert; +const log = std.log.scoped(.format); + +const bits = @import("bits.zig"); +const Mir = @import("Mir.zig"); +const Mnemonic = @import("mnem.zig").Mnemonic; +const Lower = @import("Lower.zig"); + +const Register = bits.Register; +const CSR = bits.CSR; +const Memory = bits.Memory; +const Immediate = bits.Immediate; diff --git a/src/arch/riscv64/mnem.zig b/src/arch/riscv64/mnem.zig new file mode 100644 index 0000000000..afd2f5c78c --- /dev/null +++ b/src/arch/riscv64/mnem.zig @@ -0,0 +1,232 @@ +pub const Mnemonic = enum(u16) { + // Arithmetics + addi, + add, + addw, + + sub, + subw, + + // Bits + xori, + xor, + @"or", + + @"and", + andi, + + slt, + sltu, + sltiu, + + slli, + srli, + srai, + + slliw, + srliw, + sraiw, + + sll, + srl, + sra, + + sllw, + srlw, + sraw, + + // Control Flow + jalr, + jal, + + beq, + + // Memory + lui, + auipc, + + ld, + lw, + lh, + lb, + lbu, + lhu, + lwu, + + sd, + sw, + sh, + sb, + + // System + ebreak, + ecall, + unimp, + nop, + + // M extension + mul, + mulh, + mulhu, + mulhsu, + mulw, + + div, + divu, + divw, + divuw, + + rem, + remu, + remw, + remuw, + + // F extension (32-bit float) + fadds, + fsubs, + fmuls, + fdivs, + + fmins, + fmaxs, + + fsqrts, + + flw, + fsw, + + feqs, + flts, + fles, + + // D extension (64-bit float) + faddd, + fsubd, + fmuld, + fdivd, + + fmind, + fmaxd, + + fsqrtd, + + fld, + fsd, + + feqd, + fltd, + fled, + + fcvtws, + fcvtwus, + fcvtls, + fcvtlus, + + fcvtwd, + fcvtwud, + fcvtld, + fcvtlud, + + fsgnjns, + fsgnjnd, + + fsgnjxs, + fsgnjxd, + + // Zicsr Extension Instructions + csrrs, + + // V Extension Instructions + vsetvli, + vsetivli, + vaddvv, + vfaddvv, + vsubvv, + vfsubvv, + vmulvv, + vfmulvv, + vslidedownvx, + + vle8v, + vle16v, + vle32v, + vle64v, + + vse8v, + vse16v, + vse32v, + vse64v, + + vadcvv, + vmvvx, + + // Zbb Extension Instructions + clz, + clzw, + + // A Extension Instructions + fence, + fencetso, + + amoswapw, + amoaddw, + amoandw, + amoorw, + amoxorw, + amomaxw, + amominw, + amomaxuw, + amominuw, + + amoswapd, + amoaddd, + amoandd, + amoord, + amoxord, + amomaxd, + amomind, + amomaxud, + amominud, + + // Pseudo-instructions. Used for anything that isn't 1:1 with an + // assembly instruction. + + /// Pseudo-instruction that will generate a backpatched + /// function prologue. + pseudo_prologue, + /// Pseudo-instruction that will generate a backpatched + /// function epilogue + pseudo_epilogue, + + /// Pseudo-instruction: End of prologue + pseudo_dbg_prologue_end, + /// Pseudo-instruction: Beginning of epilogue + pseudo_dbg_epilogue_begin, + /// Pseudo-instruction: Update debug line + pseudo_dbg_line_column, + + /// Pseudo-instruction that loads from memory into a register. + pseudo_load_rm, + /// Pseudo-instruction that stores from a register into memory + pseudo_store_rm, + /// Pseudo-instruction that loads the address of memory into a register. + pseudo_lea_rm, + /// Jumps. Uses `inst` payload. + pseudo_j, + /// Dead inst, ignored by the emitter. + pseudo_dead, + /// Loads the address of a value that hasn't yet been allocated in memory. + pseudo_load_symbol, + + /// Moves the value of rs1 to rd. + pseudo_mv, + + pseudo_restore_regs, + pseudo_spill_regs, + + pseudo_compare, + pseudo_not, + pseudo_extern_fn_reloc, + pseudo_fence, + pseudo_amo, +}; diff --git a/src/link/riscv.zig b/src/link/riscv.zig index 5107992b48..bf23010c80 100644 --- a/src/link/riscv.zig +++ b/src/link/riscv.zig @@ -25,47 +25,27 @@ pub fn writeAddend( } pub fn writeInstU(code: *[4]u8, value: u32) void { - var data = Encoding.Data{ - .U = mem.bytesToValue(std.meta.TagPayload( - Encoding.Data, - Encoding.Data.U, - ), code), - }; + var data: Instruction = .{ .U = mem.bytesToValue(std.meta.TagPayload(Instruction, .U), code) }; const compensated: u32 = @bitCast(@as(i32, @bitCast(value)) + 0x800); data.U.imm12_31 = bitSlice(compensated, 31, 12); mem.writeInt(u32, code, data.toU32(), .little); } pub fn writeInstI(code: *[4]u8, value: u32) void { - var data = Encoding.Data{ - .I = mem.bytesToValue(std.meta.TagPayload( - Encoding.Data, - Encoding.Data.I, - ), code), - }; + var data: Instruction = .{ .I = mem.bytesToValue(std.meta.TagPayload(Instruction, .I), code) }; data.I.imm0_11 = bitSlice(value, 11, 0); mem.writeInt(u32, code, data.toU32(), .little); } pub fn writeInstS(code: *[4]u8, value: u32) void { - var data = Encoding.Data{ - .S = mem.bytesToValue(std.meta.TagPayload( - Encoding.Data, - Encoding.Data.S, - ), code), - }; + var data: Instruction = .{ .S = mem.bytesToValue(std.meta.TagPayload(Instruction, .S), code) }; data.S.imm0_4 = bitSlice(value, 4, 0); data.S.imm5_11 = bitSlice(value, 11, 5); mem.writeInt(u32, code, data.toU32(), .little); } pub fn writeInstJ(code: *[4]u8, value: u32) void { - var data = Encoding.Data{ - .J = mem.bytesToValue(std.meta.TagPayload( - Encoding.Data, - Encoding.Data.J, - ), code), - }; + var data: Instruction = .{ .J = mem.bytesToValue(std.meta.TagPayload(Instruction, .J), code) }; data.J.imm1_10 = bitSlice(value, 10, 1); data.J.imm11 = bitSlice(value, 11, 11); data.J.imm12_19 = bitSlice(value, 19, 12); @@ -74,12 +54,7 @@ pub fn writeInstJ(code: *[4]u8, value: u32) void { } pub fn writeInstB(code: *[4]u8, value: u32) void { - var data = Encoding.Data{ - .B = mem.bytesToValue(std.meta.TagPayload( - Encoding.Data, - Encoding.Data.B, - ), code), - }; + var data: Instruction = .{ .B = mem.bytesToValue(std.meta.TagPayload(Instruction, .B), code) }; data.B.imm1_4 = bitSlice(value, 4, 1); data.B.imm5_10 = bitSlice(value, 10, 5); data.B.imm11 = bitSlice(value, 11, 11); @@ -109,9 +84,8 @@ pub const RiscvEflags = packed struct(u32) { _unused: u8, }; -const encoder = @import("../arch/riscv64/encoder.zig"); -const Encoding = @import("../arch/riscv64/Encoding.zig"); const mem = std.mem; const std = @import("std"); -pub const Instruction = encoder.Instruction; +const encoding = @import("../arch/riscv64/encoding.zig"); +const Instruction = encoding.Instruction; diff --git a/test/behavior/align.zig b/test/behavior/align.zig index 33e43740f6..8c51be2685 100644 --- a/test/behavior/align.zig +++ b/test/behavior/align.zig @@ -510,7 +510,6 @@ test "read 128-bit field from default aligned struct in global memory" { } test "struct field explicit alignment" { - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO diff --git a/test/behavior/byteswap.zig b/test/behavior/byteswap.zig index fd7e2af850..0c6e655b25 100644 --- a/test/behavior/byteswap.zig +++ b/test/behavior/byteswap.zig @@ -100,6 +100,7 @@ test "@byteSwap vectors u8" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; try comptime vector8(); try vector8(); diff --git a/test/behavior/defer.zig b/test/behavior/defer.zig index fc764f55e3..4ea6f54787 100644 --- a/test/behavior/defer.zig +++ b/test/behavior/defer.zig @@ -116,6 +116,7 @@ test "errdefer with payload" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { fn foo() !i32 { @@ -138,6 +139,7 @@ test "reference to errdefer payload" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { fn foo() !i32 { diff --git a/test/behavior/optional.zig b/test/behavior/optional.zig index ae9cc5ee4d..a11a20f012 100644 --- a/test/behavior/optional.zig +++ b/test/behavior/optional.zig @@ -591,6 +591,7 @@ test "cast slice to const slice nested in error union and optional" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { fn inner() !?[]u8 { diff --git a/test/behavior/pointers.zig b/test/behavior/pointers.zig index 1cc9092fd7..5308ebf358 100644 --- a/test/behavior/pointers.zig +++ b/test/behavior/pointers.zig @@ -228,6 +228,7 @@ test "implicit cast error unions with non-optional to optional pointer" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { fn doTheTest() !void { diff --git a/test/behavior/switch.zig b/test/behavior/switch.zig index 4c0b4a88f4..ac62461b22 100644 --- a/test/behavior/switch.zig +++ b/test/behavior/switch.zig @@ -427,6 +427,7 @@ test "else prong of switch on error set excludes other cases" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { fn doTheTest() !void { @@ -462,6 +463,7 @@ test "switch prongs with error set cases make a new error set type for capture v if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { fn doTheTest() !void { diff --git a/test/behavior/try.zig b/test/behavior/try.zig index cc76658e93..53fdc48934 100644 --- a/test/behavior/try.zig +++ b/test/behavior/try.zig @@ -51,6 +51,7 @@ test "`try`ing an if/else expression" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { fn getError() !void { From c00a5ff792e78564673d0dc76b901c98b938bb9b Mon Sep 17 00:00:00 2001 From: David Rubin Date: Wed, 24 Jul 2024 19:31:51 -0700 Subject: [PATCH 16/27] riscv: implement `@floatFromInt` --- src/arch/riscv64/CodeGen.zig | 66 ++++++++++++++++++++++++++++++++--- src/arch/riscv64/encoding.zig | 10 ++++++ src/arch/riscv64/mnem.zig | 10 ++++++ test/behavior/align.zig | 1 + test/behavior/cast.zig | 2 -- 5 files changed, 82 insertions(+), 7 deletions(-) diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index 6e807f1f3b..b1ef2bbb10 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -6712,9 +6712,65 @@ fn airArrayToSlice(func: *Func, inst: Air.Inst.Index) !void { fn airFloatFromInt(func: *Func, inst: Air.Inst.Index) !void { const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else return func.fail("TODO implement airFloatFromInt for {}", .{ - func.target.cpu.arch, - }); + const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else result: { + const pt = func.pt; + const zcu = pt.zcu; + + const operand = try func.resolveInst(ty_op.operand); + + const src_ty = func.typeOf(ty_op.operand); + const dst_ty = ty_op.ty.toType(); + + const src_reg, const src_lock = try func.promoteReg(src_ty, operand); + defer if (src_lock) |lock| func.register_manager.unlockReg(lock); + + const is_unsigned = dst_ty.isUnsignedInt(zcu); + const src_bits = src_ty.bitSize(pt); + const dst_bits = dst_ty.bitSize(pt); + + switch (src_bits) { + 32, 64 => {}, + else => try func.truncateRegister(src_ty, src_reg), + } + + const int_mod: Mir.FcvtOp = switch (src_bits) { + 8, 16, 32 => if (is_unsigned) .wu else .w, + 64 => if (is_unsigned) .lu else .l, + else => return func.fail("TODO: airFloatFromInt src size: {d}", .{src_bits}), + }; + + const float_mod: enum { s, d } = switch (dst_bits) { + 32 => .s, + 64 => .d, + else => return func.fail("TODO: airFloatFromInt dst size {d}", .{dst_bits}), + }; + + const dst_reg, const dst_lock = try func.allocReg(.int); + defer func.register_manager.unlockReg(dst_lock); + + _ = try func.addInst(.{ + .tag = switch (float_mod) { + .s => switch (int_mod) { + .l => .fcvtsl, + .lu => .fcvtslu, + .w => .fcvtsw, + .wu => .fcvtswu, + }, + .d => switch (int_mod) { + .l => .fcvtdl, + .lu => .fcvtdlu, + .w => .fcvtdw, + .wu => .fcvtdwu, + }, + }, + .data = .{ .rr = .{ + .rd = dst_reg, + .rs = src_reg, + } }, + }); + + break :result .{ .register = dst_reg }; + }; return func.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } @@ -6726,7 +6782,7 @@ fn airIntFromFloat(func: *Func, inst: Air.Inst.Index) !void { const operand = try func.resolveInst(ty_op.operand); const src_ty = func.typeOf(ty_op.operand); - const dst_ty = func.typeOfIndex(inst); + const dst_ty = ty_op.ty.toType(); const is_unsigned = dst_ty.isUnsignedInt(zcu); const src_bits = src_ty.bitSize(pt); @@ -6740,7 +6796,7 @@ fn airIntFromFloat(func: *Func, inst: Air.Inst.Index) !void { const int_mod: Mir.FcvtOp = switch (dst_bits) { 32 => if (is_unsigned) .wu else .w, - 64 => if (is_unsigned) .lu else .l, + 8, 16, 64 => if (is_unsigned) .lu else .l, else => return func.fail("TODO: airIntFromFloat dst size: {d}", .{dst_bits}), }; diff --git a/src/arch/riscv64/encoding.zig b/src/arch/riscv64/encoding.zig index a3e20698b6..7c1bd274e0 100644 --- a/src/arch/riscv64/encoding.zig +++ b/src/arch/riscv64/encoding.zig @@ -280,6 +280,16 @@ pub const Lir = struct { .fcvtld => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fcvt = .{ .funct5 = 0b11000, .fmt = .D, .rm = 0b111, .width = .l } } }, .fcvtlud => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fcvt = .{ .funct5 = 0b11000, .fmt = .D, .rm = 0b111, .width = .lu } } }, + .fcvtsw => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fcvt = .{ .funct5 = 0b11010, .fmt = .S, .rm = 0b111, .width = .w } } }, + .fcvtswu => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fcvt = .{ .funct5 = 0b11010, .fmt = .S, .rm = 0b111, .width = .wu } } }, + .fcvtsl => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fcvt = .{ .funct5 = 0b11010, .fmt = .S, .rm = 0b111, .width = .l } } }, + .fcvtslu => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fcvt = .{ .funct5 = 0b11010, .fmt = .S, .rm = 0b111, .width = .lu } } }, + + .fcvtdw => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fcvt = .{ .funct5 = 0b11010, .fmt = .D, .rm = 0b111, .width = .w } } }, + .fcvtdwu => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fcvt = .{ .funct5 = 0b11010, .fmt = .D, .rm = 0b111, .width = .wu } } }, + .fcvtdl => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fcvt = .{ .funct5 = 0b11010, .fmt = .D, .rm = 0b111, .width = .l } } }, + .fcvtdlu => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fcvt = .{ .funct5 = 0b11010, .fmt = .D, .rm = 0b111, .width = .lu } } }, + // LOAD .lb => .{ .opcode = .LOAD, .format = .I, .data = .{ .f = .{ .funct3 = 0b000 } } }, diff --git a/src/arch/riscv64/mnem.zig b/src/arch/riscv64/mnem.zig index afd2f5c78c..d75bf5ccd0 100644 --- a/src/arch/riscv64/mnem.zig +++ b/src/arch/riscv64/mnem.zig @@ -127,6 +127,16 @@ pub const Mnemonic = enum(u16) { fcvtld, fcvtlud, + fcvtsw, + fcvtswu, + fcvtsl, + fcvtslu, + + fcvtdw, + fcvtdwu, + fcvtdl, + fcvtdlu, + fsgnjns, fsgnjnd, diff --git a/test/behavior/align.zig b/test/behavior/align.zig index 8c51be2685..659fd764aa 100644 --- a/test/behavior/align.zig +++ b/test/behavior/align.zig @@ -514,6 +514,7 @@ test "struct field explicit alignment" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // flaky + if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { const Node = struct { diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index 44fcb08cf3..db0e96128b 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -104,7 +104,6 @@ test "@floatFromInt" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { fn doTheTest() !void { @@ -163,7 +162,6 @@ test "@intFromFloat" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; try testIntFromFloats(); try comptime testIntFromFloats(); From b533e848a288bcf91da2720dc2646b88225642e9 Mon Sep 17 00:00:00 2001 From: David Rubin Date: Wed, 24 Jul 2024 21:57:16 -0700 Subject: [PATCH 17/27] riscv: enable passing tests --- test/behavior/align.zig | 2 -- test/behavior/array.zig | 4 ---- test/behavior/basic.zig | 7 ------- test/behavior/bitcast.zig | 3 --- test/behavior/cast.zig | 3 --- test/behavior/cast_int.zig | 2 -- test/behavior/enum.zig | 3 --- test/behavior/error.zig | 3 --- test/behavior/eval.zig | 1 - test/behavior/generics.zig | 2 -- test/behavior/math.zig | 11 ----------- test/behavior/optional.zig | 1 - test/behavior/packed-struct.zig | 9 --------- test/behavior/packed-union.zig | 2 -- test/behavior/src.zig | 2 -- test/behavior/string_literals.zig | 1 - test/behavior/struct.zig | 9 --------- test/behavior/union.zig | 1 - 18 files changed, 66 deletions(-) diff --git a/test/behavior/align.zig b/test/behavior/align.zig index 659fd764aa..83ebf7ec86 100644 --- a/test/behavior/align.zig +++ b/test/behavior/align.zig @@ -25,7 +25,6 @@ test "large alignment of local constant" { } test "slicing array of length 1 can not assume runtime index is always zero" { - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // flaky @@ -514,7 +513,6 @@ test "struct field explicit alignment" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // flaky - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { const Node = struct { diff --git a/test/behavior/array.zig b/test/behavior/array.zig index 05da574edf..fa20342491 100644 --- a/test/behavior/array.zig +++ b/test/behavior/array.zig @@ -159,7 +159,6 @@ test "array len field" { test "array with sentinels" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { fn doTheTest(is_ct: bool) !void { @@ -531,7 +530,6 @@ test "sentinel element count towards the ABI size calculation" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { fn doTheTest() !void { @@ -685,8 +683,6 @@ test "array init of container level array variable" { } test "runtime initialized sentinel-terminated array literal" { - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - var c: u16 = 300; _ = &c; const f = &[_:0x9999]u16{c}; diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig index bd942bab49..fbddcd15fc 100644 --- a/test/behavior/basic.zig +++ b/test/behavior/basic.zig @@ -25,7 +25,6 @@ fn testTruncate(x: u32) u8 { test "truncate to non-power-of-two integers" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; try testTrunc(u32, u1, 0b10101, 0b1); try testTrunc(u32, u1, 0b10110, 0b0); @@ -43,7 +42,6 @@ test "truncate to non-power-of-two integers from 128-bit" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; try testTrunc(u128, u1, 0xffffffff_ffffffff_ffffffff_01010101, 0x01); try testTrunc(u128, u1, 0xffffffff_ffffffff_ffffffff_01010110, 0x00); @@ -222,7 +220,6 @@ const OpaqueB = opaque {}; test "opaque types" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; try expect(*OpaqueA != *OpaqueB); @@ -374,7 +371,6 @@ test "take address of parameter" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; try testTakeAddressOfParameter(12.34); } @@ -1069,7 +1065,6 @@ test "returning an opaque type from a function" { test "orelse coercion as function argument" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const Loc = struct { start: i32 = -1 }; const Container = struct { @@ -1183,8 +1178,6 @@ fn testUnsignedCmp(comptime T: type) !void { } test "integer compare <= 64 bits" { - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - inline for (.{ u8, u16, u32, u64, usize, u10, u20, u30, u60 }) |T| { try testUnsignedCmp(T); try comptime testUnsignedCmp(T); diff --git a/test/behavior/bitcast.zig b/test/behavior/bitcast.zig index cc84a948d1..1cb72ac42b 100644 --- a/test/behavior/bitcast.zig +++ b/test/behavior/bitcast.zig @@ -10,7 +10,6 @@ const native_endian = builtin.target.cpu.arch.endian(); test "@bitCast iX -> uX (32, 64)" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const bit_values = [_]usize{ 32, 64 }; @@ -165,7 +164,6 @@ test "@bitCast packed structs at runtime and comptime" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const Full = packed struct { number: u16, @@ -491,7 +489,6 @@ test "@bitCast of packed struct of bools all true" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const P = packed struct { b0: bool, diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index db0e96128b..cd25259064 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -448,7 +448,6 @@ test "implicitly cast from T to anyerror!?T" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; try castToOptionalTypeError(1); try comptime castToOptionalTypeError(1); @@ -761,7 +760,6 @@ test "peer type resolution: error union and error set" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const a: error{Three} = undefined; const b: error{ One, Two }!u32 = undefined; @@ -1751,7 +1749,6 @@ test "peer type resolution: array and vector with same child type" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; var arr: [2]u32 = .{ 0, 1 }; var vec: @Vector(2, u32) = .{ 2, 3 }; diff --git a/test/behavior/cast_int.zig b/test/behavior/cast_int.zig index 67834385d1..9faa123a62 100644 --- a/test/behavior/cast_int.zig +++ b/test/behavior/cast_int.zig @@ -35,8 +35,6 @@ test "coerce i8 to i32 and @intCast back" { } test "coerce non byte-sized integers accross 32bits boundary" { - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - { var v: u21 = 6417; _ = &v; diff --git a/test/behavior/enum.zig b/test/behavior/enum.zig index 982adc234d..32d291e4fd 100644 --- a/test/behavior/enum.zig +++ b/test/behavior/enum.zig @@ -618,7 +618,6 @@ test "enum with specified tag values" { test "non-exhaustive enum" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { const E = enum(u8) { a, b, _ }; @@ -934,7 +933,6 @@ const Bar = enum { A, B, C, D }; test "enum literal casting to error union with payload enum" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; var bar: error{B}!Bar = undefined; bar = .B; // should never cast to the error set @@ -1104,7 +1102,6 @@ test "bit field access with enum fields" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; var data = bit_field_1; try expect(getA(&data) == A.Two); diff --git a/test/behavior/error.zig b/test/behavior/error.zig index 9b6300e743..cc37e36207 100644 --- a/test/behavior/error.zig +++ b/test/behavior/error.zig @@ -403,7 +403,6 @@ test "nested error union function call in optional unwrap" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { const Foo = struct { @@ -450,7 +449,6 @@ test "nested error union function call in optional unwrap" { test "return function call to error set from error union function" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { fn errorable() anyerror!i32 { @@ -1079,7 +1077,6 @@ test "result location initialization of error union with OPV payload" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { x: u0, diff --git a/test/behavior/eval.zig b/test/behavior/eval.zig index fb916df3cf..b32191cbb2 100644 --- a/test/behavior/eval.zig +++ b/test/behavior/eval.zig @@ -782,7 +782,6 @@ test "array concatenation peer resolves element types - pointer" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; var a = [2]u3{ 1, 7 }; var b = [3]u8{ 200, 225, 255 }; diff --git a/test/behavior/generics.zig b/test/behavior/generics.zig index 712df05011..67ec438d9e 100644 --- a/test/behavior/generics.zig +++ b/test/behavior/generics.zig @@ -401,8 +401,6 @@ test "generic struct as parameter type" { } test "slice as parameter type" { - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - const S = struct { fn internComptimeString(comptime str: []const u8) *const []const u8 { return &struct { diff --git a/test/behavior/math.zig b/test/behavior/math.zig index 17ca44c6f1..9d911c556b 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -662,8 +662,6 @@ fn rem(comptime T: type, a: T, b: T) T { } test "unsigned wrapping" { - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - try testUnsignedWrappingEval(maxInt(u32)); try comptime testUnsignedWrappingEval(maxInt(u32)); } @@ -675,8 +673,6 @@ fn testUnsignedWrappingEval(x: u32) !void { } test "signed wrapping" { - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - try testSignedWrappingEval(maxInt(i32)); try comptime testSignedWrappingEval(maxInt(i32)); } @@ -688,8 +684,6 @@ fn testSignedWrappingEval(x: i32) !void { } test "signed negation wrapping" { - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - try testSignedNegationWrappingEval(minInt(i16)); try comptime testSignedNegationWrappingEval(minInt(i16)); } @@ -700,8 +694,6 @@ fn testSignedNegationWrappingEval(x: i16) !void { } test "unsigned negation wrapping" { - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - try testUnsignedNegationWrappingEval(1); try comptime testUnsignedNegationWrappingEval(1); } @@ -900,7 +892,6 @@ test "@addWithOverflow > 64 bits" { test "small int addition" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; var x: u2 = 0; try expect(x == 0); @@ -1330,8 +1321,6 @@ test "quad hex float literal parsing accurate" { } test "truncating shift left" { - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - try testShlTrunc(maxInt(u16)); try comptime testShlTrunc(maxInt(u16)); } diff --git a/test/behavior/optional.zig b/test/behavior/optional.zig index a11a20f012..0abd547596 100644 --- a/test/behavior/optional.zig +++ b/test/behavior/optional.zig @@ -630,7 +630,6 @@ test "result location initialization of optional with OPV payload" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { x: u0, diff --git a/test/behavior/packed-struct.zig b/test/behavior/packed-struct.zig index 5b449ab8fc..9880aff14e 100644 --- a/test/behavior/packed-struct.zig +++ b/test/behavior/packed-struct.zig @@ -258,7 +258,6 @@ test "nested packed struct unaligned" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; if (native_endian != .little) return error.SkipZigTest; // Byte aligned packed struct field pointers have not been implemented yet const S1 = packed struct { @@ -331,7 +330,6 @@ test "byte-aligned field pointer offsets" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { const A = packed struct { @@ -434,7 +432,6 @@ test "nested packed struct field pointers" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // ubsan unaligned pointer access - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; if (native_endian != .little) return error.SkipZigTest; // Byte aligned packed struct field pointers have not been implemented yet const S2 = packed struct { @@ -962,7 +959,6 @@ test "pointer to container level packed struct field" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = packed struct(u32) { test_bit: bool, @@ -1008,8 +1004,6 @@ test "bitcast back and forth" { } test "field access of packed struct smaller than its abi size inside struct initialized with rls" { - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - // Originally reported at https://github.com/ziglang/zig/issues/14200 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; @@ -1028,8 +1022,6 @@ test "field access of packed struct smaller than its abi size inside struct init } test "modify nested packed struct aligned field" { - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - // Originally reported at https://github.com/ziglang/zig/issues/14632 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; @@ -1163,7 +1155,6 @@ test "assignment to non-byte-aligned field in packed struct" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const Frame = packed struct { num: u20, diff --git a/test/behavior/packed-union.zig b/test/behavior/packed-union.zig index b0b0bd7f39..aa4f98b783 100644 --- a/test/behavior/packed-union.zig +++ b/test/behavior/packed-union.zig @@ -8,7 +8,6 @@ test "flags in packed union" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; try testFlagsInPackedUnion(); try comptime testFlagsInPackedUnion(); @@ -51,7 +50,6 @@ test "flags in packed union at offset" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; try testFlagsInPackedUnionAtOffset(); try comptime testFlagsInPackedUnionAtOffset(); diff --git a/test/behavior/src.zig b/test/behavior/src.zig index f0455bdd2e..ebf6ab06b0 100644 --- a/test/behavior/src.zig +++ b/test/behavior/src.zig @@ -37,8 +37,6 @@ test "@src used as a comptime parameter" { } test "@src in tuple passed to anytype function" { - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - const S = struct { fn Foo(a: anytype) u32 { return a[0].line; diff --git a/test/behavior/string_literals.zig b/test/behavior/string_literals.zig index 8a0a105228..13cceb2f83 100644 --- a/test/behavior/string_literals.zig +++ b/test/behavior/string_literals.zig @@ -34,7 +34,6 @@ const ptr_type_name: [*:0]const u8 = type_name; test "@typeName() returns a string literal" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; try std.testing.expect(*const [type_name.len:0]u8 == @TypeOf(type_name)); diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig index f84404c1e3..bda25fa61f 100644 --- a/test/behavior/struct.zig +++ b/test/behavior/struct.zig @@ -68,7 +68,6 @@ const SmallStruct = struct { test "lower unnamed constants" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; var foo = SmallStruct{ .a = 1, .b = 255 }; try expect(foo.first() == 1); @@ -419,7 +418,6 @@ test "packed struct 24bits" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; if (builtin.cpu.arch == .wasm32) return error.SkipZigTest; // TODO if (comptime builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -527,7 +525,6 @@ test "implicit cast packed struct field to const ptr" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const LevelUpMove = packed struct { move_id: u9, @@ -593,7 +590,6 @@ test "bit field access" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; var data = bit_field_1; try expect(getA(&data) == 1); @@ -650,7 +646,6 @@ test "packed array 24bits" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; comptime { try expect(@sizeOf([9]Foo32Bits) == 9 * 4); @@ -718,7 +713,6 @@ test "pointer to packed struct member in a stack variable" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = packed struct { a: u2, @@ -1103,7 +1097,6 @@ test "packed struct with undefined initializers" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { const P = packed struct { @@ -1792,8 +1785,6 @@ test "runtime side-effects in comptime-known struct init" { } test "pointer to struct initialized through reference to anonymous initializer provides result types" { - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - const S = struct { a: u8, b: u16, c: *const anyopaque }; var my_u16: u16 = 0xABCD; _ = &my_u16; diff --git a/test/behavior/union.zig b/test/behavior/union.zig index f6d37db439..8ca3ce0223 100644 --- a/test/behavior/union.zig +++ b/test/behavior/union.zig @@ -1916,7 +1916,6 @@ test "reinterpret packed union" { test "reinterpret packed union inside packed struct" { if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const U = packed union { a: u7, From a1f6a8ef90f0778e4fc5d314eeb1f0a0a93fa53a Mon Sep 17 00:00:00 2001 From: David Rubin Date: Thu, 25 Jul 2024 05:50:30 -0700 Subject: [PATCH 18/27] riscv: airAsm rewrite with this rewrite we can call functions inside of inline assembly, enabling us to use the default start.zig logic all that's left is to implement lr/sc loops for atomically manipulating 1 and 2 byte values, after which we can use the segfault handler logic. --- lib/std/start.zig | 53 +--- src/arch/riscv64/CodeGen.zig | 445 +++++++++++++++++++++++++++------- src/arch/riscv64/Lower.zig | 9 +- src/arch/riscv64/mnem.zig | 6 + test/behavior/basic.zig | 4 - test/behavior/cast.zig | 6 - test/behavior/for.zig | 1 - test/behavior/memcpy.zig | 3 - test/behavior/pointers.zig | 1 - test/behavior/struct.zig | 1 - test/behavior/threadlocal.zig | 1 - test/behavior/union.zig | 1 - 12 files changed, 377 insertions(+), 154 deletions(-) diff --git a/lib/std/start.zig b/lib/std/start.zig index 326857d9c0..f911550f08 100644 --- a/lib/std/start.zig +++ b/lib/std/start.zig @@ -20,8 +20,7 @@ pub const simplified_logic = builtin.zig_backend == .stage2_arm or builtin.zig_backend == .stage2_sparc64 or builtin.cpu.arch == .spirv32 or - builtin.cpu.arch == .spirv64 or - builtin.zig_backend == .stage2_riscv64; + builtin.cpu.arch == .spirv64; comptime { // No matter what, we import the root file, so that any export, test, comptime @@ -41,10 +40,6 @@ comptime { } else if (builtin.os.tag == .opencl) { if (@hasDecl(root, "main")) @export(spirvMain2, .{ .name = "main" }); - } else if (native_arch.isRISCV()) { - if (!@hasDecl(root, "_start")) { - @export(riscv_start, .{ .name = "_start" }); - } } else { if (!@hasDecl(root, "_start")) { @export(_start2, .{ .name = "_start" }); @@ -206,42 +201,6 @@ fn wasi_start() callconv(.C) void { } } -fn riscv_start() callconv(.C) noreturn { - std.process.exit(switch (@typeInfo(@typeInfo(@TypeOf(root.main)).Fn.return_type.?)) { - .NoReturn => root.main(), - .Void => ret: { - root.main(); - break :ret 0; - }, - .Int => |info| ret: { - if (info.bits != 8 or info.signedness == .signed) { - @compileError(bad_main_ret); - } - break :ret root.main(); - }, - .ErrorUnion => ret: { - const result = root.main() catch { - const stderr = std.io.getStdErr().writer(); - stderr.writeAll("failed with error\n") catch { - @panic("failed to print when main returned error"); - }; - break :ret 1; - }; - switch (@typeInfo(@TypeOf(result))) { - .Void => break :ret 0, - .Int => |info| { - if (info.bits != 8 or info.signedness == .signed) { - @compileError(bad_main_ret); - } - return result; - }, - else => @compileError(bad_main_ret), - } - }, - else => @compileError(bad_main_ret), - }); -} - fn EfiMain(handle: uefi.Handle, system_table: *uefi.tables.SystemTable) callconv(.C) usize { uefi.handle = handle; uefi.system_table = system_table; @@ -519,8 +478,10 @@ inline fn callMainWithArgs(argc: usize, argv: [*][*:0]u8, envp: [][*:0]u8) u8 { std.os.argv = argv[0..argc]; std.os.environ = envp; - std.debug.maybeEnableSegfaultHandler(); - maybeIgnoreSigpipe(); + if (builtin.zig_backend != .stage2_riscv64) { + std.debug.maybeEnableSegfaultHandler(); + maybeIgnoreSigpipe(); + } return callMain(); } @@ -563,6 +524,10 @@ pub inline fn callMain() u8 { if (@typeInfo(ReturnType) != .ErrorUnion) @compileError(bad_main_ret); const result = root.main() catch |err| { + if (builtin.zig_backend == .stage2_riscv64) { + std.debug.print("error: failed with error\n", .{}); + return 1; + } std.log.err("{s}", .{@errorName(err)}); if (@errorReturnTrace()) |trace| { std.debug.dumpStackTrace(trace.*); diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index b1ef2bbb10..be17fd8aae 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -20,7 +20,6 @@ const InternPool = @import("../../InternPool.zig"); const Compilation = @import("../../Compilation.zig"); const trace = @import("../../tracy.zig").trace; const codegen = @import("../../codegen.zig"); -const Mnemonic = @import("mnem.zig").Mnemonic; const ErrorMsg = Zcu.ErrorMsg; const Target = std.Target; @@ -38,6 +37,10 @@ const DebugInfoOutput = codegen.DebugInfoOutput; const bits = @import("bits.zig"); const abi = @import("abi.zig"); const Lower = @import("Lower.zig"); +const mnem_import = @import("mnem.zig"); +const Mnemonic = mnem_import.Mnemonic; +const Pseudo = mnem_import.Pseudo; +const encoding = @import("encoding.zig"); const Register = bits.Register; const CSR = bits.CSR; @@ -46,6 +49,7 @@ const Memory = bits.Memory; const FrameIndex = bits.FrameIndex; const RegisterManager = abi.RegisterManager; const RegisterLock = RegisterManager.RegisterLock; +const Instruction = encoding.Instruction; const InnerError = CodeGenError || error{OutOfRegisters}; @@ -3858,8 +3862,55 @@ fn airArrayElemVal(func: *Func, inst: Air.Inst.Index) !void { fn airPtrElemVal(func: *Func, inst: Air.Inst.Index) !void { const is_volatile = false; // TODO + const pt = func.pt; + const zcu = pt.zcu; const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; - const result: MCValue = if (!is_volatile and func.liveness.isUnused(inst)) .unreach else return func.fail("TODO implement ptr_elem_val for {}", .{func.target.cpu.arch}); + const base_ptr_ty = func.typeOf(bin_op.lhs); + + const result: MCValue = if (!is_volatile and func.liveness.isUnused(inst)) .unreach else result: { + const elem_ty = base_ptr_ty.elemType2(zcu); + if (!elem_ty.hasRuntimeBitsIgnoreComptime(pt)) break :result .none; + + const base_ptr_mcv = try func.resolveInst(bin_op.lhs); + const base_ptr_lock: ?RegisterLock = switch (base_ptr_mcv) { + .register => |reg| func.register_manager.lockRegAssumeUnused(reg), + else => null, + }; + defer if (base_ptr_lock) |lock| func.register_manager.unlockReg(lock); + + const index_mcv = try func.resolveInst(bin_op.rhs); + const index_lock: ?RegisterLock = switch (index_mcv) { + .register => |reg| func.register_manager.lockRegAssumeUnused(reg), + else => null, + }; + defer if (index_lock) |lock| func.register_manager.unlockReg(lock); + + const elem_ptr_reg = if (base_ptr_mcv.isRegister() and func.liveness.operandDies(inst, 0)) + base_ptr_mcv.register + else + try func.copyToTmpRegister(base_ptr_ty, base_ptr_mcv); + const elem_ptr_lock = func.register_manager.lockRegAssumeUnused(elem_ptr_reg); + defer func.register_manager.unlockReg(elem_ptr_lock); + + try func.genBinOp( + .ptr_add, + base_ptr_mcv, + base_ptr_ty, + index_mcv, + Type.u64, + elem_ptr_reg, + ); + + const dst_mcv = try func.allocRegOrMem(func.typeOfIndex(inst), inst, true); + const dst_lock = switch (dst_mcv) { + .register => |reg| func.register_manager.lockRegAssumeUnused(reg), + else => null, + }; + defer if (dst_lock) |lock| func.register_manager.unlockReg(lock); + + try func.load(dst_mcv, .{ .register = elem_ptr_reg }, base_ptr_ty); + break :result dst_mcv; + }; return func.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } @@ -3873,13 +3924,6 @@ fn airPtrElemPtr(func: *Func, inst: Air.Inst.Index) !void { const elem_ptr_ty = func.typeOfIndex(inst); const base_ptr_ty = func.typeOf(extra.lhs); - const base_ptr_mcv = try func.resolveInst(extra.lhs); - const base_ptr_lock: ?RegisterLock = switch (base_ptr_mcv) { - .register => |reg| func.register_manager.lockRegAssumeUnused(reg), - else => null, - }; - defer if (base_ptr_lock) |lock| func.register_manager.unlockReg(lock); - if (elem_ptr_ty.ptrInfo(zcu).flags.vector_index != .none) { // break :result if (func.reuseOperand(inst, extra.lhs, 0, base_ptr_mcv)) // base_ptr_mcv @@ -3888,6 +3932,13 @@ fn airPtrElemPtr(func: *Func, inst: Air.Inst.Index) !void { @panic("audit"); } + const base_ptr_mcv = try func.resolveInst(extra.lhs); + const base_ptr_lock: ?RegisterLock = switch (base_ptr_mcv) { + .register => |reg| func.register_manager.lockRegAssumeUnused(reg), + else => null, + }; + defer if (base_ptr_lock) |lock| func.register_manager.unlockReg(lock); + const index_mcv = try func.resolveInst(extra.rhs); const index_lock: ?RegisterLock = switch (index_mcv) { .register => |reg| func.register_manager.lockRegAssumeUnused(reg), @@ -4392,15 +4443,16 @@ fn airStore(func: *Func, inst: Air.Inst.Index, safety: bool) !void { const ptr = try func.resolveInst(bin_op.lhs); const value = try func.resolveInst(bin_op.rhs); const ptr_ty = func.typeOf(bin_op.lhs); - const value_ty = func.typeOf(bin_op.rhs); - try func.store(ptr, value, ptr_ty, value_ty); + try func.store(ptr, value, ptr_ty); return func.finishAir(inst, .none, .{ bin_op.lhs, bin_op.rhs, .none }); } /// Loads `value` into the "payload" of `pointer`. -fn store(func: *Func, ptr_mcv: MCValue, src_mcv: MCValue, ptr_ty: Type, src_ty: Type) !void { +fn store(func: *Func, ptr_mcv: MCValue, src_mcv: MCValue, ptr_ty: Type) !void { + const zcu = func.pt.zcu; + const src_ty = ptr_ty.childType(zcu); log.debug("storing {}:{} in {}:{}", .{ src_mcv, src_ty.fmt(func.pt), ptr_mcv, ptr_ty.fmt(func.pt) }); switch (ptr_mcv) { @@ -4429,7 +4481,7 @@ fn store(func: *Func, ptr_mcv: MCValue, src_mcv: MCValue, ptr_ty: Type, src_ty: try func.genCopy(src_ty, .{ .indirect = .{ .reg = addr_reg } }, src_mcv); }, - .air_ref => |ptr_ref| try func.store(try func.resolveInst(ptr_ref), src_mcv, ptr_ty, src_ty), + .air_ref => |ptr_ref| try func.store(try func.resolveInst(ptr_ref), src_mcv, ptr_ty), } } @@ -5795,7 +5847,6 @@ fn airBoolOp(func: *Func, inst: Air.Inst.Index) !void { fn airAsm(func: *Func, inst: Air.Inst.Index) !void { const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const extra = func.air.extraData(Air.Asm, ty_pl.payload); - const is_volatile = @as(u1, @truncate(extra.data.flags >> 31)) != 0; const clobbers_len: u31 = @truncate(extra.data.flags); var extra_i: usize = extra.end; const outputs: []const Air.Inst.Ref = @@ -5804,86 +5855,300 @@ fn airAsm(func: *Func, inst: Air.Inst.Index) !void { const inputs: []const Air.Inst.Ref = @ptrCast(func.air.extra[extra_i..][0..extra.data.inputs_len]); extra_i += inputs.len; - const dead = !is_volatile and func.liveness.isUnused(inst); - const result: MCValue = if (dead) .unreach else result: { - if (outputs.len > 1) { - return func.fail("TODO implement codegen for asm with more than 1 output", .{}); - } + var result: MCValue = .none; + var args = std.ArrayList(MCValue).init(func.gpa); + try args.ensureTotalCapacity(outputs.len + inputs.len); + defer { + for (args.items) |arg| if (arg.getReg()) |reg| func.register_manager.unlockReg(.{ + .tracked_index = RegisterManager.indexOfRegIntoTracked(reg) orelse continue, + }); + args.deinit(); + } + var arg_map = std.StringHashMap(u8).init(func.gpa); + try arg_map.ensureTotalCapacity(@intCast(outputs.len + inputs.len)); + defer arg_map.deinit(); - const output_constraint: ?[]const u8 = for (outputs) |output| { - if (output != .none) { - return func.fail("TODO implement codegen for non-expr asm", .{}); - } - const extra_bytes = std.mem.sliceAsBytes(func.air.extra[extra_i..]); - const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(func.air.extra[extra_i..]), 0); - const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); - // This equation accounts for the fact that even if we have exactly 4 bytes - // for the string, we still use the next u32 for the null terminator. - extra_i += (constraint.len + name.len + (2 + 3)) / 4; + var outputs_extra_i = extra_i; + for (outputs) |output| { + const extra_bytes = mem.sliceAsBytes(func.air.extra[extra_i..]); + const constraint = mem.sliceTo(mem.sliceAsBytes(func.air.extra[extra_i..]), 0); + const name = mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); + // This equation accounts for the fact that even if we have exactly 4 bytes + // for the string, we still use the next u32 for the null terminator. + extra_i += (constraint.len + name.len + (2 + 3)) / 4; - break constraint; - } else null; - - for (inputs) |input| { - const input_bytes = std.mem.sliceAsBytes(func.air.extra[extra_i..]); - const constraint = std.mem.sliceTo(input_bytes, 0); - const name = std.mem.sliceTo(input_bytes[constraint.len + 1 ..], 0); - // This equation accounts for the fact that even if we have exactly 4 bytes - // for the string, we still use the next u32 for the null terminator. - extra_i += (constraint.len + name.len + (2 + 3)) / 4; - - if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') { - return func.fail("unrecognized asm input constraint: '{s}'", .{constraint}); - } - const reg_name = constraint[1 .. constraint.len - 1]; - const reg = parseRegName(reg_name) orelse - return func.fail("unrecognized register: '{s}'", .{reg_name}); - - const arg_mcv = try func.resolveInst(input); - try func.register_manager.getReg(reg, null); - try func.genSetReg(func.typeOf(input), reg, arg_mcv); - } - - { - var clobber_i: u32 = 0; - while (clobber_i < clobbers_len) : (clobber_i += 1) { - const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(func.air.extra[extra_i..]), 0); - // This equation accounts for the fact that even if we have exactly 4 bytes - // for the string, we still use the next u32 for the null terminator. - extra_i += clobber.len / 4 + 1; - - if (std.mem.eql(u8, clobber, "") or std.mem.eql(u8, clobber, "memory")) { - // nothing really to do - } else { - try func.register_manager.getReg(parseRegName(clobber) orelse - return func.fail("invalid clobber: '{s}'", .{clobber}), null); + const is_read = switch (constraint[0]) { + '=' => false, + '+' => read: { + if (output == .none) return func.fail( + "read-write constraint unsupported for asm result: '{s}'", + .{constraint}, + ); + break :read true; + }, + else => return func.fail("invalid constraint: '{s}'", .{constraint}), + }; + const is_early_clobber = constraint[1] == '&'; + const rest = constraint[@as(usize, 1) + @intFromBool(is_early_clobber) ..]; + const arg_mcv: MCValue = arg_mcv: { + const arg_maybe_reg: ?Register = if (mem.eql(u8, rest, "m")) + if (output != .none) null else return func.fail( + "memory constraint unsupported for asm result: '{s}'", + .{constraint}, + ) + else if (mem.startsWith(u8, rest, "{") and mem.endsWith(u8, rest, "}")) + parseRegName(rest["{".len .. rest.len - "}".len]) orelse + return func.fail("invalid register constraint: '{s}'", .{constraint}) + else if (rest.len == 1 and std.ascii.isDigit(rest[0])) { + const index = std.fmt.charToDigit(rest[0], 10) catch unreachable; + if (index >= args.items.len) return func.fail("constraint out of bounds: '{s}'", .{ + constraint, + }); + break :arg_mcv args.items[index]; + } else return func.fail("invalid constraint: '{s}'", .{constraint}); + break :arg_mcv if (arg_maybe_reg) |reg| .{ .register = reg } else arg: { + const ptr_mcv = try func.resolveInst(output); + switch (ptr_mcv) { + .immediate => |addr| if (math.cast(i32, @as(i64, @bitCast(addr)))) |_| + break :arg ptr_mcv.deref(), + .register, .register_offset, .lea_frame => break :arg ptr_mcv.deref(), + else => {}, } + break :arg .{ .indirect = .{ .reg = try func.copyToTmpRegister(Type.usize, ptr_mcv) } }; + }; + }; + if (arg_mcv.getReg()) |reg| if (RegisterManager.indexOfRegIntoTracked(reg)) |_| { + _ = func.register_manager.lockReg(reg); + }; + if (!mem.eql(u8, name, "_")) + arg_map.putAssumeCapacityNoClobber(name, @intCast(args.items.len)); + args.appendAssumeCapacity(arg_mcv); + if (output == .none) result = arg_mcv; + if (is_read) try func.load(arg_mcv, .{ .air_ref = output }, func.typeOf(output)); + } + + for (inputs) |input| { + const input_bytes = mem.sliceAsBytes(func.air.extra[extra_i..]); + const constraint = mem.sliceTo(input_bytes, 0); + const name = mem.sliceTo(input_bytes[constraint.len + 1 ..], 0); + // This equation accounts for the fact that even if we have exactly 4 bytes + // for the string, we still use the next u32 for the null terminator. + extra_i += (constraint.len + name.len + (2 + 3)) / 4; + + const ty = func.typeOf(input); + const input_mcv = try func.resolveInst(input); + const arg_mcv: MCValue = if (mem.eql(u8, constraint, "X")) + input_mcv + else if (mem.startsWith(u8, constraint, "{") and mem.endsWith(u8, constraint, "}")) arg: { + const reg = parseRegName(constraint["{".len .. constraint.len - "}".len]) orelse + return func.fail("invalid register constraint: '{s}'", .{constraint}); + try func.register_manager.getReg(reg, null); + try func.genSetReg(ty, reg, input_mcv); + break :arg .{ .register = reg }; + } else return func.fail("invalid constraint: '{s}'", .{constraint}); + if (arg_mcv.getReg()) |reg| if (RegisterManager.indexOfRegIntoTracked(reg)) |_| { + _ = func.register_manager.lockReg(reg); + }; + if (!mem.eql(u8, name, "_")) + arg_map.putAssumeCapacityNoClobber(name, @intCast(args.items.len)); + args.appendAssumeCapacity(arg_mcv); + } + + { + var clobber_i: u32 = 0; + while (clobber_i < clobbers_len) : (clobber_i += 1) { + const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(func.air.extra[extra_i..]), 0); + // This equation accounts for the fact that even if we have exactly 4 bytes + // for the string, we still use the next u32 for the null terminator. + extra_i += clobber.len / 4 + 1; + + if (std.mem.eql(u8, clobber, "") or std.mem.eql(u8, clobber, "memory")) { + // nothing really to do + } else { + try func.register_manager.getReg(parseRegName(clobber) orelse + return func.fail("invalid clobber: '{s}'", .{clobber}), null); } } + } - const asm_source = std.mem.sliceAsBytes(func.air.extra[extra_i..])[0..extra.data.source_len]; + const asm_source = std.mem.sliceAsBytes(func.air.extra[extra_i..])[0..extra.data.source_len]; + var line_it = mem.tokenizeAny(u8, asm_source, "\n\r;"); + next_line: while (line_it.next()) |line| { + var mnem_it = mem.tokenizeAny(u8, line, " \t"); + const instruction: union(enum) { mnem: Mnemonic, pseudo: Pseudo } = while (mnem_it.next()) |mnem_str| { + if (mem.startsWith(u8, mnem_str, "#")) continue :next_line; + if (mem.startsWith(u8, mnem_str, "//")) continue :next_line; + if (std.meta.stringToEnum(Mnemonic, mnem_str)) |mnem| { + break .{ .mnem = mnem }; + } else if (std.meta.stringToEnum(Pseudo, mnem_str)) |pseudo| { + break .{ .pseudo = pseudo }; + } else return func.fail("TODO: airAsm labels, found '{s}'", .{mnem_str}); + } else continue; - if (std.meta.stringToEnum(Mnemonic, asm_source)) |tag| { - _ = try func.addInst(.{ - .tag = tag, - .data = .none, - }); - } else { - return func.fail("TODO: asm_source {s}", .{asm_source}); + const Operand = union(enum) { + none, + reg: Register, + imm: Immediate, + sym: SymbolOffset, + }; + + var ops: [4]Operand = .{.none} ** 4; + var last_op = false; + var op_it = mem.splitScalar(u8, mnem_it.rest(), ','); + next_op: for (&ops) |*op| { + const op_str = while (!last_op) { + const full_str = op_it.next() orelse break :next_op; + const code_str = if (mem.indexOfScalar(u8, full_str, '#') orelse + mem.indexOf(u8, full_str, "//")) |comment| + code: { + last_op = true; + break :code full_str[0..comment]; + } else full_str; + const trim_str = mem.trim(u8, code_str, " \t*"); + if (trim_str.len > 0) break trim_str; + } else break; + + if (parseRegName(op_str)) |reg| { + op.* = .{ .reg = reg }; + } else if (std.fmt.parseInt(i12, op_str, 10)) |int| { + op.* = .{ .imm = Immediate.s(int) }; + } else |_| if (mem.startsWith(u8, op_str, "%[")) { + const mod_index = mem.indexOf(u8, op_str, "]@"); + const modifier = if (mod_index) |index| + op_str[index + "]@".len ..] + else + ""; + + op.* = switch (args.items[ + arg_map.get(op_str["%[".len .. mod_index orelse op_str.len - "]".len]) orelse + return func.fail("no matching constraint: '{s}'", .{op_str}) + ]) { + .load_symbol => |sym_off| if (mem.eql(u8, modifier, "plt")) blk: { + assert(sym_off.off == 0); + break :blk .{ .sym = sym_off }; + } else return func.fail("invalid modifier: '{s}'", .{modifier}), + else => return func.fail("invalid constraint: '{s}'", .{op_str}), + }; + } else return func.fail("invalid operand: '{s}'", .{op_str}); + } else if (op_it.next()) |op_str| return func.fail("extra operand: '{s}'", .{op_str}); + + switch (instruction) { + .mnem => |mnem| { + _ = (switch (ops[0]) { + .none => try func.addInst(.{ + .tag = mnem, + .data = .none, + }), + .reg => |reg1| switch (ops[1]) { + .reg => |reg2| switch (ops[2]) { + .imm => |imm1| try func.addInst(.{ + .tag = mnem, + .data = .{ .i_type = .{ + .rd = reg1, + .rs1 = reg2, + .imm12 = imm1, + } }, + }), + else => error.InvalidInstruction, + }, + else => error.InvalidInstruction, + }, + else => error.InvalidInstruction, + }) catch |err| { + switch (err) { + error.InvalidInstruction => return func.fail( + "invalid instruction: {s} {s} {s} {s} {s}", + .{ + @tagName(mnem), + @tagName(ops[0]), + @tagName(ops[1]), + @tagName(ops[2]), + @tagName(ops[3]), + }, + ), + else => |e| return e, + } + }; + }, + .pseudo => |pseudo| { + (@as(error{InvalidInstruction}!void, switch (pseudo) { + .li => blk: { + if (ops[0] != .reg or ops[1] != .imm) { + break :blk error.InvalidInstruction; + } + + const reg = ops[0].reg; + const imm = ops[1].imm; + + try func.genSetReg(Type.usize, reg, .{ .immediate = imm.asBits(u64) }); + }, + .mv => blk: { + if (ops[0] != .reg or ops[1] != .reg) { + break :blk error.InvalidInstruction; + } + + const dst = ops[0].reg; + const src = ops[1].reg; + + if (dst.class() != .int or src.class() != .int) { + return func.fail("pseudo instruction 'mv' only works on integer registers", .{}); + } + + try func.genSetReg(Type.usize, dst, .{ .register = src }); + }, + .tail => blk: { + if (ops[0] != .sym) { + break :blk error.InvalidInstruction; + } + + const sym_offset = ops[0].sym; + assert(sym_offset.off == 0); + + const random_link_reg, const lock = try func.allocReg(.int); + defer func.register_manager.unlockReg(lock); + + _ = try func.addInst(.{ + .tag = .pseudo_extern_fn_reloc, + .data = .{ .reloc = .{ + .register = random_link_reg, + .atom_index = try func.owner.getSymbolIndex(func), + .sym_index = sym_offset.sym, + } }, + }); + }, + })) catch |err| { + switch (err) { + error.InvalidInstruction => return func.fail( + "invalid instruction: {s} {s} {s} {s} {s}", + .{ + @tagName(pseudo), + @tagName(ops[0]), + @tagName(ops[1]), + @tagName(ops[2]), + @tagName(ops[3]), + }, + ), + else => |e| return e, + } + }; + }, } + } - if (output_constraint) |output| { - if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') { - return func.fail("unrecognized asm output constraint: '{s}'", .{output}); - } - const reg_name = output[2 .. output.len - 1]; - const reg = parseRegName(reg_name) orelse - return func.fail("unrecognized register: '{s}'", .{reg_name}); - break :result .{ .register = reg }; - } else { - break :result .{ .none = {} }; - } - }; + for (outputs, args.items[0..outputs.len]) |output, arg_mcv| { + const extra_bytes = mem.sliceAsBytes(func.air.extra[outputs_extra_i..]); + const constraint = + mem.sliceTo(mem.sliceAsBytes(func.air.extra[outputs_extra_i..]), 0); + const name = mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); + // This equation accounts for the fact that even if we have exactly 4 bytes + // for the string, we still use the next u32 for the null terminator. + outputs_extra_i += (constraint.len + name.len + (2 + 3)) / 4; + + if (output == .none) continue; + if (arg_mcv != .register) continue; + if (constraint.len == 2 and std.ascii.isDigit(constraint[1])) continue; + try func.store(.{ .air_ref = output }, arg_mcv, func.typeOf(output)); + } simple: { var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1); @@ -6867,7 +7132,7 @@ fn airAtomicRmw(func: *Func, inst: Air.Inst.Index) !void { } switch (val_size) { - 1, 2 => return func.fail("TODO: airAtomicRmw Int {}", .{val_size}), + 1, 2 => return func.fail("TODO: airAtomicRmw {s} Int {}", .{ @tagName(op), val_size }), 4, 8 => {}, else => unreachable, } @@ -6997,7 +7262,7 @@ fn airAtomicStore(func: *Func, inst: Air.Inst.Index, order: std.builtin.AtomicOr else => unreachable, } - try func.store(ptr_mcv, val_mcv, ptr_ty, val_ty); + try func.store(ptr_mcv, val_mcv, ptr_ty); return func.finishAir(inst, .unreach, .{ bin_op.lhs, bin_op.rhs, .none }); } @@ -7061,7 +7326,7 @@ fn airMemset(func: *Func, inst: Air.Inst.Index, safety: bool) !void { const len = dst_ptr_ty.childType(zcu).arrayLen(zcu); assert(len != 0); // prevented by Sema - try func.store(dst_ptr, src_val, elem_ptr_ty, elem_ty); + try func.store(dst_ptr, src_val, elem_ptr_ty); const second_elem_ptr_reg, const second_elem_ptr_lock = try func.allocReg(.int); defer func.register_manager.unlockReg(second_elem_ptr_lock); @@ -7108,6 +7373,10 @@ fn airMemcpy(func: *Func, inst: Air.Inst.Index) !void { ); break :len .{ .register = len_reg }; }, + .One => len: { + const array_ty = dst_ty.childType(zcu); + break :len .{ .immediate = array_ty.arrayLen(zcu) * array_ty.childType(zcu).abiSize(pt) }; + }, else => |size| return func.fail("TODO: airMemcpy size {s}", .{@tagName(size)}), }; const len_lock: ?RegisterLock = switch (len_mcv) { diff --git a/src/arch/riscv64/Lower.zig b/src/arch/riscv64/Lower.zig index c8a4ec942c..a3b68b85ba 100644 --- a/src/arch/riscv64/Lower.zig +++ b/src/arch/riscv64/Lower.zig @@ -428,9 +428,10 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index, options: struct { .pseudo_extern_fn_reloc => { const inst_reloc = inst.data.reloc; + const link_reg = inst_reloc.register; try lower.emit(.auipc, &.{ - .{ .reg = .ra }, + .{ .reg = link_reg }, .{ .imm = lower.reloc( .{ .call_extern_fn_reloc = .{ .atom_index = inst_reloc.atom_index, @@ -440,8 +441,8 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index, options: struct { }); try lower.emit(.jalr, &.{ - .{ .reg = .ra }, - .{ .reg = .ra }, + .{ .reg = link_reg }, + .{ .reg = link_reg }, .{ .imm = Immediate.s(0) }, }); }, @@ -523,7 +524,7 @@ fn generic(lower: *Lower, inst: Mir.Inst) Error!void { .{ .reg = csr.rs1 }, .{ .reg = csr.rd }, }, - else => return lower.fail("TODO: generic lower {s}", .{@tagName(mnemonic)}), + else => return lower.fail("TODO: generic lower {s}", .{@tagName(inst.data)}), }); } diff --git a/src/arch/riscv64/mnem.zig b/src/arch/riscv64/mnem.zig index d75bf5ccd0..7f0fd3475f 100644 --- a/src/arch/riscv64/mnem.zig +++ b/src/arch/riscv64/mnem.zig @@ -240,3 +240,9 @@ pub const Mnemonic = enum(u16) { pseudo_fence, pseudo_amo, }; + +pub const Pseudo = enum(u8) { + li, + mv, + tail, +}; diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig index fbddcd15fc..052993ddc6 100644 --- a/test/behavior/basic.zig +++ b/test/behavior/basic.zig @@ -395,7 +395,6 @@ test "array 2D const double ptr" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const rect_2d_vertexes = [_][1]f32{ [_]f32{1.0}, @@ -408,7 +407,6 @@ test "array 2D const double ptr with offset" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const rect_2d_vertexes = [_][2]f32{ [_]f32{ 3.0, 4.239 }, @@ -421,7 +419,6 @@ test "array 3D const double ptr with offset" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const rect_3d_vertexes = [_][2][2]f32{ [_][2]f32{ @@ -634,7 +631,6 @@ test "global constant is loaded with a runtime-known index" { test "multiline string literal is null terminated" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const s1 = \\one diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index cd25259064..39e42c05e6 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -599,7 +599,6 @@ fn testCastPtrOfArrayToSliceAndPtr() !void { test "cast *[1][*]const u8 to [*]const ?[*]const u8" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const window_name = [1][*]const u8{"window name"}; const x: [*]const ?[*]const u8 = &window_name; @@ -953,7 +952,6 @@ test "peer cast [:x]T to [*:x]T" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { fn doTheTest() !void { @@ -1019,7 +1017,6 @@ test "variable initialization uses result locations properly with regards to the test "cast between C pointer with different but compatible types" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { fn foo(arg: [*]c_ushort) u16 { @@ -1540,7 +1537,6 @@ test "cast typed undefined to int" { test "implicit cast from [:0]T to [*c]T" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; var a: [:0]const u8 = "foo"; _ = &a; @@ -2086,7 +2082,6 @@ test "peer type resolution: many compatible pointers" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; var buf = "foo-3".*; @@ -2208,7 +2203,6 @@ test "peer type resolution: pointer attributes are combined correctly" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; var buf_a align(4) = "foo".*; var buf_b align(4) = "bar".*; diff --git a/test/behavior/for.zig b/test/behavior/for.zig index bc433c578a..87ee55eee7 100644 --- a/test/behavior/for.zig +++ b/test/behavior/for.zig @@ -153,7 +153,6 @@ test "for loop with pointer elem var" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const source = "abcdefg"; var target: [source.len]u8 = undefined; diff --git a/test/behavior/memcpy.zig b/test/behavior/memcpy.zig index c570f28ab7..fa9203713d 100644 --- a/test/behavior/memcpy.zig +++ b/test/behavior/memcpy.zig @@ -7,7 +7,6 @@ test "memcpy and memset intrinsics" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; try testMemcpyMemset(); try comptime testMemcpyMemset(); @@ -29,7 +28,6 @@ test "@memcpy with both operands single-ptr-to-array, one is null-terminated" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; try testMemcpyBothSinglePtrArrayOneIsNullTerminated(); try comptime testMemcpyBothSinglePtrArrayOneIsNullTerminated(); @@ -72,7 +70,6 @@ test "@memcpy slice" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; try testMemcpySlice(); try comptime testMemcpySlice(); diff --git a/test/behavior/pointers.zig b/test/behavior/pointers.zig index 5308ebf358..5b8abb9350 100644 --- a/test/behavior/pointers.zig +++ b/test/behavior/pointers.zig @@ -342,7 +342,6 @@ test "array initialization types" { test "null terminated pointer" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { fn doTheTest() !void { diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig index bda25fa61f..52a6ec7930 100644 --- a/test/behavior/struct.zig +++ b/test/behavior/struct.zig @@ -504,7 +504,6 @@ test "packed struct fields are ordered from LSB to MSB" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; var all: u64 = 0x7765443322221111; var bytes: [8]u8 align(@alignOf(Bitfields)) = undefined; diff --git a/test/behavior/threadlocal.zig b/test/behavior/threadlocal.zig index 4418870149..f91e10d12d 100644 --- a/test/behavior/threadlocal.zig +++ b/test/behavior/threadlocal.zig @@ -28,7 +28,6 @@ test "pointer to thread local array" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_llvm) switch (builtin.cpu.arch) { .x86_64, .x86 => {}, else => return error.SkipZigTest, diff --git a/test/behavior/union.zig b/test/behavior/union.zig index 8ca3ce0223..a760a0e34c 100644 --- a/test/behavior/union.zig +++ b/test/behavior/union.zig @@ -100,7 +100,6 @@ const FooExtern = extern union { test "basic extern unions" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; var foo = FooExtern{ .int = 1 }; try expect(foo.int == 1); From 7ff5709e1b04de4c33988ce6a27bc593dcf20f63 Mon Sep 17 00:00:00 2001 From: David Rubin Date: Thu, 25 Jul 2024 23:01:44 -0700 Subject: [PATCH 19/27] riscv: implement `lr/sr` loop logic for non-native atomics --- lib/std/start.zig | 6 +- src/arch/riscv64/CodeGen.zig | 166 +++++++++++++++++++++++----------- src/arch/riscv64/Lower.zig | 49 +++------- src/arch/riscv64/Mir.zig | 6 -- src/arch/riscv64/abi.zig | 5 +- src/arch/riscv64/encoding.zig | 12 +-- src/arch/riscv64/mnem.zig | 7 +- 7 files changed, 138 insertions(+), 113 deletions(-) diff --git a/lib/std/start.zig b/lib/std/start.zig index f911550f08..aeefbaffc0 100644 --- a/lib/std/start.zig +++ b/lib/std/start.zig @@ -478,10 +478,8 @@ inline fn callMainWithArgs(argc: usize, argv: [*][*:0]u8, envp: [][*:0]u8) u8 { std.os.argv = argv[0..argc]; std.os.environ = envp; - if (builtin.zig_backend != .stage2_riscv64) { - std.debug.maybeEnableSegfaultHandler(); - maybeIgnoreSigpipe(); - } + std.debug.maybeEnableSegfaultHandler(); + maybeIgnoreSigpipe(); return callMain(); } diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index be17fd8aae..e0a0033d15 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -4717,14 +4717,11 @@ fn airFence(func: *Func, inst: Air.Inst.Index) !void { }; _ = try func.addInst(.{ - .tag = .pseudo_fence, - .data = .{ - .fence = .{ - .pred = pred, - .succ = succ, - .fm = if (order == .acq_rel) .tso else .none, - }, - }, + .tag = if (order == .acq_rel) .fencetso else .fence, + .data = .{ .fence = .{ + .pred = pred, + .succ = succ, + } }, }); return func.finishAirBookkeeping(); } @@ -5278,12 +5275,12 @@ fn isNull(func: *Func, inst: Air.Inst.Index, opt_ty: Type, opt_mcv: MCValue) !MC .dead, .undef, .immediate, - .register_pair, .register_offset, .lea_frame, .lea_symbol, .reserved_frame, .air_ref, + .register_pair, => unreachable, .register => |opt_reg| { @@ -7109,6 +7106,7 @@ fn airCmpxchg(func: *Func, inst: Air.Inst.Index) !void { fn airAtomicRmw(func: *Func, inst: Air.Inst.Index) !void { const pt = func.pt; + const zcu = pt.zcu; const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; const extra = func.air.extraData(Air.AtomicRmw, pl_op.payload).data; @@ -7131,11 +7129,11 @@ fn airAtomicRmw(func: *Func, inst: Air.Inst.Index) !void { else => unreachable, } - switch (val_size) { - 1, 2 => return func.fail("TODO: airAtomicRmw {s} Int {}", .{ @tagName(op), val_size }), - 4, 8 => {}, + const method: enum { amo, loop } = switch (val_size) { + 1, 2 => .loop, + 4, 8 => .amo, else => unreachable, - } + }; const ptr_register, const ptr_lock = try func.promoteReg(ptr_ty, ptr_mcv); defer if (ptr_lock) |lock| func.register_manager.unlockReg(lock); @@ -7145,6 +7143,7 @@ fn airAtomicRmw(func: *Func, inst: Air.Inst.Index) !void { const result_mcv = try func.allocRegOrMem(val_ty, inst, true); assert(result_mcv == .register); // should fit into 8 bytes + const result_reg = result_mcv.register; const aq, const rl = switch (order) { .unordered => unreachable, @@ -7155,28 +7154,96 @@ fn airAtomicRmw(func: *Func, inst: Air.Inst.Index) !void { .seq_cst => .{ true, true }, }; - _ = try func.addInst(.{ - .tag = .pseudo_amo, - .data = .{ .amo = .{ - .rd = result_mcv.register, - .rs1 = ptr_register, - .rs2 = val_register, - .aq = if (aq) .aq else .none, - .rl = if (rl) .rl else .none, - .op = switch (op) { - .Xchg => .SWAP, - .Add => .ADD, - .Sub => return func.fail("TODO: airAtomicRmw SUB", .{}), - .And => .AND, - .Nand => return func.fail("TODO: airAtomicRmw NAND", .{}), - .Or => .OR, - .Xor => .XOR, - .Max => .MAX, - .Min => .MIN, - }, - .ty = val_ty, - } }, - }); + switch (method) { + .amo => { + const is_d = val_ty.abiSize(pt) == 8; + const is_un = val_ty.isUnsignedInt(zcu); + + const mnem: Mnemonic = switch (op) { + // zig fmt: off + .Xchg => if (is_d) .amoswapd else .amoswapw, + .Add => if (is_d) .amoaddd else .amoaddw, + .And => if (is_d) .amoandd else .amoandw, + .Or => if (is_d) .amoord else .amoorw, + .Xor => if (is_d) .amoxord else .amoxorw, + .Max => if (is_d) if (is_un) .amomaxud else .amomaxd else if (is_un) .amomaxuw else .amomaxw, + .Min => if (is_d) if (is_un) .amominud else .amomind else if (is_un) .amominuw else .amominw, + else => return func.fail("TODO: airAtomicRmw amo {s}", .{@tagName(op)}), + // zig fmt: on + }; + + _ = try func.addInst(.{ + .tag = mnem, + .data = .{ .amo = .{ + .rd = result_reg, + .rs1 = ptr_register, + .rs2 = val_register, + .aq = if (aq) .aq else .none, + .rl = if (rl) .rl else .none, + } }, + }); + }, + .loop => { + // where we'll jump back when the sc fails + const jump_back = try func.addInst(.{ + .tag = .lrw, + .data = .{ .amo = .{ + .rd = result_reg, + .rs1 = ptr_register, + .rs2 = .zero, + .aq = if (aq) .aq else .none, + .rl = if (rl) .rl else .none, + } }, + }); + + const after_reg, const after_lock = try func.allocReg(.int); + defer func.register_manager.unlockReg(after_lock); + + switch (op) { + .Add => { + _ = try func.genBinOp( + .add, + .{ .register = result_reg }, + val_ty, + .{ .register = val_register }, + val_ty, + after_reg, + ); + }, + .Sub => { + _ = try func.genBinOp( + .sub, + .{ .register = result_reg }, + val_ty, + .{ .register = val_register }, + val_ty, + after_reg, + ); + }, + else => return func.fail("TODO: airAtomicRmw loop {s}", .{@tagName(op)}), + } + + _ = try func.addInst(.{ + .tag = .scw, + .data = .{ .amo = .{ + .rd = after_reg, + .rs1 = ptr_register, + .rs2 = after_reg, + .aq = if (aq) .aq else .none, + .rl = if (rl) .rl else .none, + } }, + }); + + _ = try func.addInst(.{ + .tag = .bne, + .data = .{ .b_type = .{ + .inst = jump_back, + .rs1 = after_reg, + .rs2 = .zero, + } }, + }); + }, + } return func.finishAir(inst, result_mcv, .{ pl_op.operand, extra.operand, .none }); } @@ -7199,11 +7266,10 @@ fn airAtomicLoad(func: *Func, inst: Air.Inst.Index) !void { if (order == .seq_cst) { _ = try func.addInst(.{ - .tag = .pseudo_fence, + .tag = .fence, .data = .{ .fence = .{ .pred = .rw, .succ = .rw, - .fm = .none, } }, }); } @@ -7217,14 +7283,11 @@ fn airAtomicLoad(func: *Func, inst: Air.Inst.Index) !void { // Make sure all previous reads happen before any reading or writing accurs. .seq_cst, .acquire => { _ = try func.addInst(.{ - .tag = .pseudo_fence, - .data = .{ - .fence = .{ - .pred = .r, - .succ = .rw, - .fm = .none, - }, - }, + .tag = .fence, + .data = .{ .fence = .{ + .pred = .r, + .succ = .rw, + } }, }); }, else => unreachable, @@ -7249,14 +7312,11 @@ fn airAtomicStore(func: *Func, inst: Air.Inst.Index, order: std.builtin.AtomicOr .unordered, .monotonic => {}, .release, .seq_cst => { _ = try func.addInst(.{ - .tag = .pseudo_fence, - .data = .{ - .fence = .{ - .pred = .rw, - .succ = .w, - .fm = .none, - }, - }, + .tag = .fence, + .data = .{ .fence = .{ + .pred = .rw, + .succ = .w, + } }, }); }, else => unreachable, diff --git a/src/arch/riscv64/Lower.zig b/src/arch/riscv64/Lower.zig index a3b68b85ba..370731c0ae 100644 --- a/src/arch/riscv64/Lower.zig +++ b/src/arch/riscv64/Lower.zig @@ -446,44 +446,6 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index, options: struct { .{ .imm = Immediate.s(0) }, }); }, - - .pseudo_amo => { - const amo = inst.data.amo; - const is_d = amo.ty.abiSize(pt) == 8; - const is_un = amo.ty.isUnsignedInt(pt.zcu); - - const mnem: Mnemonic = switch (amo.op) { - // zig fmt: off - .SWAP => if (is_d) .amoswapd else .amoswapw, - .ADD => if (is_d) .amoaddd else .amoaddw, - .AND => if (is_d) .amoandd else .amoandw, - .OR => if (is_d) .amoord else .amoorw, - .XOR => if (is_d) .amoxord else .amoxorw, - .MAX => if (is_d) if (is_un) .amomaxud else .amomaxd else if (is_un) .amomaxuw else .amomaxw, - .MIN => if (is_d) if (is_un) .amominud else .amomind else if (is_un) .amominuw else .amominw, - // zig fmt: on - }; - - try lower.emit(mnem, &.{ - .{ .reg = inst.data.amo.rd }, - .{ .reg = inst.data.amo.rs1 }, - .{ .reg = inst.data.amo.rs2 }, - .{ .barrier = inst.data.amo.rl }, - .{ .barrier = inst.data.amo.aq }, - }); - }, - - .pseudo_fence => { - const fence = inst.data.fence; - - try lower.emit(switch (fence.fm) { - .tso => .fencetso, - .none => .fence, - }, &.{ - .{ .barrier = fence.succ }, - .{ .barrier = fence.pred }, - }); - }, } return .{ @@ -524,6 +486,17 @@ fn generic(lower: *Lower, inst: Mir.Inst) Error!void { .{ .reg = csr.rs1 }, .{ .reg = csr.rd }, }, + .amo => |amo| &.{ + .{ .reg = amo.rd }, + .{ .reg = amo.rs1 }, + .{ .reg = amo.rs2 }, + .{ .barrier = amo.rl }, + .{ .barrier = amo.aq }, + }, + .fence => |fence| &.{ + .{ .barrier = fence.succ }, + .{ .barrier = fence.pred }, + }, else => return lower.fail("TODO: generic lower {s}", .{@tagName(inst.data)}), }); } diff --git a/src/arch/riscv64/Mir.zig b/src/arch/riscv64/Mir.zig index bb4fd28536..2ae62fd9b2 100644 --- a/src/arch/riscv64/Mir.zig +++ b/src/arch/riscv64/Mir.zig @@ -73,10 +73,6 @@ pub const Inst = struct { fence: struct { pred: Barrier, succ: Barrier, - fm: enum { - none, - tso, - }, }, amo: struct { rd: Register, @@ -84,8 +80,6 @@ pub const Inst = struct { rs2: Register, aq: Barrier, rl: Barrier, - op: AmoOp, - ty: Type, }, csr: struct { csr: CSR, diff --git a/src/arch/riscv64/abi.zig b/src/arch/riscv64/abi.zig index baed5cc68c..9f0a39280d 100644 --- a/src/arch/riscv64/abi.zig +++ b/src/arch/riscv64/abi.zig @@ -125,10 +125,7 @@ pub fn classifySystem(ty: Type, pt: Zcu.PerThread) [8]SystemClass { result[0] = .integer; return result; } - result[0] = .integer; - if (ty.optionalChild(zcu).abiSize(pt) == 0) return result; - result[1] = .integer; - return result; + return memory_class; }, .Int, .Enum, .ErrorSet => { const int_bits = ty.intInfo(pt.zcu).bits; diff --git a/src/arch/riscv64/encoding.zig b/src/arch/riscv64/encoding.zig index 7c1bd274e0..61ba0132f4 100644 --- a/src/arch/riscv64/encoding.zig +++ b/src/arch/riscv64/encoding.zig @@ -353,6 +353,7 @@ pub const Lir = struct { // BRANCH .beq => .{ .opcode = .BRANCH, .format = .B, .data = .{ .f = .{ .funct3 = 0b000 } } }, + .bne => .{ .opcode = .BRANCH, .format = .B, .data = .{ .f = .{ .funct3 = 0b001 } } }, // SYSTEM @@ -378,8 +379,8 @@ pub const Lir = struct { .amoaddw => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .W, .funct5 = 0b00000 } } }, .amoswapw => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .W, .funct5 = 0b00001 } } }, - // LR.W - // SC.W + .lrw => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .W, .funct5 = 0b00010 } } }, + .scw => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .W, .funct5 = 0b00011 } } }, .amoxorw => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .W, .funct5 = 0b00100 } } }, .amoandw => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .W, .funct5 = 0b01100 } } }, .amoorw => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .W, .funct5 = 0b01000 } } }, @@ -388,10 +389,11 @@ pub const Lir = struct { .amominuw => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .W, .funct5 = 0b11000 } } }, .amomaxuw => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .W, .funct5 = 0b11100 } } }, + .amoaddd => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .D, .funct5 = 0b00000 } } }, .amoswapd => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .D, .funct5 = 0b00001 } } }, - // LR.D - // SC.D + .lrd => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .D, .funct5 = 0b00010 } } }, + .scd => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .D, .funct5 = 0b00011 } } }, .amoxord => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .D, .funct5 = 0b00100 } } }, .amoandd => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .D, .funct5 = 0b01100 } } }, .amoord => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .D, .funct5 = 0b01000 } } }, @@ -434,8 +436,6 @@ pub const Lir = struct { .pseudo_compare, .pseudo_not, .pseudo_extern_fn_reloc, - .pseudo_fence, - .pseudo_amo, .nop, => std.debug.panic("lir: didn't catch pseudo {s}", .{@tagName(mnem)}), // zig fmt: on diff --git a/src/arch/riscv64/mnem.zig b/src/arch/riscv64/mnem.zig index 7f0fd3475f..926ee19ba9 100644 --- a/src/arch/riscv64/mnem.zig +++ b/src/arch/riscv64/mnem.zig @@ -40,6 +40,7 @@ pub const Mnemonic = enum(u16) { jal, beq, + bne, // Memory lui, @@ -178,6 +179,8 @@ pub const Mnemonic = enum(u16) { fence, fencetso, + lrw, + scw, amoswapw, amoaddw, amoandw, @@ -188,6 +191,8 @@ pub const Mnemonic = enum(u16) { amomaxuw, amominuw, + lrd, + scd, amoswapd, amoaddd, amoandd, @@ -237,8 +242,6 @@ pub const Mnemonic = enum(u16) { pseudo_compare, pseudo_not, pseudo_extern_fn_reloc, - pseudo_fence, - pseudo_amo, }; pub const Pseudo = enum(u8) { From 046001a34a070dffa6230ccbd8f1571e0b1c1240 Mon Sep 17 00:00:00 2001 From: David Rubin Date: Fri, 26 Jul 2024 02:28:04 -0700 Subject: [PATCH 20/27] riscv implement `@popCount` --- src/arch/riscv64/CodeGen.zig | 271 +++++++++++++++++++--------------- src/arch/riscv64/abi.zig | 2 +- src/arch/riscv64/encoding.zig | 4 +- src/arch/riscv64/mnem.zig | 2 + 4 files changed, 158 insertions(+), 121 deletions(-) diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index e0a0033d15..7f70be6231 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -2844,6 +2844,9 @@ fn genBinOp( .cmp_gt, .cmp_gte, => { + try func.truncateRegister(lhs_ty, lhs_reg); + try func.truncateRegister(rhs_ty, rhs_reg); + _ = try func.addInst(.{ .tag = .pseudo_compare, .data = .{ @@ -3925,10 +3928,6 @@ fn airPtrElemPtr(func: *Func, inst: Air.Inst.Index) !void { const base_ptr_ty = func.typeOf(extra.lhs); if (elem_ptr_ty.ptrInfo(zcu).flags.vector_index != .none) { - // break :result if (func.reuseOperand(inst, extra.lhs, 0, base_ptr_mcv)) - // base_ptr_mcv - // else - // try func.copyToNewRegister(inst, base_ptr_mcv); @panic("audit"); } @@ -3992,7 +3991,7 @@ fn airGetUnionTag(func: *Func, inst: Air.Inst.Index) !void { defer func.register_manager.unlockReg(result_lock); switch (frame_mcv) { - .load_frame => |frame_addr| { + .load_frame => { if (tag_abi_size <= 8) { const off: i32 = if (layout.tag_align.compare(.lt, layout.payload_align)) @intCast(layout.payload_size) @@ -4002,7 +4001,7 @@ fn airGetUnionTag(func: *Func, inst: Air.Inst.Index) !void { try func.genCopy( tag_ty, .{ .register = result_reg }, - .{ .load_frame = .{ .index = frame_addr.index, .off = frame_addr.off + off } }, + frame_mcv.offset(off), ); } else { return func.fail( @@ -4081,7 +4080,37 @@ fn airCtz(func: *Func, inst: Air.Inst.Index) !void { fn airPopcount(func: *Func, inst: Air.Inst.Index) !void { const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else return func.fail("TODO implement airPopcount for {}", .{func.target.cpu.arch}); + const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else result: { + const pt = func.pt; + + const operand = try func.resolveInst(ty_op.operand); + const src_ty = func.typeOf(ty_op.operand); + const operand_reg, const operand_lock = try func.promoteReg(src_ty, operand); + defer if (operand_lock) |lock| func.register_manager.unlockReg(lock); + + const dst_reg, const dst_lock = try func.allocReg(.int); + defer func.register_manager.unlockReg(dst_lock); + + const bit_size = src_ty.bitSize(pt); + switch (bit_size) { + 32, 64 => {}, + 1...31, 33...63 => try func.truncateRegister(src_ty, operand_reg), + else => return func.fail("TODO: airPopcount > 64 bits", .{}), + } + + _ = try func.addInst(.{ + .tag = if (bit_size <= 32) .cpopw else .cpop, + .data = .{ + .r_type = .{ + .rd = dst_reg, + .rs1 = operand_reg, + .rs2 = @enumFromInt(0b00010), // this is the cpop funct5 + }, + }, + }); + + break :result .{ .register = dst_reg }; + }; return func.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } @@ -5630,7 +5659,6 @@ fn lowerBlock(func: *Func, inst: Air.Inst.Index, body: []const Air.Inst.Index) ! fn airSwitchBr(func: *Func, inst: Air.Inst.Index) !void { const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; - const condition = try func.resolveInst(pl_op.operand); const condition_ty = func.typeOf(pl_op.operand); const switch_br = func.air.extraData(Air.SwitchBr, pl_op.payload); var extra_index: usize = switch_br.end; @@ -5638,6 +5666,8 @@ fn airSwitchBr(func: *Func, inst: Air.Inst.Index) !void { const liveness = try func.liveness.getSwitchBr(func.gpa, inst, switch_br.data.cases_len + 1); defer func.gpa.free(liveness.deaths); + const condition = try func.resolveInst(pl_op.operand); + // If the condition dies here in this switch instruction, process // that death now instead of later as this has an effect on // whether it needs to be spilled in the branches @@ -5660,9 +5690,14 @@ fn airSwitchBr(func: *Func, inst: Air.Inst.Index) !void { defer func.gpa.free(relocs); for (items, relocs, 0..) |item, *reloc, i| { - // switch branches must be comptime-known, so this is stored in an immediate const item_mcv = try func.resolveInst(item); + const cond_lock = switch (condition) { + .register => func.register_manager.lockRegAssumeUnused(condition.register), + else => null, + }; + defer if (cond_lock) |lock| func.register_manager.unlockReg(lock); + const cmp_reg, const cmp_lock = try func.allocReg(.int); defer func.register_manager.unlockReg(cmp_lock); @@ -7110,57 +7145,58 @@ fn airAtomicRmw(func: *Func, inst: Air.Inst.Index) !void { const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; const extra = func.air.extraData(Air.AtomicRmw, pl_op.payload).data; - const op = extra.op(); - const order = extra.ordering(); + const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else result: { + const op = extra.op(); + const order = extra.ordering(); - const ptr_ty = func.typeOf(pl_op.operand); - const ptr_mcv = try func.resolveInst(pl_op.operand); + const ptr_ty = func.typeOf(pl_op.operand); + const ptr_mcv = try func.resolveInst(pl_op.operand); - const val_ty = func.typeOf(extra.operand); - const val_size = val_ty.abiSize(pt); - const val_mcv = try func.resolveInst(extra.operand); + const val_ty = func.typeOf(extra.operand); + 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", .{}); + if (!math.isPowerOfTwo(val_size)) + return func.fail("TODO: airAtomicRmw non-pow 2", .{}); - switch (val_ty.zigTypeTag(pt.zcu)) { - .Int => {}, - inline .Bool, .Float, .Enum, .Pointer => |ty| return func.fail("TODO: airAtomicRmw {s}", .{@tagName(ty)}), - else => unreachable, - } + switch (val_ty.zigTypeTag(pt.zcu)) { + .Int => {}, + inline .Bool, .Float, .Enum, .Pointer => |ty| return func.fail("TODO: airAtomicRmw {s}", .{@tagName(ty)}), + else => unreachable, + } - const method: enum { amo, loop } = switch (val_size) { - 1, 2 => .loop, - 4, 8 => .amo, - else => unreachable, - }; + const method: enum { amo, loop } = switch (val_size) { + 1, 2 => .loop, + 4, 8 => .amo, + else => unreachable, + }; - const ptr_register, const ptr_lock = try func.promoteReg(ptr_ty, ptr_mcv); - defer if (ptr_lock) |lock| func.register_manager.unlockReg(lock); + const ptr_register, const ptr_lock = try func.promoteReg(ptr_ty, ptr_mcv); + defer if (ptr_lock) |lock| func.register_manager.unlockReg(lock); - const val_register, const val_lock = try func.promoteReg(val_ty, val_mcv); - defer if (val_lock) |lock| func.register_manager.unlockReg(lock); + const val_register, const val_lock = try func.promoteReg(val_ty, val_mcv); + defer if (val_lock) |lock| func.register_manager.unlockReg(lock); - const result_mcv = try func.allocRegOrMem(val_ty, inst, true); - assert(result_mcv == .register); // should fit into 8 bytes - const result_reg = result_mcv.register; + const result_mcv = try func.allocRegOrMem(val_ty, inst, true); + assert(result_mcv == .register); // should fit into 8 bytes + const result_reg = result_mcv.register; - const aq, const rl = switch (order) { - .unordered => unreachable, - .monotonic => .{ false, false }, - .acquire => .{ true, false }, - .release => .{ false, true }, - .acq_rel => .{ true, true }, - .seq_cst => .{ true, true }, - }; + const aq, const rl = switch (order) { + .unordered => unreachable, + .monotonic => .{ false, false }, + .acquire => .{ true, false }, + .release => .{ false, true }, + .acq_rel => .{ true, true }, + .seq_cst => .{ true, true }, + }; - switch (method) { - .amo => { - const is_d = val_ty.abiSize(pt) == 8; - const is_un = val_ty.isUnsignedInt(zcu); + switch (method) { + .amo => { + const is_d = val_ty.abiSize(pt) == 8; + const is_un = val_ty.isUnsignedInt(zcu); - const mnem: Mnemonic = switch (op) { - // zig fmt: off + const mnem: Mnemonic = switch (op) { + // zig fmt: off .Xchg => if (is_d) .amoswapd else .amoswapw, .Add => if (is_d) .amoaddd else .amoaddw, .And => if (is_d) .amoandd else .amoandw, @@ -7170,82 +7206,79 @@ fn airAtomicRmw(func: *Func, inst: Air.Inst.Index) !void { .Min => if (is_d) if (is_un) .amominud else .amomind else if (is_un) .amominuw else .amominw, else => return func.fail("TODO: airAtomicRmw amo {s}", .{@tagName(op)}), // zig fmt: on - }; + }; - _ = try func.addInst(.{ - .tag = mnem, - .data = .{ .amo = .{ - .rd = result_reg, - .rs1 = ptr_register, - .rs2 = val_register, - .aq = if (aq) .aq else .none, - .rl = if (rl) .rl else .none, - } }, - }); - }, - .loop => { - // where we'll jump back when the sc fails - const jump_back = try func.addInst(.{ - .tag = .lrw, - .data = .{ .amo = .{ - .rd = result_reg, - .rs1 = ptr_register, - .rs2 = .zero, - .aq = if (aq) .aq else .none, - .rl = if (rl) .rl else .none, - } }, - }); + _ = try func.addInst(.{ + .tag = mnem, + .data = .{ .amo = .{ + .rd = result_reg, + .rs1 = ptr_register, + .rs2 = val_register, + .aq = if (aq) .aq else .none, + .rl = if (rl) .rl else .none, + } }, + }); + }, + .loop => { + // where we'll jump back when the sc fails + const jump_back = try func.addInst(.{ + .tag = .lrw, + .data = .{ .amo = .{ + .rd = result_reg, + .rs1 = ptr_register, + .rs2 = .zero, + .aq = if (aq) .aq else .none, + .rl = if (rl) .rl else .none, + } }, + }); - const after_reg, const after_lock = try func.allocReg(.int); - defer func.register_manager.unlockReg(after_lock); + const after_reg, const after_lock = try func.allocReg(.int); + defer func.register_manager.unlockReg(after_lock); - switch (op) { - .Add => { - _ = try func.genBinOp( - .add, - .{ .register = result_reg }, - val_ty, - .{ .register = val_register }, - val_ty, - after_reg, - ); - }, - .Sub => { - _ = try func.genBinOp( - .sub, - .{ .register = result_reg }, - val_ty, - .{ .register = val_register }, - val_ty, - after_reg, - ); - }, - else => return func.fail("TODO: airAtomicRmw loop {s}", .{@tagName(op)}), - } + switch (op) { + .Add, .Sub => |tag| { + _ = try func.genBinOp( + switch (tag) { + .Add => .add, + .Sub => .sub, + else => unreachable, + }, + .{ .register = result_reg }, + val_ty, + .{ .register = val_register }, + val_ty, + after_reg, + ); + }, - _ = try func.addInst(.{ - .tag = .scw, - .data = .{ .amo = .{ - .rd = after_reg, - .rs1 = ptr_register, - .rs2 = after_reg, - .aq = if (aq) .aq else .none, - .rl = if (rl) .rl else .none, - } }, - }); + else => return func.fail("TODO: airAtomicRmw loop {s}", .{@tagName(op)}), + } - _ = try func.addInst(.{ - .tag = .bne, - .data = .{ .b_type = .{ - .inst = jump_back, - .rs1 = after_reg, - .rs2 = .zero, - } }, - }); - }, - } + _ = try func.addInst(.{ + .tag = .scw, + .data = .{ .amo = .{ + .rd = after_reg, + .rs1 = ptr_register, + .rs2 = after_reg, + .aq = if (aq) .aq else .none, + .rl = if (rl) .rl else .none, + } }, + }); - return func.finishAir(inst, result_mcv, .{ pl_op.operand, extra.operand, .none }); + _ = try func.addInst(.{ + .tag = .bne, + .data = .{ .b_type = .{ + .inst = jump_back, + .rs1 = after_reg, + .rs2 = .zero, + } }, + }); + }, + } + break :result result_mcv; + }; + + return func.finishAir(inst, result, .{ pl_op.operand, extra.operand, .none }); } fn airAtomicLoad(func: *Func, inst: Air.Inst.Index) !void { diff --git a/src/arch/riscv64/abi.zig b/src/arch/riscv64/abi.zig index 9f0a39280d..d274c76f08 100644 --- a/src/arch/riscv64/abi.zig +++ b/src/arch/riscv64/abi.zig @@ -164,7 +164,7 @@ pub fn classifySystem(ty: Type, pt: Zcu.PerThread) [8]SystemClass { return memory_class; }, - .Struct => { + .Struct, .Union => { const layout = ty.containerLayout(pt.zcu); const ty_size = ty.abiSize(pt); diff --git a/src/arch/riscv64/encoding.zig b/src/arch/riscv64/encoding.zig index 61ba0132f4..15263aba1d 100644 --- a/src/arch/riscv64/encoding.zig +++ b/src/arch/riscv64/encoding.zig @@ -206,6 +206,7 @@ pub const Lir = struct { .srai => .{ .opcode = .OP_IMM, .format = .I, .data = .{ .sh = .{ .typ = 0b010000, .funct3 = 0b101, .has_5 = true } } }, .clz => .{ .opcode = .OP_IMM, .format = .R, .data = .{ .ff = .{ .funct3 = 0b001, .funct7 = 0b0110000 } } }, + .cpop => .{ .opcode = .OP_IMM, .format = .R, .data = .{ .ff = .{ .funct3 = 0b001, .funct7 = 0b0110000 } } }, // OP_IMM_32 @@ -213,7 +214,8 @@ pub const Lir = struct { .srliw => .{ .opcode = .OP_IMM_32, .format = .I, .data = .{ .sh = .{ .typ = 0b000000, .funct3 = 0b101, .has_5 = false } } }, .sraiw => .{ .opcode = .OP_IMM_32, .format = .I, .data = .{ .sh = .{ .typ = 0b010000, .funct3 = 0b101, .has_5 = false } } }, - .clzw => .{ .opcode = .OP_IMM_32, .format = .R, .data = .{ .ff = .{ .funct3 = 0b001, .funct7 = 0b0110000 } } }, + .clzw => .{ .opcode = .OP_IMM_32, .format = .R, .data = .{ .ff = .{ .funct3 = 0b001, .funct7 = 0b0110000 } } }, + .cpopw => .{ .opcode = .OP_IMM_32, .format = .R, .data = .{ .ff = .{ .funct3 = 0b001, .funct7 = 0b0110000 } } }, // OP_32 diff --git a/src/arch/riscv64/mnem.zig b/src/arch/riscv64/mnem.zig index 926ee19ba9..e37dee74b9 100644 --- a/src/arch/riscv64/mnem.zig +++ b/src/arch/riscv64/mnem.zig @@ -174,6 +174,8 @@ pub const Mnemonic = enum(u16) { // Zbb Extension Instructions clz, clzw, + cpop, + cpopw, // A Extension Instructions fence, From 8da212c11bc54bb78950988f5980c78161af8573 Mon Sep 17 00:00:00 2001 From: David Rubin Date: Fri, 26 Jul 2024 04:00:25 -0700 Subject: [PATCH 21/27] riscv: update tests and fix reuse bug --- src/arch/riscv64/CodeGen.zig | 24 +++++++++++++++----- test/behavior/array.zig | 2 -- test/behavior/atomics.zig | 2 -- test/behavior/basic.zig | 3 --- test/behavior/cast.zig | 8 ------- test/behavior/defer.zig | 1 - test/behavior/enum.zig | 2 -- test/behavior/error.zig | 7 ------ test/behavior/fn.zig | 1 - test/behavior/for.zig | 1 - test/behavior/if.zig | 2 -- test/behavior/null.zig | 1 - test/behavior/optional.zig | 5 ---- test/behavior/pointers.zig | 1 - test/behavior/popcount.zig | 1 - test/behavior/slice.zig | 4 ---- test/behavior/struct.zig | 4 ---- test/behavior/switch.zig | 9 -------- test/behavior/switch_prong_implicit_cast.zig | 1 - test/behavior/tuple.zig | 1 - test/behavior/tuple_declarations.zig | 2 -- test/behavior/type_info.zig | 5 ---- test/behavior/typename.zig | 8 ------- test/behavior/union.zig | 5 ---- test/behavior/union_with_members.zig | 1 - test/behavior/while.zig | 4 ---- 26 files changed, 18 insertions(+), 87 deletions(-) diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index 7f70be6231..0ffdb655ef 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -1982,6 +1982,8 @@ fn truncateRegister(func: *Func, ty: Type, reg: Register) !void { .signedness = .unsigned, .bits = @intCast(ty.bitSize(pt)), }; + assert(reg.class() == .int); + const shift = math.cast(u6, 64 - int_info.bits % 64) orelse return; switch (int_info.signedness) { .signed => { @@ -2844,8 +2846,11 @@ fn genBinOp( .cmp_gt, .cmp_gte, => { - try func.truncateRegister(lhs_ty, lhs_reg); - try func.truncateRegister(rhs_ty, rhs_reg); + assert(lhs_reg.class() == rhs_reg.class()); + if (lhs_reg.class() == .int) { + try func.truncateRegister(lhs_ty, lhs_reg); + try func.truncateRegister(rhs_ty, rhs_reg); + } _ = try func.addInst(.{ .tag = .pseudo_compare, @@ -3923,7 +3928,7 @@ fn airPtrElemPtr(func: *Func, inst: Air.Inst.Index) !void { const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const extra = func.air.extraData(Air.Bin, ty_pl.payload).data; - const result = result: { + const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else result: { const elem_ptr_ty = func.typeOfIndex(inst); const base_ptr_ty = func.typeOf(extra.lhs); @@ -3959,6 +3964,7 @@ fn airPtrElemPtr(func: *Func, inst: Air.Inst.Index) !void { break :result MCValue{ .register = result_reg }; }; + return func.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); } @@ -4409,9 +4415,15 @@ fn airLoad(func: *Func, inst: Air.Inst.Index) !void { const elem_size = elem_ty.abiSize(pt); const dst_mcv: MCValue = blk: { - // "ptr" is 8 bytes, and if the element is more than that, we cannot reuse it. - if (elem_size <= 8 and func.reuseOperand(inst, ty_op.operand, 0, ptr)) { - // The MCValue that holds the pointer can be re-used as the value. + // The MCValue that holds the pointer can be re-used as the value. + // - "ptr" is 8 bytes, and if the element is more than that, we cannot reuse it. + // + // - "ptr" will be stored in an integer register, so the type that we're gonna + // load into it must also be a type that can be inside of an integer register + if (elem_size <= 8 and + (if (ptr == .register) func.typeRegClass(elem_ty) == ptr.register.class() else true) and + func.reuseOperand(inst, ty_op.operand, 0, ptr)) + { break :blk ptr; } else { break :blk try func.allocRegOrMem(elem_ty, inst, true); diff --git a/test/behavior/array.zig b/test/behavior/array.zig index fa20342491..a01e624a5d 100644 --- a/test/behavior/array.zig +++ b/test/behavior/array.zig @@ -907,7 +907,6 @@ test "copied array element doesn't alias source" { test "array initialized with string literal" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { a: u32, @@ -975,7 +974,6 @@ test "accessing multidimensional global array at comptime" { if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { const array = [_][]const []const u8{ diff --git a/test/behavior/atomics.zig b/test/behavior/atomics.zig index c9d7254ef2..c9833b239e 100644 --- a/test/behavior/atomics.zig +++ b/test/behavior/atomics.zig @@ -169,7 +169,6 @@ test "atomic load and rmw with enum" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const Value = enum(u8) { a, b, c }; var x = Value.a; @@ -205,7 +204,6 @@ test "atomicrmw with floats" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .aarch64) { // https://github.com/ziglang/zig/issues/10627 diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig index 052993ddc6..90d12e6858 100644 --- a/test/behavior/basic.zig +++ b/test/behavior/basic.zig @@ -645,7 +645,6 @@ test "string escapes" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; try expectEqualStrings("\"", "\x22"); try expectEqualStrings("\'", "\x27"); @@ -778,7 +777,6 @@ test "discarding the result of various expressions" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { fn foo() !u32 { @@ -1310,7 +1308,6 @@ test "break out of block based on comptime known values" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { const source = "A-"; diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index 39e42c05e6..8118e6ad76 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -24,7 +24,6 @@ test "peer type resolution: ?T and T" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; try expect(peerTypeTAndOptionalT(true, false).? == 0); try expect(peerTypeTAndOptionalT(false, false).? == 3); @@ -301,7 +300,6 @@ test "peer result null and comptime_int" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { fn blah(n: i32) ?i32 { @@ -370,7 +368,6 @@ test "return u8 coercing into ?u32 return type" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { fn doTheTest() !void { @@ -664,7 +661,6 @@ test "@floatCast cast down" { test "peer type resolution: unreachable, error set, unreachable" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const Error = error{ FileDescriptorAlreadyPresentInSet, @@ -1212,7 +1208,6 @@ test "implicitly cast from [N]T to ?[]const T" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; try expect(mem.eql(u8, castToOptionalSlice().?, "hi")); comptime assert(mem.eql(u8, castToOptionalSlice().?, "hi")); @@ -1903,7 +1898,6 @@ test "peer type resolution: optional fixed-width int and comptime_int" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; var a: ?i32 = 42; _ = &a; @@ -2263,7 +2257,6 @@ test "cast builtins can wrap result in optional" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { const MyEnum = enum(u32) { _ }; @@ -2572,7 +2565,6 @@ test "result information is preserved through many nested structures" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { fn doTheTest() !void { diff --git a/test/behavior/defer.zig b/test/behavior/defer.zig index 4ea6f54787..219e88b554 100644 --- a/test/behavior/defer.zig +++ b/test/behavior/defer.zig @@ -53,7 +53,6 @@ test "return variable while defer expression in scope to modify it" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { fn doTheTest() !void { diff --git a/test/behavior/enum.zig b/test/behavior/enum.zig index 32d291e4fd..354d74c4a6 100644 --- a/test/behavior/enum.zig +++ b/test/behavior/enum.zig @@ -1219,8 +1219,6 @@ test "enum tag from a local variable" { } test "auto-numbered enum with signed tag type" { - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - const E = enum(i32) { a, b }; try std.testing.expectEqual(@as(i32, 0), @intFromEnum(E.a)); diff --git a/test/behavior/error.zig b/test/behavior/error.zig index cc37e36207..1903bac8f7 100644 --- a/test/behavior/error.zig +++ b/test/behavior/error.zig @@ -319,7 +319,6 @@ test "error inference with an empty set" { test "error union peer type resolution" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; try testErrorUnionPeerTypeResolution(1); } @@ -467,7 +466,6 @@ test "optional error set is the same size as error set" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; comptime assert(@sizeOf(?anyerror) == @sizeOf(anyerror)); comptime assert(@alignOf(?anyerror) == @alignOf(anyerror)); @@ -915,7 +913,6 @@ test "field access of anyerror results in smaller error set" { test "optional error union return type" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { fn foo() ?anyerror!u32 { @@ -930,7 +927,6 @@ test "optional error union return type" { test "optional error set return type" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const E = error{ A, B }; const S = struct { @@ -944,8 +940,6 @@ test "optional error set return type" { } test "optional error set function parameter" { - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - const S = struct { fn doTheTest(a: ?anyerror) !void { try std.testing.expect(a.? == error.OutOfMemory); @@ -975,7 +969,6 @@ test "returning an error union containing a type with no runtime bits" { test "try used in recursive function with inferred error set" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; const Value = union(enum) { diff --git a/test/behavior/fn.zig b/test/behavior/fn.zig index c0cb29e33a..1e4039c1bb 100644 --- a/test/behavior/fn.zig +++ b/test/behavior/fn.zig @@ -450,7 +450,6 @@ test "implicit cast function to function ptr" { test "method call with optional and error union first param" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { x: i32 = 1234, diff --git a/test/behavior/for.zig b/test/behavior/for.zig index 87ee55eee7..7614fd4683 100644 --- a/test/behavior/for.zig +++ b/test/behavior/for.zig @@ -486,7 +486,6 @@ test "inferred alloc ptr of for loop" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; { var cond = false; diff --git a/test/behavior/if.zig b/test/behavior/if.zig index 2da6e84daf..be8b6ac228 100644 --- a/test/behavior/if.zig +++ b/test/behavior/if.zig @@ -191,8 +191,6 @@ test "if value shouldn't be load-elided if used later (structs)" { } test "if value shouldn't be load-elided if used later (optionals)" { - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - var a: ?i32 = 1; var b: ?i32 = 1; diff --git a/test/behavior/null.zig b/test/behavior/null.zig index 0d32f17388..653003b8f0 100644 --- a/test/behavior/null.zig +++ b/test/behavior/null.zig @@ -73,7 +73,6 @@ fn foo(x: ?i32) ?bool { test "test null runtime" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; try testTestNullRuntime(null); } diff --git a/test/behavior/optional.zig b/test/behavior/optional.zig index 0abd547596..53738a107b 100644 --- a/test/behavior/optional.zig +++ b/test/behavior/optional.zig @@ -9,7 +9,6 @@ const expectEqualStrings = std.testing.expectEqualStrings; test "passing an optional integer as a parameter" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { fn entry() bool { @@ -259,7 +258,6 @@ test "unwrap function call with optional pointer return value" { test "nested orelse" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { fn entry() !void { @@ -342,7 +340,6 @@ test "0-bit child type coerced to optional return ptr result location" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { fn doTheTest() !void { @@ -533,7 +530,6 @@ test "Optional slice size is optimized" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; try expect(@sizeOf(?[]u8) == @sizeOf([]u8)); var a: ?[]const u8 = null; @@ -547,7 +543,6 @@ test "Optional slice passed to function" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { fn foo(a: ?[]const u8) !void { diff --git a/test/behavior/pointers.zig b/test/behavior/pointers.zig index 5b8abb9350..42e3ea0ae9 100644 --- a/test/behavior/pointers.zig +++ b/test/behavior/pointers.zig @@ -569,7 +569,6 @@ test "ptrCast comptime known slice to C pointer" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const s: [:0]const u8 = "foo"; var p: [*c]const u8 = @ptrCast(s); diff --git a/test/behavior/popcount.zig b/test/behavior/popcount.zig index 56a2171083..1bf5f96515 100644 --- a/test/behavior/popcount.zig +++ b/test/behavior/popcount.zig @@ -8,7 +8,6 @@ test "@popCount integers" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; try comptime testPopCountIntegers(); try testPopCountIntegers(); diff --git a/test/behavior/slice.zig b/test/behavior/slice.zig index f0231bfbb7..18c876c3e0 100644 --- a/test/behavior/slice.zig +++ b/test/behavior/slice.zig @@ -834,7 +834,6 @@ test "global slice field access" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { var slice: []const u8 = undefined; @@ -890,7 +889,6 @@ test "empty slice ptr is non null" { test "slice decays to many pointer" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; var buf: [8]u8 = "abcdefg\x00".*; const p: [*:0]const u8 = buf[0..7 :0]; @@ -901,7 +899,6 @@ test "write through pointer to optional slice arg" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { fn bar(foo: *?[]const u8) !void { @@ -954,7 +951,6 @@ test "slicing slices gives correct result" { if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const foo = "1234"; const bar = foo[0..4]; diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig index 52a6ec7930..c8dd22e98c 100644 --- a/test/behavior/struct.zig +++ b/test/behavior/struct.zig @@ -1234,7 +1234,6 @@ test "typed init through error unions and optionals" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { a: u32, @@ -1456,8 +1455,6 @@ test "struct field has a pointer to an aligned version of itself" { } test "struct has only one reference" { - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - const S = struct { fn optionalStructParam(_: ?struct { x: u8 }) void {} fn errorUnionStructParam(_: error{}!struct { x: u8 }) void {} @@ -1980,7 +1977,6 @@ test "runtime call in nested initializer" { if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const Holder = struct { array: []const u8, diff --git a/test/behavior/switch.zig b/test/behavior/switch.zig index ac62461b22..1cec0dfad4 100644 --- a/test/behavior/switch.zig +++ b/test/behavior/switch.zig @@ -316,7 +316,6 @@ test "switch on union with some prongs capturing" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const X = union(enum) { a, @@ -538,7 +537,6 @@ test "switch prongs with cases with identical payload types" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const Union = union(enum) { A: usize, @@ -782,8 +780,6 @@ test "comptime inline switch" { } test "switch capture peer type resolution" { - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - const U = union(enum) { a: u32, b: u64, @@ -799,8 +795,6 @@ test "switch capture peer type resolution" { } test "switch capture peer type resolution for in-memory coercible payloads" { - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - const T1 = c_int; const T2 = @Type(@typeInfo(T1)); @@ -822,7 +816,6 @@ test "switch capture peer type resolution for in-memory coercible payloads" { test "switch pointer capture peer type resolution" { if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const T1 = c_int; const T2 = @Type(@typeInfo(T1)); @@ -925,8 +918,6 @@ test "switch prong captures range" { } test "prong with inline call to unreachable" { - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - const U = union(enum) { void: void, bool: bool, diff --git a/test/behavior/switch_prong_implicit_cast.zig b/test/behavior/switch_prong_implicit_cast.zig index 2281ddd448..54107bb6bd 100644 --- a/test/behavior/switch_prong_implicit_cast.zig +++ b/test/behavior/switch_prong_implicit_cast.zig @@ -18,7 +18,6 @@ test "switch prong implicit cast" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const result = switch (foo(2) catch unreachable) { FormValue.One => false, diff --git a/test/behavior/tuple.zig b/test/behavior/tuple.zig index 030c4b6a56..495e00c409 100644 --- a/test/behavior/tuple.zig +++ b/test/behavior/tuple.zig @@ -405,7 +405,6 @@ test "nested runtime conditionals in tuple initializer" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; var data: u8 = 0; _ = &data; diff --git a/test/behavior/tuple_declarations.zig b/test/behavior/tuple_declarations.zig index dc9214e7bb..e6d5d76fc8 100644 --- a/test/behavior/tuple_declarations.zig +++ b/test/behavior/tuple_declarations.zig @@ -7,7 +7,6 @@ const expectEqualStrings = testing.expectEqualStrings; test "tuple declaration type info" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; { const T = struct { comptime u32 align(2) = 1, []const u8 }; @@ -36,7 +35,6 @@ test "tuple declaration type info" { test "Tuple declaration usage" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const T = struct { u32, []const u8 }; var t: T = .{ 1, "foo" }; diff --git a/test/behavior/type_info.zig b/test/behavior/type_info.zig index 9ac5e25e89..b650248e42 100644 --- a/test/behavior/type_info.zig +++ b/test/behavior/type_info.zig @@ -565,8 +565,6 @@ test "StructField.is_comptime" { } test "typeInfo resolves usingnamespace declarations" { - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - const A = struct { pub const f1 = 42; }; @@ -592,7 +590,6 @@ test "value from struct @typeInfo default_value can be loaded at comptime" { test "@typeInfo decls and usingnamespace" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const A = struct { pub const x = 5; @@ -633,8 +630,6 @@ test "type info of tuple of string literal default value" { } test "@typeInfo only contains pub decls" { - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - const other = struct { const std = @import("std"); diff --git a/test/behavior/typename.zig b/test/behavior/typename.zig index b08de5484e..cd83e40485 100644 --- a/test/behavior/typename.zig +++ b/test/behavior/typename.zig @@ -16,7 +16,6 @@ test "anon fn param" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/9339 try expectEqualStringsIgnoreDigits( @@ -42,7 +41,6 @@ test "anon field init" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; const Foo = .{ @@ -69,7 +67,6 @@ test "basic" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; try expectEqualStrings("i64", @typeName(i64)); try expectEqualStrings("*usize", @typeName(*usize)); @@ -91,7 +88,6 @@ test "top level decl" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; try expectEqualStrings( @@ -142,7 +138,6 @@ test "fn param" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/675 @@ -223,7 +218,6 @@ test "local variable" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; const Foo = struct { a: u32 }; @@ -243,7 +237,6 @@ test "comptime parameters not converted to anytype in function type" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const T = fn (fn (type) void, void) void; try expectEqualStrings("fn (comptime fn (comptime type) void, void) void", @typeName(T)); @@ -253,7 +246,6 @@ test "anon name strategy used in sub expression" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; const S = struct { diff --git a/test/behavior/union.zig b/test/behavior/union.zig index a760a0e34c..b1c36c42cc 100644 --- a/test/behavior/union.zig +++ b/test/behavior/union.zig @@ -171,7 +171,6 @@ test "constant tagged union with payload" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; var empty = TaggedUnionWithPayload{ .Empty = {} }; var full = TaggedUnionWithPayload{ .Full = 13 }; @@ -655,7 +654,6 @@ test "union(enum(u32)) with specified and unspecified tag values" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; comptime assert(Tag(Tag(MultipleChoice2)) == u32); try testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2{ .C = 123 }); @@ -807,7 +805,6 @@ test "return union init with void payload" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { fn entry() !void { @@ -970,7 +967,6 @@ test "function call result coerces from tagged union to the tag" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { const Arch = union(enum) { @@ -1135,7 +1131,6 @@ test "@unionInit on union with tag but no fields" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { const Type = enum(u8) { no_op = 105 }; diff --git a/test/behavior/union_with_members.zig b/test/behavior/union_with_members.zig index 83ce38d5bc..186a30ad63 100644 --- a/test/behavior/union_with_members.zig +++ b/test/behavior/union_with_members.zig @@ -21,7 +21,6 @@ test "enum with members" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const a = ET{ .SINT = -42 }; const b = ET{ .UINT = 42 }; diff --git a/test/behavior/while.zig b/test/behavior/while.zig index fd288a9460..532bac258d 100644 --- a/test/behavior/while.zig +++ b/test/behavior/while.zig @@ -207,7 +207,6 @@ test "while on optional with else result follow else prong" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const result = while (returnNull()) |value| { break value; @@ -219,7 +218,6 @@ test "while on optional with else result follow break prong" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const result = while (returnOptional(10)) |value| { break value; @@ -292,7 +290,6 @@ test "while optional 2 break statements and an else" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { fn entry(opt_t: ?bool, f: bool) !void { @@ -391,7 +388,6 @@ test "breaking from a loop in an if statement" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { fn retOpt() ?u32 { From 9752bbfeb3091b5ee812f3892738a8f1731cb4d3 Mon Sep 17 00:00:00 2001 From: David Rubin Date: Fri, 26 Jul 2024 08:49:34 -0700 Subject: [PATCH 22/27] riscv: implement basic tlv loads and stores --- src/arch/riscv64/CodeGen.zig | 87 ++++++++++++++++++++++++++++++----- src/arch/riscv64/Emit.zig | 25 ++++++++++ src/arch/riscv64/Lower.zig | 30 ++++++++++++ src/arch/riscv64/bits.zig | 2 - src/arch/riscv64/encoding.zig | 1 + src/arch/riscv64/mnem.zig | 2 + 6 files changed, 133 insertions(+), 14 deletions(-) diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index 0ffdb655ef..b3fb6b56da 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -163,8 +163,12 @@ const MCValue = union(enum) { immediate: u64, /// The value doesn't exist in memory yet. load_symbol: SymbolOffset, + /// A TLV value. + load_tlv: u32, /// The address of the memory location not-yet-allocated by the linker. lea_symbol: SymbolOffset, + /// The address of a TLV value. + lea_tlv: u32, /// The value is in a target-specific register. register: Register, /// The value is split across two registers @@ -221,6 +225,7 @@ const MCValue = union(enum) { .lea_frame, .undef, .lea_symbol, + .lea_tlv, .air_ref, .reserved_frame, => false, @@ -230,6 +235,7 @@ const MCValue = union(enum) { .register_offset, .load_frame, .load_symbol, + .load_tlv, .indirect, => true, }; @@ -248,10 +254,12 @@ const MCValue = union(enum) { .undef, .air_ref, .lea_symbol, + .lea_tlv, .reserved_frame, => unreachable, // not in memory .load_symbol => |sym_off| .{ .lea_symbol = sym_off }, + .load_tlv => |sym| .{ .lea_tlv = sym }, .memory => |addr| .{ .immediate = addr }, .load_frame => |off| .{ .lea_frame = off }, .indirect => |reg_off| switch (reg_off.off) { @@ -270,17 +278,19 @@ const MCValue = union(enum) { .indirect, .undef, .air_ref, - .load_frame, .register_pair, + .load_frame, .load_symbol, + .load_tlv, .reserved_frame, => unreachable, // not a pointer .immediate => |addr| .{ .memory = addr }, - .lea_frame => |off| .{ .load_frame = off }, .register => |reg| .{ .indirect = .{ .reg = reg } }, .register_offset => |reg_off| .{ .indirect = reg_off }, + .lea_frame => |off| .{ .load_frame = off }, .lea_symbol => |sym_off| .{ .load_symbol = sym_off }, + .lea_tlv => |sym| .{ .load_tlv = sym }, }; } @@ -298,6 +308,8 @@ const MCValue = union(enum) { .indirect, .load_symbol, .lea_symbol, + .lea_tlv, + .load_tlv, => switch (off) { 0 => mcv, else => unreachable, @@ -355,6 +367,8 @@ const InstTracking = struct { .memory, .load_frame, .lea_frame, + .load_tlv, + .lea_tlv, .load_symbol, .lea_symbol, => result, @@ -410,6 +424,8 @@ const InstTracking = struct { .lea_frame, .load_symbol, .lea_symbol, + .load_tlv, + .lea_tlv, => inst_tracking.long, .dead, .register, @@ -438,6 +454,8 @@ const InstTracking = struct { .lea_frame, .load_symbol, .lea_symbol, + .load_tlv, + .lea_tlv, => assert(std.meta.eql(inst_tracking.long, target.long)), .load_frame, .reserved_frame, @@ -3510,17 +3528,19 @@ fn airWrapOptional(func: *Func, inst: Air.Inst.Index) !void { defer if (pl_lock) |lock| func.register_manager.unlockReg(lock); const opt_mcv = try func.allocRegOrMem(opt_ty, inst, true); - try func.genCopy(pl_ty, opt_mcv, pl_mcv); if (!same_repr) { const pl_abi_size: i32 = @intCast(pl_ty.abiSize(pt)); switch (opt_mcv) { - .load_frame => |frame_addr| try func.genSetMem( - .{ .frame = frame_addr.index }, - frame_addr.off + pl_abi_size, - Type.u8, - .{ .immediate = 1 }, - ), + .load_frame => |frame_addr| { + try func.genCopy(pl_ty, opt_mcv, pl_mcv); + try func.genSetMem( + .{ .frame = frame_addr.index }, + frame_addr.off + pl_abi_size, + Type.u8, + .{ .immediate = 1 }, + ); + }, .register => |opt_reg| { try func.genBinOp( @@ -3531,6 +3551,7 @@ fn airWrapOptional(func: *Func, inst: Air.Inst.Index) !void { Type.u64, opt_reg, ); + try func.genCopy(pl_ty, opt_mcv, pl_mcv); }, else => unreachable, } @@ -4457,12 +4478,14 @@ fn load(func: *Func, dst_mcv: MCValue, ptr_mcv: MCValue, ptr_ty: Type) InnerErro .register_offset, .lea_frame, .lea_symbol, + .lea_tlv, => try func.genCopy(dst_ty, dst_mcv, ptr_mcv.deref()), .memory, .indirect, .load_symbol, .load_frame, + .load_tlv, => { const addr_reg = try func.copyToTmpRegister(ptr_ty, ptr_mcv); const addr_lock = func.register_manager.lockRegAssumeUnused(addr_reg); @@ -4509,12 +4532,14 @@ fn store(func: *Func, ptr_mcv: MCValue, src_mcv: MCValue, ptr_ty: Type) !void { .register_offset, .lea_symbol, .lea_frame, + .lea_tlv, => try func.genCopy(src_ty, ptr_mcv.deref(), src_mcv), .memory, .indirect, .load_symbol, .load_frame, + .load_tlv, => { const addr_reg = try func.copyToTmpRegister(ptr_ty, ptr_mcv); const addr_lock = func.register_manager.lockRegAssumeUnused(addr_reg); @@ -5989,7 +6014,14 @@ fn airAsm(func: *Func, inst: Air.Inst.Index) !void { try func.register_manager.getReg(reg, null); try func.genSetReg(ty, reg, input_mcv); break :arg .{ .register = reg }; - } else return func.fail("invalid constraint: '{s}'", .{constraint}); + } else if (mem.eql(u8, constraint, "r")) arg: { + switch (input_mcv) { + .register => break :arg input_mcv, + else => {}, + } + const temp_reg = try func.copyToTmpRegister(ty, input_mcv); + break :arg .{ .register = temp_reg }; + } else return func.fail("invalid input constraint: '{s}'", .{constraint}); if (arg_mcv.getReg()) |reg| if (RegisterManager.indexOfRegIntoTracked(reg)) |_| { _ = func.register_manager.lockReg(reg); }; @@ -6071,6 +6103,10 @@ fn airAsm(func: *Func, inst: Air.Inst.Index) !void { assert(sym_off.off == 0); break :blk .{ .sym = sym_off }; } else return func.fail("invalid modifier: '{s}'", .{modifier}), + .register => |reg| if (modifier.len == 0) + .{ .reg = reg } + else + return func.fail("invalid modified '{s}'", .{modifier}), else => return func.fail("invalid constraint: '{s}'", .{op_str}), }; } else return func.fail("invalid operand: '{s}'", .{op_str}); @@ -6253,6 +6289,13 @@ fn genCopy(func: *Func, ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !void { ty, src_mcv, ), + .load_tlv => { + const addr_reg, const addr_lock = try func.allocReg(.int); + defer func.register_manager.unlockReg(addr_lock); + + try func.genSetReg(ty, addr_reg, dst_mcv.address()); + try func.genCopy(ty, .{ .indirect = .{ .reg = addr_reg } }, src_mcv); + }, .memory => return func.fail("TODO: genCopy memory", .{}), .register_pair => |dst_regs| { const src_info: ?struct { addr_reg: Register, addr_lock: ?RegisterLock } = switch (src_mcv) { @@ -6774,6 +6817,25 @@ fn genSetReg(func: *Func, ty: Type, reg: Register, src_mcv: MCValue) InnerError! try func.genSetReg(ty, addr_reg, src_mcv.address()); try func.genSetReg(ty, reg, .{ .indirect = .{ .reg = addr_reg } }); }, + .lea_tlv => |sym| { + const atom_index = try func.owner.getSymbolIndex(func); + + _ = try func.addInst(.{ + .tag = .pseudo_load_tlv, + .data = .{ .reloc = .{ + .register = reg, + .atom_index = atom_index, + .sym_index = sym, + } }, + }); + }, + .load_tlv => { + const addr_reg, const addr_lock = try func.allocReg(.int); + defer func.register_manager.unlockReg(addr_lock); + + try func.genSetReg(ty, addr_reg, src_mcv.address()); + try func.genSetReg(ty, reg, .{ .indirect = .{ .reg = addr_reg } }); + }, .air_ref => |ref| try func.genSetReg(ty, reg, try func.resolveInst(ref)), else => return func.fail("TODO: genSetReg {s}", .{@tagName(src_mcv)}), } @@ -6793,7 +6855,6 @@ fn genSetMem( const dst_ptr_mcv: MCValue = switch (base) { .reg => |base_reg| .{ .register_offset = .{ .reg = base_reg, .off = disp } }, .frame => |base_frame_index| .{ .lea_frame = .{ .index = base_frame_index, .off = disp } }, - .reloc => |base_symbol| .{ .lea_symbol = .{ .sym = base_symbol.sym_index, .off = disp } }, }; switch (src_mcv) { .none, @@ -6940,6 +7001,7 @@ fn genSetMem( return func.genSetMem(base, disp, ty, .{ .register = reg }); }, .air_ref => |src_ref| try func.genSetMem(base, disp, ty, try func.resolveInst(src_ref)), + else => return func.fail("TODO: genSetMem {s}", .{@tagName(src_mcv)}), } } @@ -7761,9 +7823,10 @@ fn genTypedValue(func: *Func, val: Value) InnerError!MCValue { .none => .none, .undef => unreachable, .load_symbol => |sym_index| .{ .load_symbol = .{ .sym = sym_index } }, + .load_tlv => |sym_index| .{ .lea_tlv = sym_index }, .immediate => |imm| .{ .immediate = imm }, .memory => |addr| .{ .memory = addr }, - .load_got, .load_direct, .load_tlv => { + .load_got, .load_direct => { return func.fail("TODO: genTypedValue {s}", .{@tagName(mcv)}); }, }, diff --git a/src/arch/riscv64/Emit.zig b/src/arch/riscv64/Emit.zig index 3fd5c405f8..6c04988e78 100644 --- a/src/arch/riscv64/Emit.zig +++ b/src/arch/riscv64/Emit.zig @@ -77,6 +77,31 @@ pub fn emitMir(emit: *Emit) Error!void { .r_addend = 0, }); }, + .load_tlv_reloc => |symbol| { + const elf_file = emit.bin_file.cast(link.File.Elf).?; + + const atom_ptr = elf_file.symbol(symbol.atom_index).atom(elf_file).?; + + const R_RISCV = std.elf.R_RISCV; + + try atom_ptr.addReloc(elf_file, .{ + .r_offset = start_offset, + .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | @intFromEnum(R_RISCV.TPREL_HI20), + .r_addend = 0, + }); + + try atom_ptr.addReloc(elf_file, .{ + .r_offset = start_offset + 4, + .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | @intFromEnum(R_RISCV.TPREL_ADD), + .r_addend = 0, + }); + + try atom_ptr.addReloc(elf_file, .{ + .r_offset = start_offset + 8, + .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | @intFromEnum(R_RISCV.TPREL_LO12_I), + .r_addend = 0, + }); + }, .call_extern_fn_reloc => |symbol| { const elf_file = emit.bin_file.cast(link.File.Elf).?; const atom_ptr = elf_file.symbol(symbol.atom_index).atom(elf_file).?; diff --git a/src/arch/riscv64/Lower.zig b/src/arch/riscv64/Lower.zig index 370731c0ae..fc8202adea 100644 --- a/src/arch/riscv64/Lower.zig +++ b/src/arch/riscv64/Lower.zig @@ -34,6 +34,8 @@ pub const Reloc = struct { /// Relocs the lowered_inst_index and the next instruction. load_symbol_reloc: bits.Symbol, + /// Relocs the lowered_inst_index and the next two instructions. + load_tlv_reloc: bits.Symbol, /// Relocs the lowered_inst_index and the next instruction. call_extern_fn_reloc: bits.Symbol, }; @@ -252,6 +254,34 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index, options: struct { }); }, + .pseudo_load_tlv => { + const payload = inst.data.reloc; + const dst_reg = payload.register; + assert(dst_reg.class() == .int); + + try lower.emit(.lui, &.{ + .{ .reg = dst_reg }, + .{ .imm = lower.reloc(.{ + .load_tlv_reloc = .{ + .atom_index = payload.atom_index, + .sym_index = payload.sym_index, + }, + }) }, + }); + + try lower.emit(.add, &.{ + .{ .reg = dst_reg }, + .{ .reg = dst_reg }, + .{ .reg = .tp }, + }); + + try lower.emit(.addi, &.{ + .{ .reg = dst_reg }, + .{ .reg = dst_reg }, + .{ .imm = Immediate.s(0) }, + }); + }, + .pseudo_lea_rm => { const rm = inst.data.rm; assert(rm.r.class() == .int); diff --git a/src/arch/riscv64/bits.zig b/src/arch/riscv64/bits.zig index 5928449828..45fdd22bc0 100644 --- a/src/arch/riscv64/bits.zig +++ b/src/arch/riscv64/bits.zig @@ -15,7 +15,6 @@ pub const Memory = struct { pub const Base = union(enum) { reg: Register, frame: FrameIndex, - reloc: Symbol, }; pub const Mod = struct { @@ -82,7 +81,6 @@ pub const Memory = struct { .disp = base_loc.disp + offset, }; }, - .reloc => unreachable, } } }; diff --git a/src/arch/riscv64/encoding.zig b/src/arch/riscv64/encoding.zig index 15263aba1d..3ff497a1ea 100644 --- a/src/arch/riscv64/encoding.zig +++ b/src/arch/riscv64/encoding.zig @@ -432,6 +432,7 @@ pub const Lir = struct { .pseudo_j, .pseudo_dead, .pseudo_load_symbol, + .pseudo_load_tlv, .pseudo_mv, .pseudo_restore_regs, .pseudo_spill_regs, diff --git a/src/arch/riscv64/mnem.zig b/src/arch/riscv64/mnem.zig index e37dee74b9..121312e941 100644 --- a/src/arch/riscv64/mnem.zig +++ b/src/arch/riscv64/mnem.zig @@ -234,6 +234,8 @@ pub const Mnemonic = enum(u16) { pseudo_dead, /// Loads the address of a value that hasn't yet been allocated in memory. pseudo_load_symbol, + /// Loads the address of a TLV. + pseudo_load_tlv, /// Moves the value of rs1 to rd. pseudo_mv, From 846bd4036154e82fae5d9385c3e349c99840ac5f Mon Sep 17 00:00:00 2001 From: David Rubin Date: Fri, 26 Jul 2024 12:43:47 -0700 Subject: [PATCH 23/27] riscv: implement `@cmpxchg*` and remove fixes --- src/arch/riscv64/CodeGen.zig | 317 ++++++++++++++++++++++++++++++----- src/arch/riscv64/mnem.zig | 2 + test/behavior/atomics.zig | 3 - 3 files changed, 274 insertions(+), 48 deletions(-) diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index b3fb6b56da..799b1305c4 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -1287,13 +1287,11 @@ fn gen(func: *Func) !void { // ret _ = try func.addInst(.{ .tag = .jalr, - .data = .{ - .i_type = .{ - .rd = .zero, - .rs1 = .ra, - .imm12 = Immediate.s(0), - }, - }, + .data = .{ .i_type = .{ + .rd = .zero, + .rs1 = .ra, + .imm12 = Immediate.s(0), + } }, }); const frame_layout = try func.computeFrameLayout(); @@ -1472,14 +1470,11 @@ fn genLazy(func: *Func, lazy_sym: link.File.LazySymbol) InnerError!void { _ = try func.addInst(.{ .tag = .jalr, - - .data = .{ - .i_type = .{ - .rd = .zero, - .rs1 = .ra, - .imm12 = Immediate.s(0), - }, - }, + .data = .{ .i_type = .{ + .rd = .zero, + .rs1 = .ra, + .imm12 = Immediate.s(0), + } }, }); }, else => return func.fail( @@ -1629,8 +1624,8 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void { .struct_field_val=> try func.airStructFieldVal(inst), .float_from_int => try func.airFloatFromInt(inst), .int_from_float => try func.airIntFromFloat(inst), - .cmpxchg_strong => try func.airCmpxchg(inst), - .cmpxchg_weak => try func.airCmpxchg(inst), + .cmpxchg_strong => try func.airCmpxchg(inst, .strong), + .cmpxchg_weak => try func.airCmpxchg(inst, .weak), .atomic_rmw => try func.airAtomicRmw(inst), .atomic_load => try func.airAtomicLoad(inst), .memcpy => try func.airMemcpy(inst), @@ -3527,7 +3522,8 @@ fn airWrapOptional(func: *Func, inst: Air.Inst.Index) !void { }; defer if (pl_lock) |lock| func.register_manager.unlockReg(lock); - const opt_mcv = try func.allocRegOrMem(opt_ty, inst, true); + const opt_mcv = try func.allocRegOrMem(opt_ty, inst, false); + try func.genCopy(pl_ty, opt_mcv, pl_mcv); if (!same_repr) { const pl_abi_size: i32 = @intCast(pl_ty.abiSize(pt)); @@ -3541,18 +3537,6 @@ fn airWrapOptional(func: *Func, inst: Air.Inst.Index) !void { .{ .immediate = 1 }, ); }, - - .register => |opt_reg| { - try func.genBinOp( - .shl, - .{ .immediate = 1 }, - Type.u64, - .{ .immediate = 32 }, - Type.u64, - opt_reg, - ); - try func.genCopy(pl_ty, opt_mcv, pl_mcv); - }, else => unreachable, } } @@ -5800,6 +5784,7 @@ fn performReloc(func: *Func, inst: Mir.Inst.Index) void { switch (tag) { .beq, + .bne, => func.mir_instructions.items(.data)[inst].b_type.inst = target, .jal => func.mir_instructions.items(.data)[inst].j_type.inst = target, .pseudo_j => func.mir_instructions.items(.data)[inst].j_type.inst = target, @@ -6047,30 +6032,85 @@ fn airAsm(func: *Func, inst: Air.Inst.Index) !void { } } + const Label = struct { + target: Mir.Inst.Index = undefined, + pending_relocs: std.ArrayListUnmanaged(Mir.Inst.Index) = .{}, + + const Kind = enum { definition, reference }; + + fn isValid(kind: Kind, name: []const u8) bool { + for (name, 0..) |c, i| switch (c) { + else => return false, + '$' => if (i == 0) return false, + '.' => {}, + '0'...'9' => if (i == 0) switch (kind) { + .definition => if (name.len != 1) return false, + .reference => { + if (name.len != 2) return false; + switch (name[1]) { + else => return false, + 'B', 'F', 'b', 'f' => {}, + } + }, + }, + '@', 'A'...'Z', '_', 'a'...'z' => {}, + }; + return name.len > 0; + } + }; + var labels: std.StringHashMapUnmanaged(Label) = .{}; + defer { + var label_it = labels.valueIterator(); + while (label_it.next()) |label| label.pending_relocs.deinit(func.gpa); + labels.deinit(func.gpa); + } + const asm_source = std.mem.sliceAsBytes(func.air.extra[extra_i..])[0..extra.data.source_len]; var line_it = mem.tokenizeAny(u8, asm_source, "\n\r;"); next_line: while (line_it.next()) |line| { var mnem_it = mem.tokenizeAny(u8, line, " \t"); - const instruction: union(enum) { mnem: Mnemonic, pseudo: Pseudo } = while (mnem_it.next()) |mnem_str| { + const mnem_str = while (mnem_it.next()) |mnem_str| { if (mem.startsWith(u8, mnem_str, "#")) continue :next_line; if (mem.startsWith(u8, mnem_str, "//")) continue :next_line; - if (std.meta.stringToEnum(Mnemonic, mnem_str)) |mnem| { - break .{ .mnem = mnem }; - } else if (std.meta.stringToEnum(Pseudo, mnem_str)) |pseudo| { - break .{ .pseudo = pseudo }; - } else return func.fail("TODO: airAsm labels, found '{s}'", .{mnem_str}); + if (!mem.endsWith(u8, mnem_str, ":")) break mnem_str; + const label_name = mnem_str[0 .. mnem_str.len - ":".len]; + if (!Label.isValid(.definition, label_name)) + return func.fail("invalid label: '{s}'", .{label_name}); + + const label_gop = try labels.getOrPut(func.gpa, label_name); + if (!label_gop.found_existing) label_gop.value_ptr.* = .{} else { + const anon = std.ascii.isDigit(label_name[0]); + if (!anon and label_gop.value_ptr.pending_relocs.items.len == 0) + return func.fail("redefined label: '{s}'", .{label_name}); + for (label_gop.value_ptr.pending_relocs.items) |pending_reloc| + func.performReloc(pending_reloc); + if (anon) + label_gop.value_ptr.pending_relocs.clearRetainingCapacity() + else + label_gop.value_ptr.pending_relocs.clearAndFree(func.gpa); + } + label_gop.value_ptr.target = @intCast(func.mir_instructions.len); } else continue; + const instruction: union(enum) { mnem: Mnemonic, pseudo: Pseudo } = + if (std.meta.stringToEnum(Mnemonic, mnem_str)) |mnem| + .{ .mnem = mnem } + else if (std.meta.stringToEnum(Pseudo, mnem_str)) |pseudo| + .{ .pseudo = pseudo } + else + return func.fail("invalid mnem str '{s}'", .{mnem_str}); + const Operand = union(enum) { none, reg: Register, imm: Immediate, + inst: Mir.Inst.Index, sym: SymbolOffset, }; var ops: [4]Operand = .{.none} ** 4; var last_op = false; - var op_it = mem.splitScalar(u8, mnem_it.rest(), ','); + var op_it = mem.splitAny(u8, mnem_it.rest(), ",("); next_op: for (&ops) |*op| { const op_str = while (!last_op) { const full_str = op_it.next() orelse break :next_op; @@ -6109,6 +6149,25 @@ fn airAsm(func: *Func, inst: Air.Inst.Index) !void { return func.fail("invalid modified '{s}'", .{modifier}), else => return func.fail("invalid constraint: '{s}'", .{op_str}), }; + } else if (mem.endsWith(u8, op_str, ")")) { + const reg = op_str[0 .. op_str.len - ")".len]; + const addr_reg = parseRegName(reg) orelse + return func.fail("expected valid register, found '{s}'", .{reg}); + + op.* = .{ .reg = addr_reg }; + } else if (Label.isValid(.reference, op_str)) { + const anon = std.ascii.isDigit(op_str[0]); + const label_gop = try labels.getOrPut(func.gpa, op_str[0..if (anon) 1 else op_str.len]); + if (!label_gop.found_existing) label_gop.value_ptr.* = .{}; + if (anon and (op_str[1] == 'b' or op_str[1] == 'B') and !label_gop.found_existing) + return func.fail("undefined label: '{s}'", .{op_str}); + const pending_relocs = &label_gop.value_ptr.pending_relocs; + if (if (anon) + op_str[1] == 'f' or op_str[1] == 'F' + else + !label_gop.found_existing or pending_relocs.items.len > 0) + try pending_relocs.append(func.gpa, @intCast(func.mir_instructions.len)); + op.* = .{ .inst = label_gop.value_ptr.target }; } else return func.fail("invalid operand: '{s}'", .{op_str}); } else if (op_it.next()) |op_str| return func.fail("extra operand: '{s}'", .{op_str}); @@ -6131,6 +6190,39 @@ fn airAsm(func: *Func, inst: Air.Inst.Index) !void { }), else => error.InvalidInstruction, }, + .imm => |imm1| switch (ops[2]) { + .reg => |reg2| switch (mnem) { + .sd => try func.addInst(.{ + .tag = mnem, + .data = .{ .i_type = .{ + .rd = reg2, + .rs1 = reg1, + .imm12 = imm1, + } }, + }), + .ld => try func.addInst(.{ + .tag = mnem, + .data = .{ .i_type = .{ + .rd = reg1, + .rs1 = reg2, + .imm12 = imm1, + } }, + }), + else => error.InvalidInstruction, + }, + else => error.InvalidInstruction, + }, + .none => switch (mnem) { + .jalr => try func.addInst(.{ + .tag = mnem, + .data = .{ .i_type = .{ + .rd = .zero, + .rs1 = reg1, + .imm12 = Immediate.s(0), + } }, + }), + else => error.InvalidInstruction, + }, else => error.InvalidInstruction, }, else => error.InvalidInstruction, @@ -6196,6 +6288,28 @@ fn airAsm(func: *Func, inst: Air.Inst.Index) !void { } }, }); }, + .ret => _ = try func.addInst(.{ + .tag = .jalr, + .data = .{ .i_type = .{ + .rd = .zero, + .rs1 = .ra, + .imm12 = Immediate.s(0), + } }, + }), + .beqz => blk: { + if (ops[0] != .reg or ops[1] != .inst) { + break :blk error.InvalidInstruction; + } + + _ = try func.addInst(.{ + .tag = .beq, + .data = .{ .b_type = .{ + .rs1 = ops[0].reg, + .rs2 = .zero, + .inst = ops[1].inst, + } }, + }); + }, })) catch |err| { switch (err) { error.InvalidInstruction => return func.fail( @@ -6215,6 +6329,10 @@ fn airAsm(func: *Func, inst: Air.Inst.Index) !void { } } + var label_it = labels.iterator(); + while (label_it.next()) |label| if (label.value_ptr.pending_relocs.items.len > 0) + return func.fail("undefined label: '{s}'", .{label.key_ptr.*}); + for (outputs, args.items[0..outputs.len]) |output, arg_mcv| { const extra_bytes = mem.sliceAsBytes(func.air.extra[outputs_extra_i..]); const constraint = @@ -7203,14 +7321,123 @@ fn airIntFromFloat(func: *Func, inst: Air.Inst.Index) !void { return func.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } -fn airCmpxchg(func: *Func, inst: Air.Inst.Index) !void { +fn airCmpxchg(func: *Func, inst: Air.Inst.Index, strength: enum { weak, strong }) !void { + _ = strength; // TODO: do something with this + + const pt = func.pt; const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const extra = func.air.extraData(Air.Block, ty_pl.payload); - _ = extra; - return func.fail("TODO implement airCmpxchg for {}", .{ - func.target.cpu.arch, + const extra = func.air.extraData(Air.Cmpxchg, ty_pl.payload).data; + + const ptr_ty = func.typeOf(extra.ptr); + const val_ty = func.typeOf(extra.expected_value); + const val_abi_size: u32 = @intCast(val_ty.abiSize(pt)); + + switch (val_abi_size) { + 1, 2, 4, 8 => {}, + else => return func.fail("TODO: airCmpxchg Int size {}", .{val_abi_size}), + } + + const succ_order: struct { aq: Mir.Barrier, rl: Mir.Barrier } = switch (extra.successOrder()) { + .unordered, + .release, + .acq_rel, + => unreachable, + + .monotonic => .{ .aq = .none, .rl = .none }, + .acquire => .{ .aq = .aq, .rl = .none }, + .seq_cst => .{ .aq = .aq, .rl = .rl }, + }; + + const ptr_mcv = try func.resolveInst(extra.ptr); + const ptr_reg, const ptr_lock = try func.promoteReg(ptr_ty, ptr_mcv); + defer if (ptr_lock) |lock| func.register_manager.unlockReg(lock); + + const exp_mcv = try func.resolveInst(extra.expected_value); + const exp_reg, const exp_lock = try func.promoteReg(val_ty, exp_mcv); + defer if (exp_lock) |lock| func.register_manager.unlockReg(lock); + try func.truncateRegister(val_ty, exp_reg); + + const new_mcv = try func.resolveInst(extra.new_value); + const new_reg, const new_lock = try func.promoteReg(val_ty, new_mcv); + defer if (new_lock) |lock| func.register_manager.unlockReg(lock); + try func.truncateRegister(val_ty, new_reg); + + const branch_reg, const branch_lock = try func.allocReg(.int); + defer func.register_manager.unlockReg(branch_lock); + + const fallthrough_reg, const fallthrough_lock = try func.allocReg(.int); + defer func.register_manager.unlockReg(fallthrough_lock); + + const jump_back = try func.addInst(.{ + .tag = if (val_ty.bitSize(pt) <= 32) .lrw else .lrd, + .data = .{ .amo = .{ + .aq = succ_order.aq, + .rl = succ_order.rl, + .rd = branch_reg, + .rs1 = ptr_reg, + .rs2 = .zero, + } }, }); - // return func.finishAir(inst, result, .{ extra.ptr, extra.expected_value, extra.new_value }); + try func.truncateRegister(val_ty, branch_reg); + + const jump_forward = try func.addInst(.{ + .tag = .bne, + .data = .{ .b_type = .{ + .rs1 = branch_reg, + .rs2 = exp_reg, + .inst = undefined, + } }, + }); + + _ = try func.addInst(.{ + .tag = if (val_ty.bitSize(pt) <= 32) .scw else .scd, + .data = .{ .amo = .{ + .aq = .none, + .rl = succ_order.rl, + .rd = fallthrough_reg, + .rs1 = ptr_reg, + .rs2 = new_reg, + } }, + }); + try func.truncateRegister(Type.bool, fallthrough_reg); + + _ = try func.addInst(.{ + .tag = .bne, + .data = .{ .b_type = .{ + .rs1 = fallthrough_reg, + .rs2 = .zero, + .inst = jump_back, + } }, + }); + + func.performReloc(jump_forward); + + const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else result: { + const dst_mcv = try func.allocRegOrMem(func.typeOfIndex(inst), inst, false); + + const tmp_reg, const tmp_lock = try func.allocReg(.int); + defer func.register_manager.unlockReg(tmp_lock); + + try func.genBinOp( + .cmp_neq, + .{ .register = branch_reg }, + val_ty, + .{ .register = exp_reg }, + val_ty, + tmp_reg, + ); + + try func.genCopy(val_ty, dst_mcv, .{ .register = branch_reg }); + try func.genCopy( + Type.bool, + dst_mcv.address().offset(@intCast(val_abi_size)).deref(), + .{ .register = tmp_reg }, + ); + + break :result dst_mcv; + }; + + return func.finishAir(inst, result, .{ extra.ptr, extra.expected_value, extra.new_value }); } fn airAtomicRmw(func: *Func, inst: Air.Inst.Index) !void { @@ -7234,8 +7461,8 @@ fn airAtomicRmw(func: *Func, inst: Air.Inst.Index) !void { return func.fail("TODO: airAtomicRmw non-pow 2", .{}); switch (val_ty.zigTypeTag(pt.zcu)) { - .Int => {}, - inline .Bool, .Float, .Enum, .Pointer => |ty| return func.fail("TODO: airAtomicRmw {s}", .{@tagName(ty)}), + .Enum, .Int => {}, + inline .Bool, .Float, .Pointer => |ty| return func.fail("TODO: airAtomicRmw {s}", .{@tagName(ty)}), else => unreachable, } diff --git a/src/arch/riscv64/mnem.zig b/src/arch/riscv64/mnem.zig index 121312e941..cb4734e59c 100644 --- a/src/arch/riscv64/mnem.zig +++ b/src/arch/riscv64/mnem.zig @@ -252,4 +252,6 @@ pub const Pseudo = enum(u8) { li, mv, tail, + beqz, + ret, }; diff --git a/test/behavior/atomics.zig b/test/behavior/atomics.zig index c9833b239e..393829a989 100644 --- a/test/behavior/atomics.zig +++ b/test/behavior/atomics.zig @@ -15,7 +15,6 @@ test "cmpxchg" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; try testCmpxchg(); try comptime testCmpxchg(); @@ -108,7 +107,6 @@ test "cmpxchg with ignored result" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; var x: i32 = 1234; @@ -153,7 +151,6 @@ test "cmpxchg on a global variable" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .aarch64) { // https://github.com/ziglang/zig/issues/10627 From a7498a6a53b2aa50274aa425c91e2e449a736cf4 Mon Sep 17 00:00:00 2001 From: David Rubin Date: Fri, 26 Jul 2024 14:02:48 -0700 Subject: [PATCH 24/27] comp: enable compilation of zig_libc --- src/Compilation.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Compilation.zig b/src/Compilation.zig index 8808e72e04..10961af84a 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -6333,6 +6333,7 @@ fn canBuildZigLibC(target: std.Target, use_llvm: bool) bool { } return switch (target_util.zigBackend(target, use_llvm)) { .stage2_llvm => true, + .stage2_riscv64 => true, .stage2_x86_64 => if (target.ofmt == .elf or target.ofmt == .macho) true else build_options.have_llvm, else => build_options.have_llvm, }; From c20def73af49bac435d21cd058e7b9612ef3d2a5 Mon Sep 17 00:00:00 2001 From: David Rubin Date: Fri, 26 Jul 2024 14:03:20 -0700 Subject: [PATCH 25/27] riscv: workarounds for riscv threading --- lib/std/Thread.zig | 2 +- lib/std/heap/PageAllocator.zig | 14 -------------- 2 files changed, 1 insertion(+), 15 deletions(-) diff --git a/lib/std/Thread.zig b/lib/std/Thread.zig index c3802cc1fe..8775b9a52e 100644 --- a/lib/std/Thread.zig +++ b/lib/std/Thread.zig @@ -385,7 +385,7 @@ pub fn yield() YieldError!void { } /// State to synchronize detachment of spawner thread to spawned thread -const Completion = std.atomic.Value(enum(u8) { +const Completion = std.atomic.Value(enum(if (builtin.zig_backend == .stage2_riscv64) u32 else u8) { running, detached, completed, diff --git a/lib/std/heap/PageAllocator.zig b/lib/std/heap/PageAllocator.zig index 92c8ca6b28..4188c25528 100644 --- a/lib/std/heap/PageAllocator.zig +++ b/lib/std/heap/PageAllocator.zig @@ -34,20 +34,6 @@ fn alloc(_: *anyopaque, n: usize, log2_align: u8, ra: usize) ?[*]u8 { return @ptrCast(addr); } - if (builtin.zig_backend == .stage2_riscv64) { - const aligned_len = mem.alignForward(usize, n, mem.page_size); - const slice = posix.mmap( - null, - aligned_len, - posix.PROT.READ | posix.PROT.WRITE, - .{ .TYPE = .PRIVATE, .ANONYMOUS = true }, - -1, - 0, - ) catch return null; - assert(mem.isAligned(@intFromPtr(slice.ptr), mem.page_size)); - return slice.ptr; - } - const aligned_len = mem.alignForward(usize, n, mem.page_size); const hint = @atomicLoad(@TypeOf(std.heap.next_mmap_addr_hint), &std.heap.next_mmap_addr_hint, .unordered); const slice = posix.mmap( From 8f84212855115799def2106e8b3a807c766058ed Mon Sep 17 00:00:00 2001 From: David Rubin Date: Fri, 26 Jul 2024 14:44:38 -0700 Subject: [PATCH 26/27] riscv: make multi-threaded enabled compilation the default --- src/Package/Module.zig | 2 +- src/target.zig | 3 +-- test/behavior/if.zig | 1 - test/behavior/null.zig | 1 - 4 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/Package/Module.zig b/src/Package/Module.zig index 371b8e9816..02d9921016 100644 --- a/src/Package/Module.zig +++ b/src/Package/Module.zig @@ -159,7 +159,7 @@ pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module { if (options.inherited.single_threaded) |x| break :b x; if (options.parent) |p| break :b p.single_threaded; - break :b target_util.defaultSingleThreaded(target, zig_backend); + break :b target_util.defaultSingleThreaded(target); }; const error_tracing = b: { diff --git a/src/target.zig b/src/target.zig index 9b7e12408e..6ff9e69e61 100644 --- a/src/target.zig +++ b/src/target.zig @@ -60,10 +60,9 @@ pub fn alwaysSingleThreaded(target: std.Target) bool { return false; } -pub fn defaultSingleThreaded(target: std.Target, backend: std.builtin.CompilerBackend) bool { +pub fn defaultSingleThreaded(target: std.Target) bool { switch (target.cpu.arch) { .wasm32, .wasm64 => return true, - .riscv64 => if (backend == .stage2_riscv64) return true, else => {}, } switch (target.os.tag) { diff --git a/test/behavior/if.zig b/test/behavior/if.zig index be8b6ac228..69ad917e6a 100644 --- a/test/behavior/if.zig +++ b/test/behavior/if.zig @@ -118,7 +118,6 @@ test "if peer expressions inferred optional type" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; var self: []const u8 = "abcdef"; var index: usize = 0; diff --git a/test/behavior/null.zig b/test/behavior/null.zig index 653003b8f0..9815389838 100644 --- a/test/behavior/null.zig +++ b/test/behavior/null.zig @@ -53,7 +53,6 @@ test "maybe return" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; try maybeReturnImpl(); try comptime maybeReturnImpl(); From b20007daf74f44cfc86c5f2d2d7e2acde079e127 Mon Sep 17 00:00:00 2001 From: David Rubin Date: Sat, 27 Jul 2024 08:14:09 -0700 Subject: [PATCH 27/27] riscv: correct airAsm to generate correct jalr call --- src/arch/riscv64/CodeGen.zig | 44 ++++++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index 799b1305c4..4fd47a43ff 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -1841,7 +1841,7 @@ fn finishAir( } const FrameLayout = struct { - stack_adjust: u32, + stack_adjust: i12, save_reg_list: Mir.RegisterList, }; @@ -1855,10 +1855,7 @@ fn setFrameLoc( const frame_i = @intFromEnum(frame_index); if (aligned) { const alignment: InternPool.Alignment = func.frame_allocs.items(.abi_align)[frame_i]; - offset.* = if (math.sign(offset.*) < 0) - -1 * @as(i32, @intCast(alignment.backward(@intCast(@abs(offset.*))))) - else - @intCast(alignment.forward(@intCast(@abs(offset.*)))); + offset.* = math.sign(offset.*) * @as(i32, @intCast(alignment.backward(@intCast(@abs(offset.*))))); } func.frame_locs.set(frame_i, .{ .base = base, .disp = offset.* }); offset.* += func.frame_allocs.items(.abi_size)[frame_i]; @@ -6216,7 +6213,7 @@ fn airAsm(func: *Func, inst: Air.Inst.Index) !void { .jalr => try func.addInst(.{ .tag = mnem, .data = .{ .i_type = .{ - .rd = .zero, + .rd = .ra, .rs1 = reg1, .imm12 = Immediate.s(0), } }, @@ -7337,15 +7334,34 @@ fn airCmpxchg(func: *Func, inst: Air.Inst.Index, strength: enum { weak, strong } else => return func.fail("TODO: airCmpxchg Int size {}", .{val_abi_size}), } - const succ_order: struct { aq: Mir.Barrier, rl: Mir.Barrier } = switch (extra.successOrder()) { + const lr_order: struct { aq: Mir.Barrier, rl: Mir.Barrier } = switch (extra.successOrder()) { + .unordered, + => unreachable, + + .monotonic, + .release, + => .{ .aq = .none, .rl = .none }, + .acquire, + .acq_rel, + => .{ .aq = .aq, .rl = .none }, + .seq_cst => .{ .aq = .aq, .rl = .rl }, + }; + + const sc_order: struct { aq: Mir.Barrier, rl: Mir.Barrier } = switch (extra.failureOrder()) { .unordered, .release, .acq_rel, => unreachable, - .monotonic => .{ .aq = .none, .rl = .none }, - .acquire => .{ .aq = .aq, .rl = .none }, - .seq_cst => .{ .aq = .aq, .rl = .rl }, + .monotonic, + .acquire, + .seq_cst, + => switch (extra.successOrder()) { + .release, + .seq_cst, + => .{ .aq = .none, .rl = .rl }, + else => .{ .aq = .none, .rl = .none }, + }, }; const ptr_mcv = try func.resolveInst(extra.ptr); @@ -7371,8 +7387,8 @@ fn airCmpxchg(func: *Func, inst: Air.Inst.Index, strength: enum { weak, strong } const jump_back = try func.addInst(.{ .tag = if (val_ty.bitSize(pt) <= 32) .lrw else .lrd, .data = .{ .amo = .{ - .aq = succ_order.aq, - .rl = succ_order.rl, + .aq = lr_order.aq, + .rl = lr_order.rl, .rd = branch_reg, .rs1 = ptr_reg, .rs2 = .zero, @@ -7392,8 +7408,8 @@ fn airCmpxchg(func: *Func, inst: Air.Inst.Index, strength: enum { weak, strong } _ = try func.addInst(.{ .tag = if (val_ty.bitSize(pt) <= 32) .scw else .scd, .data = .{ .amo = .{ - .aq = .none, - .rl = succ_order.rl, + .aq = sc_order.aq, + .rl = sc_order.rl, .rd = fallthrough_reg, .rs1 = ptr_reg, .rs2 = new_reg,