authorgravatar for david@vortan.devDavid Rubin <david@vortan.dev> 2024-07-26 02:28:04-07:00
committergravatar for david@vortan.devDavid Rubin <david@vortan.dev> 2024-07-26 04:19:57-07:00
log046001a34a070dffa6230ccbd8f1571e0b1c1240
tree897d17068a42eb2da3a05ce4a5994e15a55c9c45
parent7ff5709e1b04de4c33988ce6a27bc593dcf20f63
signaturelock-open Commit is signed but in an unrecognized format.

riscv implement `@popCount`


4 files changed, 158 insertions(+), 121 deletions(-)

src/arch/riscv64/CodeGen.zig+152-119
......@@ -2844,6 +2844,9 @@ fn genBinOp(
28442844 .cmp_gt,
28452845 .cmp_gte,
28462846 => {
2847 try func.truncateRegister(lhs_ty, lhs_reg);
2848 try func.truncateRegister(rhs_ty, rhs_reg);
2849
28472850 _ = try func.addInst(.{
28482851 .tag = .pseudo_compare,
28492852 .data = .{
......@@ -3925,10 +3928,6 @@ fn airPtrElemPtr(func: *Func, inst: Air.Inst.Index) !void {
39253928 const base_ptr_ty = func.typeOf(extra.lhs);
39263929
39273930 if (elem_ptr_ty.ptrInfo(zcu).flags.vector_index != .none) {
3928 // break :result if (func.reuseOperand(inst, extra.lhs, 0, base_ptr_mcv))
3929 // base_ptr_mcv
3930 // else
3931 // try func.copyToNewRegister(inst, base_ptr_mcv);
39323931 @panic("audit");
39333932 }
39343933
......@@ -3992,7 +3991,7 @@ fn airGetUnionTag(func: *Func, inst: Air.Inst.Index) !void {
39923991 defer func.register_manager.unlockReg(result_lock);
39933992
39943993 switch (frame_mcv) {
3995 .load_frame => |frame_addr| {
3994 .load_frame => {
39963995 if (tag_abi_size <= 8) {
39973996 const off: i32 = if (layout.tag_align.compare(.lt, layout.payload_align))
39983997 @intCast(layout.payload_size)
......@@ -4002,7 +4001,7 @@ fn airGetUnionTag(func: *Func, inst: Air.Inst.Index) !void {
40024001 try func.genCopy(
40034002 tag_ty,
40044003 .{ .register = result_reg },
4005 .{ .load_frame = .{ .index = frame_addr.index, .off = frame_addr.off + off } },
4004 frame_mcv.offset(off),
40064005 );
40074006 } else {
40084007 return func.fail(
......@@ -4081,7 +4080,37 @@ fn airCtz(func: *Func, inst: Air.Inst.Index) !void {
40814080
40824081fn airPopcount(func: *Func, inst: Air.Inst.Index) !void {
40834082 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
4084 const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else return func.fail("TODO implement airPopcount for {}", .{func.target.cpu.arch});
4083 const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else result: {
4084 const pt = func.pt;
4085
4086 const operand = try func.resolveInst(ty_op.operand);
4087 const src_ty = func.typeOf(ty_op.operand);
4088 const operand_reg, const operand_lock = try func.promoteReg(src_ty, operand);
4089 defer if (operand_lock) |lock| func.register_manager.unlockReg(lock);
4090
4091 const dst_reg, const dst_lock = try func.allocReg(.int);
4092 defer func.register_manager.unlockReg(dst_lock);
4093
4094 const bit_size = src_ty.bitSize(pt);
4095 switch (bit_size) {
4096 32, 64 => {},
4097 1...31, 33...63 => try func.truncateRegister(src_ty, operand_reg),
4098 else => return func.fail("TODO: airPopcount > 64 bits", .{}),
4099 }
4100
4101 _ = try func.addInst(.{
4102 .tag = if (bit_size <= 32) .cpopw else .cpop,
4103 .data = .{
4104 .r_type = .{
4105 .rd = dst_reg,
4106 .rs1 = operand_reg,
4107 .rs2 = @enumFromInt(0b00010), // this is the cpop funct5
4108 },
4109 },
4110 });
4111
4112 break :result .{ .register = dst_reg };
4113 };
40854114 return func.finishAir(inst, result, .{ ty_op.operand, .none, .none });
40864115}
40874116
......@@ -5630,7 +5659,6 @@ fn lowerBlock(func: *Func, inst: Air.Inst.Index, body: []const Air.Inst.Index) !
56305659
56315660fn airSwitchBr(func: *Func, inst: Air.Inst.Index) !void {
56325661 const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
5633 const condition = try func.resolveInst(pl_op.operand);
56345662 const condition_ty = func.typeOf(pl_op.operand);
56355663 const switch_br = func.air.extraData(Air.SwitchBr, pl_op.payload);
56365664 var extra_index: usize = switch_br.end;
......@@ -5638,6 +5666,8 @@ fn airSwitchBr(func: *Func, inst: Air.Inst.Index) !void {
56385666 const liveness = try func.liveness.getSwitchBr(func.gpa, inst, switch_br.data.cases_len + 1);
56395667 defer func.gpa.free(liveness.deaths);
56405668
5669 const condition = try func.resolveInst(pl_op.operand);
5670
56415671 // If the condition dies here in this switch instruction, process
56425672 // that death now instead of later as this has an effect on
56435673 // whether it needs to be spilled in the branches
......@@ -5660,9 +5690,14 @@ fn airSwitchBr(func: *Func, inst: Air.Inst.Index) !void {
56605690 defer func.gpa.free(relocs);
56615691
56625692 for (items, relocs, 0..) |item, *reloc, i| {
5663 // switch branches must be comptime-known, so this is stored in an immediate
56645693 const item_mcv = try func.resolveInst(item);
56655694
5695 const cond_lock = switch (condition) {
5696 .register => func.register_manager.lockRegAssumeUnused(condition.register),
5697 else => null,
5698 };
5699 defer if (cond_lock) |lock| func.register_manager.unlockReg(lock);
5700
56665701 const cmp_reg, const cmp_lock = try func.allocReg(.int);
56675702 defer func.register_manager.unlockReg(cmp_lock);
56685703
......@@ -7110,57 +7145,58 @@ fn airAtomicRmw(func: *Func, inst: Air.Inst.Index) !void {
71107145 const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
71117146 const extra = func.air.extraData(Air.AtomicRmw, pl_op.payload).data;
71127147
7113 const op = extra.op();
7114 const order = extra.ordering();
7148 const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else result: {
7149 const op = extra.op();
7150 const order = extra.ordering();
71157151
7116 const ptr_ty = func.typeOf(pl_op.operand);
7117 const ptr_mcv = try func.resolveInst(pl_op.operand);
7152 const ptr_ty = func.typeOf(pl_op.operand);
7153 const ptr_mcv = try func.resolveInst(pl_op.operand);
71187154
7119 const val_ty = func.typeOf(extra.operand);
7120 const val_size = val_ty.abiSize(pt);
7121 const val_mcv = try func.resolveInst(extra.operand);
7155 const val_ty = func.typeOf(extra.operand);
7156 const val_size = val_ty.abiSize(pt);
7157 const val_mcv = try func.resolveInst(extra.operand);
71227158
7123 if (!math.isPowerOfTwo(val_size))
7124 return func.fail("TODO: airAtomicRmw non-pow 2", .{});
7159 if (!math.isPowerOfTwo(val_size))
7160 return func.fail("TODO: airAtomicRmw non-pow 2", .{});
71257161
7126 switch (val_ty.zigTypeTag(pt.zcu)) {
7127 .Int => {},
7128 inline .Bool, .Float, .Enum, .Pointer => |ty| return func.fail("TODO: airAtomicRmw {s}", .{@tagName(ty)}),
7129 else => unreachable,
7130 }
7162 switch (val_ty.zigTypeTag(pt.zcu)) {
7163 .Int => {},
7164 inline .Bool, .Float, .Enum, .Pointer => |ty| return func.fail("TODO: airAtomicRmw {s}", .{@tagName(ty)}),
7165 else => unreachable,
7166 }
71317167
7132 const method: enum { amo, loop } = switch (val_size) {
7133 1, 2 => .loop,
7134 4, 8 => .amo,
7135 else => unreachable,
7136 };
7168 const method: enum { amo, loop } = switch (val_size) {
7169 1, 2 => .loop,
7170 4, 8 => .amo,
7171 else => unreachable,
7172 };
71377173
7138 const ptr_register, const ptr_lock = try func.promoteReg(ptr_ty, ptr_mcv);
7139 defer if (ptr_lock) |lock| func.register_manager.unlockReg(lock);
7174 const ptr_register, const ptr_lock = try func.promoteReg(ptr_ty, ptr_mcv);
7175 defer if (ptr_lock) |lock| func.register_manager.unlockReg(lock);
71407176
7141 const val_register, const val_lock = try func.promoteReg(val_ty, val_mcv);
7142 defer if (val_lock) |lock| func.register_manager.unlockReg(lock);
7177 const val_register, const val_lock = try func.promoteReg(val_ty, val_mcv);
7178 defer if (val_lock) |lock| func.register_manager.unlockReg(lock);
71437179
7144 const result_mcv = try func.allocRegOrMem(val_ty, inst, true);
7145 assert(result_mcv == .register); // should fit into 8 bytes
7146 const result_reg = result_mcv.register;
7180 const result_mcv = try func.allocRegOrMem(val_ty, inst, true);
7181 assert(result_mcv == .register); // should fit into 8 bytes
7182 const result_reg = result_mcv.register;
71477183
7148 const aq, const rl = switch (order) {
7149 .unordered => unreachable,
7150 .monotonic => .{ false, false },
7151 .acquire => .{ true, false },
7152 .release => .{ false, true },
7153 .acq_rel => .{ true, true },
7154 .seq_cst => .{ true, true },
7155 };
7184 const aq, const rl = switch (order) {
7185 .unordered => unreachable,
7186 .monotonic => .{ false, false },
7187 .acquire => .{ true, false },
7188 .release => .{ false, true },
7189 .acq_rel => .{ true, true },
7190 .seq_cst => .{ true, true },
7191 };
71567192
7157 switch (method) {
7158 .amo => {
7159 const is_d = val_ty.abiSize(pt) == 8;
7160 const is_un = val_ty.isUnsignedInt(zcu);
7193 switch (method) {
7194 .amo => {
7195 const is_d = val_ty.abiSize(pt) == 8;
7196 const is_un = val_ty.isUnsignedInt(zcu);
71617197
7162 const mnem: Mnemonic = switch (op) {
7163 // zig fmt: off
7198 const mnem: Mnemonic = switch (op) {
7199 // zig fmt: off
71647200 .Xchg => if (is_d) .amoswapd else .amoswapw,
71657201 .Add => if (is_d) .amoaddd else .amoaddw,
71667202 .And => if (is_d) .amoandd else .amoandw,
......@@ -7170,82 +7206,79 @@ fn airAtomicRmw(func: *Func, inst: Air.Inst.Index) !void {
71707206 .Min => if (is_d) if (is_un) .amominud else .amomind else if (is_un) .amominuw else .amominw,
71717207 else => return func.fail("TODO: airAtomicRmw amo {s}", .{@tagName(op)}),
71727208 // zig fmt: on
7173 };
7209 };
71747210
7175 _ = try func.addInst(.{
7176 .tag = mnem,
7177 .data = .{ .amo = .{
7178 .rd = result_reg,
7179 .rs1 = ptr_register,
7180 .rs2 = val_register,
7181 .aq = if (aq) .aq else .none,
7182 .rl = if (rl) .rl else .none,
7183 } },
7184 });
7185 },
7186 .loop => {
7187 // where we'll jump back when the sc fails
7188 const jump_back = try func.addInst(.{
7189 .tag = .lrw,
7190 .data = .{ .amo = .{
7191 .rd = result_reg,
7192 .rs1 = ptr_register,
7193 .rs2 = .zero,
7194 .aq = if (aq) .aq else .none,
7195 .rl = if (rl) .rl else .none,
7196 } },
7197 });
7211 _ = try func.addInst(.{
7212 .tag = mnem,
7213 .data = .{ .amo = .{
7214 .rd = result_reg,
7215 .rs1 = ptr_register,
7216 .rs2 = val_register,
7217 .aq = if (aq) .aq else .none,
7218 .rl = if (rl) .rl else .none,
7219 } },
7220 });
7221 },
7222 .loop => {
7223 // where we'll jump back when the sc fails
7224 const jump_back = try func.addInst(.{
7225 .tag = .lrw,
7226 .data = .{ .amo = .{
7227 .rd = result_reg,
7228 .rs1 = ptr_register,
7229 .rs2 = .zero,
7230 .aq = if (aq) .aq else .none,
7231 .rl = if (rl) .rl else .none,
7232 } },
7233 });
71987234
7199 const after_reg, const after_lock = try func.allocReg(.int);
7200 defer func.register_manager.unlockReg(after_lock);
7235 const after_reg, const after_lock = try func.allocReg(.int);
7236 defer func.register_manager.unlockReg(after_lock);
72017237
7202 switch (op) {
7203 .Add => {
7204 _ = try func.genBinOp(
7205 .add,
7206 .{ .register = result_reg },
7207 val_ty,
7208 .{ .register = val_register },
7209 val_ty,
7210 after_reg,
7211 );
7212 },
7213 .Sub => {
7214 _ = try func.genBinOp(
7215 .sub,
7216 .{ .register = result_reg },
7217 val_ty,
7218 .{ .register = val_register },
7219 val_ty,
7220 after_reg,
7221 );
7222 },
7223 else => return func.fail("TODO: airAtomicRmw loop {s}", .{@tagName(op)}),
7224 }
7238 switch (op) {
7239 .Add, .Sub => |tag| {
7240 _ = try func.genBinOp(
7241 switch (tag) {
7242 .Add => .add,
7243 .Sub => .sub,
7244 else => unreachable,
7245 },
7246 .{ .register = result_reg },
7247 val_ty,
7248 .{ .register = val_register },
7249 val_ty,
7250 after_reg,
7251 );
7252 },
72257253
7226 _ = try func.addInst(.{
7227 .tag = .scw,
7228 .data = .{ .amo = .{
7229 .rd = after_reg,
7230 .rs1 = ptr_register,
7231 .rs2 = after_reg,
7232 .aq = if (aq) .aq else .none,
7233 .rl = if (rl) .rl else .none,
7234 } },
7235 });
7254 else => return func.fail("TODO: airAtomicRmw loop {s}", .{@tagName(op)}),
7255 }
72367256
7237 _ = try func.addInst(.{
7238 .tag = .bne,
7239 .data = .{ .b_type = .{
7240 .inst = jump_back,
7241 .rs1 = after_reg,
7242 .rs2 = .zero,
7243 } },
7244 });
7245 },
7246 }
7257 _ = try func.addInst(.{
7258 .tag = .scw,
7259 .data = .{ .amo = .{
7260 .rd = after_reg,
7261 .rs1 = ptr_register,
7262 .rs2 = after_reg,
7263 .aq = if (aq) .aq else .none,
7264 .rl = if (rl) .rl else .none,
7265 } },
7266 });
7267
7268 _ = try func.addInst(.{
7269 .tag = .bne,
7270 .data = .{ .b_type = .{
7271 .inst = jump_back,
7272 .rs1 = after_reg,
7273 .rs2 = .zero,
7274 } },
7275 });
7276 },
7277 }
7278 break :result result_mcv;
7279 };
72477280
7248 return func.finishAir(inst, result_mcv, .{ pl_op.operand, extra.operand, .none });
7281 return func.finishAir(inst, result, .{ pl_op.operand, extra.operand, .none });
72497282}
72507283
72517284fn airAtomicLoad(func: *Func, inst: Air.Inst.Index) !void {
src/arch/riscv64/abi.zig+1-1
......@@ -164,7 +164,7 @@ pub fn classifySystem(ty: Type, pt: Zcu.PerThread) [8]SystemClass {
164164
165165 return memory_class;
166166 },
167 .Struct => {
167 .Struct, .Union => {
168168 const layout = ty.containerLayout(pt.zcu);
169169 const ty_size = ty.abiSize(pt);
170170
src/arch/riscv64/encoding.zig+3-1
......@@ -206,6 +206,7 @@ pub const Lir = struct {
206206 .srai => .{ .opcode = .OP_IMM, .format = .I, .data = .{ .sh = .{ .typ = 0b010000, .funct3 = 0b101, .has_5 = true } } },
207207
208208 .clz => .{ .opcode = .OP_IMM, .format = .R, .data = .{ .ff = .{ .funct3 = 0b001, .funct7 = 0b0110000 } } },
209 .cpop => .{ .opcode = .OP_IMM, .format = .R, .data = .{ .ff = .{ .funct3 = 0b001, .funct7 = 0b0110000 } } },
209210
210211 // OP_IMM_32
211212
......@@ -213,7 +214,8 @@ pub const Lir = struct {
213214 .srliw => .{ .opcode = .OP_IMM_32, .format = .I, .data = .{ .sh = .{ .typ = 0b000000, .funct3 = 0b101, .has_5 = false } } },
214215 .sraiw => .{ .opcode = .OP_IMM_32, .format = .I, .data = .{ .sh = .{ .typ = 0b010000, .funct3 = 0b101, .has_5 = false } } },
215216
216 .clzw => .{ .opcode = .OP_IMM_32, .format = .R, .data = .{ .ff = .{ .funct3 = 0b001, .funct7 = 0b0110000 } } },
217 .clzw => .{ .opcode = .OP_IMM_32, .format = .R, .data = .{ .ff = .{ .funct3 = 0b001, .funct7 = 0b0110000 } } },
218 .cpopw => .{ .opcode = .OP_IMM_32, .format = .R, .data = .{ .ff = .{ .funct3 = 0b001, .funct7 = 0b0110000 } } },
217219
218220 // OP_32
219221
src/arch/riscv64/mnem.zig+2
......@@ -174,6 +174,8 @@ pub const Mnemonic = enum(u16) {
174174 // Zbb Extension Instructions
175175 clz,
176176 clzw,
177 cpop,
178 cpopw,
177179
178180 // A Extension Instructions
179181 fence,