riscv: implement @clz

This commit is contained in:
David Rubin 2024-07-09 22:53:55 -07:00
parent 8d30fc45c4
commit 93e9c7a963
No known key found for this signature in database
GPG Key ID: A4390FEB5F00C0A5
5 changed files with 89 additions and 55 deletions

View File

@ -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 });
}

View File

@ -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,

View File

@ -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,

View File

@ -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();

View File

@ -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,