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,