authorgravatar for david@vortan.devDavid Rubin <david@vortan.dev> 2024-07-09 22:53:55-07:00
committergravatar for david@vortan.devDavid Rubin <david@vortan.dev> 2024-07-26 04:05:39-07:00
log93e9c7a963a86f72cd7f603ee7e6d6af2ff3b0b5
tree715b9d5c583f1d41ba62135f49ee61ad20626427
parent8d30fc45c424ed1aaf9067436a64b0744619c250
signaturelock-open Commit is signed but in an unrecognized format.

riscv: implement `@clz`


5 files changed, 89 insertions(+), 55 deletions(-)

src/arch/riscv64/CodeGen.zig+74-53
...@@ -1123,6 +1123,7 @@ const required_features = [_]Target.riscv.Feature{...@@ -1123,6 +1123,7 @@ const required_features = [_]Target.riscv.Feature{
1123 .a,1123 .a,
1124 .zicsr,1124 .zicsr,
1125 .v,1125 .v,
1126 .zbb,
1126};1127};
11271128
1128fn gen(func: *Func) !void {1129fn gen(func: *Func) !void {
...@@ -2385,20 +2386,24 @@ fn genBinOp(...@@ -2385,20 +2386,24 @@ fn genBinOp(
2385 .mul,2386 .mul,
2386 .mul_wrap,2387 .mul_wrap,
2387 .rem,2388 .rem,
2389 .div_trunc,
2388 => {2390 => {
2389 if (!math.isPowerOfTwo(bit_size))
2390 return func.fail(
2391 "TODO: genBinOp {s} non-pow 2, found {}",
2392 .{ @tagName(tag), bit_size },
2393 );
2394
2395 switch (tag) {2391 switch (tag) {
2396 .rem,2392 .rem,
2393 .div_trunc,
2397 => {2394 => {
2398 try func.truncateRegister(lhs_ty, lhs_reg);2395 if (!math.isPowerOfTwo(bit_size)) {
2399 try func.truncateRegister(rhs_ty, rhs_reg);2396 try func.truncateRegister(lhs_ty, lhs_reg);
2397 try func.truncateRegister(rhs_ty, rhs_reg);
2398 }
2399 },
2400 else => {
2401 if (!math.isPowerOfTwo(bit_size))
2402 return func.fail(
2403 "TODO: genBinOp verify {s} non-pow 2, found {}",
2404 .{ @tagName(tag), bit_size },
2405 );
2400 },2406 },
2401 else => {},
2402 }2407 }
24032408
2404 switch (lhs_ty.zigTypeTag(zcu)) {2409 switch (lhs_ty.zigTypeTag(zcu)) {
...@@ -2420,8 +2425,12 @@ fn genBinOp(...@@ -2420,8 +2425,12 @@ fn genBinOp(
2420 else => unreachable,2425 else => unreachable,
2421 },2426 },
2422 .rem => switch (bit_size) {2427 .rem => switch (bit_size) {
2423 64 => if (is_unsigned) .remu else .rem,2428 8, 16, 32 => if (is_unsigned) .remuw else .remw,
2424 else => if (is_unsigned) .remuw else .remu,2429 else => if (is_unsigned) .remu else .rem,
2430 },
2431 .div_trunc => switch (bit_size) {
2432 8, 16, 32 => if (is_unsigned) .divuw else .divw,
2433 else => if (is_unsigned) .divu else .div,
2425 },2434 },
2426 else => unreachable,2435 else => unreachable,
2427 };2436 };
...@@ -2455,7 +2464,7 @@ fn genBinOp(...@@ -2455,7 +2464,7 @@ fn genBinOp(
2455 64 => .fmuld,2464 64 => .fmuld,
2456 else => unreachable,2465 else => unreachable,
2457 },2466 },
2458 else => unreachable,2467 else => return func.fail("TODO: genBinOp {s} Float", .{@tagName(tag)}),
2459 };2468 };
24602469
2461 _ = try func.addInst(.{2470 _ = try func.addInst(.{
...@@ -2588,46 +2597,6 @@ fn genBinOp(...@@ -2588,46 +2597,6 @@ fn genBinOp(
2588 }2597 }
2589 },2598 },
25902599
2591 .div_trunc,
2592 => {
2593 if (!math.isPowerOfTwo(bit_size))
2594 return func.fail(
2595 "TODO: genBinOp {s} non-pow 2, found {}",
2596 .{ @tagName(tag), bit_size },
2597 );
2598
2599 const mir_tag: Mir.Inst.Tag = switch (tag) {
2600 .div_trunc => switch (bit_size) {
2601 8, 16, 32 => if (is_unsigned) .divuw else .divw,
2602 64 => if (is_unsigned) .divu else .div,
2603 else => unreachable,
2604 },
2605 else => unreachable,
2606 };
2607
2608 _ = try func.addInst(.{
2609 .tag = mir_tag,
2610 .ops = .rrr,
2611 .data = .{
2612 .r_type = .{
2613 .rd = dst_reg,
2614 .rs1 = lhs_reg,
2615 .rs2 = rhs_reg,
2616 },
2617 },
2618 });
2619
2620 if (!is_unsigned) {
2621 // truncate when the instruction is larger than the bit size.
2622 switch (bit_size) {
2623 8, 16 => try func.truncateRegister(lhs_ty, dst_reg),
2624 32 => {}, // divw affects the first 32-bits
2625 64 => {}, // div affects the entire register
2626 else => unreachable,
2627 }
2628 }
2629 },
2630
2631 .shr,2600 .shr,
2632 .shr_exact,2601 .shr_exact,
2633 .shl,2602 .shl,
...@@ -3740,7 +3709,59 @@ fn airGetUnionTag(func: *Func, inst: Air.Inst.Index) !void {...@@ -3740,7 +3709,59 @@ fn airGetUnionTag(func: *Func, inst: Air.Inst.Index) !void {
37403709
3741fn airClz(func: *Func, inst: Air.Inst.Index) !void {3710fn airClz(func: *Func, inst: Air.Inst.Index) !void {
3742 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;3711 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3743 const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else return func.fail("TODO implement airClz for {}", .{func.target.cpu.arch});3712 const operand = try func.resolveInst(ty_op.operand);
3713 const ty = func.typeOf(ty_op.operand);
3714
3715 const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else result: {
3716 const src_reg, const src_lock = try func.promoteReg(ty, operand);
3717 defer if (src_lock) |lock| func.register_manager.unlockReg(lock);
3718
3719 const dst_reg: Register = if (func.reuseOperand(
3720 inst,
3721 ty_op.operand,
3722 0,
3723 operand,
3724 ) and operand == .register)
3725 operand.register
3726 else
3727 (try func.allocRegOrMem(func.typeOfIndex(inst), inst, true)).register;
3728
3729 const bit_size = ty.bitSize(func.pt);
3730 if (!math.isPowerOfTwo(bit_size)) try func.truncateRegister(ty, src_reg);
3731
3732 if (bit_size > 64) {
3733 return func.fail("TODO: airClz > 64 bits, found {d}", .{bit_size});
3734 }
3735
3736 _ = try func.addInst(.{
3737 .tag = switch (bit_size) {
3738 32 => .clzw,
3739 else => .clz,
3740 },
3741 .ops = .rrr,
3742 .data = .{
3743 .r_type = .{
3744 .rs2 = .zero, // rs2 is 0 filled in the spec
3745 .rs1 = src_reg,
3746 .rd = dst_reg,
3747 },
3748 },
3749 });
3750
3751 if (!(bit_size == 32 or bit_size == 64)) {
3752 _ = try func.addInst(.{
3753 .tag = .addi,
3754 .ops = .rri,
3755 .data = .{ .i_type = .{
3756 .rd = dst_reg,
3757 .rs1 = dst_reg,
3758 .imm12 = Immediate.s(-@as(i12, @intCast(64 - bit_size % 64))),
3759 } },
3760 });
3761 }
3762
3763 break :result .{ .register = dst_reg };
3764 };
3744 return func.finishAir(inst, result, .{ ty_op.operand, .none, .none });3765 return func.finishAir(inst, result, .{ ty_op.operand, .none, .none });
3745}3766}
37463767
src/arch/riscv64/Encoding.zig+10
...@@ -135,6 +135,7 @@ const Enc = struct {...@@ -135,6 +135,7 @@ const Enc = struct {
135 };135 };
136};136};
137137
138// TODO: this is basically a copy of the MIR table, we should be able to de-dupe them somehow.
138pub const Mnemonic = enum {139pub const Mnemonic = enum {
139 // base mnemonics140 // base mnemonics
140141
...@@ -324,6 +325,10 @@ pub const Mnemonic = enum {...@@ -324,6 +325,10 @@ pub const Mnemonic = enum {
324325
325 // TODO: Q extension326 // TODO: Q extension
326327
328 // Zbb Extension
329 clz,
330 clzw,
331
327 pub fn encoding(mnem: Mnemonic) Enc {332 pub fn encoding(mnem: Mnemonic) Enc {
328 return switch (mnem) {333 return switch (mnem) {
329 // zig fmt: off334 // zig fmt: off
...@@ -368,6 +373,7 @@ pub const Mnemonic = enum {...@@ -368,6 +373,7 @@ pub const Mnemonic = enum {
368 .srli => .{ .opcode = .OP_IMM, .data = .{ .sh = .{ .typ = 0b000000, .funct3 = 0b101, .has_5 = true } } },373 .srli => .{ .opcode = .OP_IMM, .data = .{ .sh = .{ .typ = 0b000000, .funct3 = 0b101, .has_5 = true } } },
369 .srai => .{ .opcode = .OP_IMM, .data = .{ .sh = .{ .typ = 0b010000, .funct3 = 0b101, .has_5 = true } } },374 .srai => .{ .opcode = .OP_IMM, .data = .{ .sh = .{ .typ = 0b010000, .funct3 = 0b101, .has_5 = true } } },
370375
376 .clz => .{ .opcode = .OP_IMM, .data = .{ .ff = .{ .funct3 = 0b001, .funct7 = 0b0110000 } } },
371377
372 // OP_IMM_32378 // OP_IMM_32
373379
...@@ -375,6 +381,7 @@ pub const Mnemonic = enum {...@@ -375,6 +381,7 @@ pub const Mnemonic = enum {
375 .srliw => .{ .opcode = .OP_IMM_32, .data = .{ .sh = .{ .typ = 0b000000, .funct3 = 0b101, .has_5 = false } } },381 .srliw => .{ .opcode = .OP_IMM_32, .data = .{ .sh = .{ .typ = 0b000000, .funct3 = 0b101, .has_5 = false } } },
376 .sraiw => .{ .opcode = .OP_IMM_32, .data = .{ .sh = .{ .typ = 0b010000, .funct3 = 0b101, .has_5 = false } } },382 .sraiw => .{ .opcode = .OP_IMM_32, .data = .{ .sh = .{ .typ = 0b010000, .funct3 = 0b101, .has_5 = false } } },
377383
384 .clzw => .{ .opcode = .OP_IMM_32, .data = .{ .ff = .{ .funct3 = 0b001, .funct7 = 0b0110000 } } },
378385
379 // OP_32386 // OP_32
380387
...@@ -722,6 +729,9 @@ pub const InstEnc = enum {...@@ -722,6 +729,9 @@ pub const InstEnc = enum {
722 .vadcvv,729 .vadcvv,
723 .vmvvx,730 .vmvvx,
724 .vslidedownvx,731 .vslidedownvx,
732
733 .clz,
734 .clzw,
725 => .R,735 => .R,
726736
727 .ecall,737 .ecall,
src/arch/riscv64/Mir.zig+4
...@@ -149,6 +149,10 @@ pub const Inst = struct {...@@ -149,6 +149,10 @@ pub const Inst = struct {
149 vfmulvv,149 vfmulvv,
150 vslidedownvx,150 vslidedownvx,
151151
152 // Zbb Extension Instructions
153 clz,
154 clzw,
155
152 /// A pseudo-instruction. Used for anything that isn't 1:1 with an156 /// A pseudo-instruction. Used for anything that isn't 1:1 with an
153 /// assembly instruction.157 /// assembly instruction.
154 pseudo,158 pseudo,
test/behavior/math.zig-1
...@@ -65,7 +65,6 @@ test "@clz" {...@@ -65,7 +65,6 @@ test "@clz" {
65 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO65 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
66 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO66 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
67 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO67 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
68 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
6968
70 try testClz();69 try testClz();
71 try comptime testClz();70 try comptime testClz();
test/tests.zig+1-1
...@@ -439,7 +439,7 @@ const test_targets = blk: {...@@ -439,7 +439,7 @@ const test_targets = blk: {
439 .target = std.Target.Query.parse(439 .target = std.Target.Query.parse(
440 .{440 .{
441 .arch_os_abi = "riscv64-linux-musl",441 .arch_os_abi = "riscv64-linux-musl",
442 .cpu_features = "baseline+v",442 .cpu_features = "baseline+v+zbb",
443 },443 },
444 ) catch @panic("OOM"),444 ) catch @panic("OOM"),
445 .use_llvm = false,445 .use_llvm = false,