mirror of
https://github.com/ziglang/zig.git
synced 2024-11-15 00:26:57 +00:00
riscv: implement @clz
This commit is contained in:
parent
8d30fc45c4
commit
93e9c7a963
@ -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 });
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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();
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user