authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2024-02-17 09:24:08+01:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2024-02-25 11:22:10+01:00
log4fcc750ba58f51606c49310bdd7c81c156d48cfc
tree600e9a131e78f5b997f4ceaef02bc386937bd295
parente5c439a16ddb34e3b59e3e6998aa7dc61f652d88

x86_64: implement more shuffles


5 files changed, 598 insertions(+), 54 deletions(-)

lib/std/crypto/blake3.zig+1-2
......@@ -200,8 +200,7 @@ const CompressGeneric = struct {
200200 }
201201};
202202
203const compress = if (builtin.cpu.arch == .x86_64 and
204 (builtin.zig_backend != .stage2_x86_64 or std.Target.x86.featureSetHas(builtin.cpu.features, .ssse3)))
203const compress = if (builtin.cpu.arch == .x86_64)
205204 CompressVectorized.compress
206205else
207206 CompressGeneric.compress;
lib/std/crypto/salsa20.zig+1-2
......@@ -302,8 +302,7 @@ fn SalsaNonVecImpl(comptime rounds: comptime_int) type {
302302 };
303303}
304304
305const SalsaImpl = if (builtin.cpu.arch == .x86_64 and
306 (builtin.zig_backend != .stage2_x86_64 or std.Target.x86.featureSetHas(builtin.cpu.features, .ssse3)))
305const SalsaImpl = if (builtin.cpu.arch == .x86_64)
307306 SalsaVecImpl
308307else
309308 SalsaNonVecImpl;
src/arch/x86_64/CodeGen.zig+592-49
......@@ -4799,7 +4799,7 @@ fn airShlShrBinOp(self: *Self, inst: Air.Inst.Index) !void {
47994799 switch (lhs_ty.zigTypeTag(mod)) {
48004800 .Int => {
48014801 try self.spillRegisters(&.{.rcx});
4802 try self.register_manager.getReg(.rcx, null);
4802 try self.register_manager.getKnownReg(.rcx, null);
48034803 const lhs_mcv = try self.resolveInst(bin_op.lhs);
48044804 const rhs_mcv = try self.resolveInst(bin_op.rhs);
48054805
......@@ -6825,7 +6825,7 @@ fn floatSign(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, ty: Type)
68256825
68266826 const dst_mcv: MCValue = .{ .register = .st0 };
68276827 if (!std.meta.eql(src_mcv, dst_mcv) or !self.reuseOperand(inst, operand, 0, src_mcv))
6828 try self.register_manager.getReg(.st0, inst);
6828 try self.register_manager.getKnownReg(.st0, inst);
68296829
68306830 try self.genCopy(ty, dst_mcv, src_mcv, .{});
68316831 switch (tag) {
......@@ -9059,7 +9059,7 @@ fn genShiftBinOp(
90599059 lhs_ty.fmt(mod),
90609060 });
90619061
9062 try self.register_manager.getReg(.rcx, null);
9062 try self.register_manager.getKnownReg(.rcx, null);
90639063 const rcx_lock = self.register_manager.lockReg(.rcx);
90649064 defer if (rcx_lock) |lock| self.register_manager.unlockReg(lock);
90659065
......@@ -9382,8 +9382,8 @@ fn genMulDivBinOp(
93829382 .rem => maybe_inst,
93839383 else => null,
93849384 };
9385 try self.register_manager.getReg(.rax, track_inst_rax);
9386 try self.register_manager.getReg(.rdx, track_inst_rdx);
9385 try self.register_manager.getKnownReg(.rax, track_inst_rax);
9386 try self.register_manager.getKnownReg(.rdx, track_inst_rdx);
93879387
93889388 try self.genIntMulDivOpMir(switch (signedness) {
93899389 .signed => switch (tag) {
......@@ -9423,8 +9423,11 @@ fn genMulDivBinOp(
94239423 },
94249424
94259425 .mod => {
9426 try self.register_manager.getReg(.rax, null);
9427 try self.register_manager.getReg(.rdx, if (signedness == .unsigned) maybe_inst else null);
9426 try self.register_manager.getKnownReg(.rax, null);
9427 try self.register_manager.getKnownReg(
9428 .rdx,
9429 if (signedness == .unsigned) maybe_inst else null,
9430 );
94289431
94299432 switch (signedness) {
94309433 .signed => {
......@@ -9465,8 +9468,11 @@ fn genMulDivBinOp(
94659468 },
94669469
94679470 .div_floor => {
9468 try self.register_manager.getReg(.rax, if (signedness == .unsigned) maybe_inst else null);
9469 try self.register_manager.getReg(.rdx, null);
9471 try self.register_manager.getKnownReg(
9472 .rax,
9473 if (signedness == .unsigned) maybe_inst else null,
9474 );
9475 try self.register_manager.getKnownReg(.rdx, null);
94709476
94719477 const lhs_lock: ?RegisterLock = switch (lhs_mcv) {
94729478 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
......@@ -9710,7 +9716,7 @@ fn genBinOp(
97109716 .rem, .mod => unreachable,
97119717 .max, .min => if (lhs_ty.scalarType(mod).isRuntimeFloat()) registerAlias(
97129718 if (!self.hasFeature(.avx) and self.hasFeature(.sse4_1)) mask: {
9713 try self.register_manager.getReg(.xmm0, null);
9719 try self.register_manager.getKnownReg(.xmm0, null);
97149720 break :mask .xmm0;
97159721 } else try self.register_manager.allocReg(null, abi.RegisterClass.sse),
97169722 abi_size,
......@@ -16769,7 +16775,7 @@ fn airSelect(self: *Self, inst: Air.Inst.Index) !void {
1676916775 .register => |pred_reg| switch (pred_reg.class()) {
1677016776 .general_purpose => {},
1677116777 .sse => if (need_xmm0 and pred_reg.id() != comptime Register.xmm0.id()) {
16772 try self.register_manager.getReg(.xmm0, null);
16778 try self.register_manager.getKnownReg(.xmm0, null);
1677316779 try self.genSetReg(.xmm0, pred_ty, pred_mcv, .{});
1677416780 break :mask .xmm0;
1677516781 } else break :mask if (has_blend)
......@@ -16781,7 +16787,7 @@ fn airSelect(self: *Self, inst: Air.Inst.Index) !void {
1678116787 else => {},
1678216788 }
1678316789 const mask_reg: Register = if (need_xmm0) mask_reg: {
16784 try self.register_manager.getReg(.xmm0, null);
16790 try self.register_manager.getKnownReg(.xmm0, null);
1678516791 break :mask_reg .xmm0;
1678616792 } else try self.register_manager.allocReg(null, abi.RegisterClass.sse);
1678716793 const mask_alias = registerAlias(mask_reg, abi_size);
......@@ -17093,7 +17099,7 @@ fn airShuffle(self: *Self, inst: Air.Inst.Index) !void {
1709317099
1709417100 const dst_ty = self.typeOfIndex(inst);
1709517101 const elem_ty = dst_ty.childType(mod);
17096 const elem_abi_size: u32 = @intCast(elem_ty.abiSize(mod));
17102 const elem_abi_size: u16 = @intCast(elem_ty.abiSize(mod));
1709717103 const dst_abi_size: u32 = @intCast(dst_ty.abiSize(mod));
1709817104 const lhs_ty = self.typeOf(extra.a);
1709917105 const lhs_abi_size: u32 = @intCast(lhs_ty.abiSize(mod));
......@@ -17109,8 +17115,8 @@ fn airShuffle(self: *Self, inst: Air.Inst.Index) !void {
1710917115 const mask_elems = try allocator.alloc(?i32, extra.mask_len);
1711017116 defer allocator.free(mask_elems);
1711117117 for (mask_elems, 0..) |*mask_elem, elem_index| {
17112 const mask_elem_val = Value
17113 .fromInterned(extra.mask).elemValue(mod, elem_index) catch unreachable;
17118 const mask_elem_val =
17119 Value.fromInterned(extra.mask).elemValue(mod, elem_index) catch unreachable;
1711417120 mask_elem.* = if (mask_elem_val.isUndef(mod))
1711517121 null
1711617122 else
......@@ -17124,7 +17130,7 @@ fn airShuffle(self: *Self, inst: Air.Inst.Index) !void {
1712417130 } else break :result try self.allocRegOrMem(inst, true);
1712517131
1712617132 for (mask_elems, 0..) |mask_elem, elem_index| {
17127 if (mask_elem orelse continue != @as(i32, @intCast(elem_index))) break;
17133 if (mask_elem orelse continue != elem_index) break;
1712817134 } else {
1712917135 const lhs_mcv = try self.resolveInst(extra.a);
1713017136 if (self.reuseOperand(inst, extra.a, 0, lhs_mcv)) break :result lhs_mcv;
......@@ -17134,7 +17140,7 @@ fn airShuffle(self: *Self, inst: Air.Inst.Index) !void {
1713417140 }
1713517141
1713617142 for (mask_elems, 0..) |mask_elem, elem_index| {
17137 if (mask_elem orelse continue != ~@as(i32, @intCast(elem_index))) break;
17143 if (~(mask_elem orelse continue) != elem_index) break;
1713817144 } else {
1713917145 const rhs_mcv = try self.resolveInst(extra.b);
1714017146 if (self.reuseOperand(inst, extra.b, 1, rhs_mcv)) break :result rhs_mcv;
......@@ -17143,6 +17149,219 @@ fn airShuffle(self: *Self, inst: Air.Inst.Index) !void {
1714317149 break :result dst_mcv;
1714417150 }
1714517151
17152 for ([_]Mir.Inst.Tag{ .unpckl, .unpckh }) |variant| unpck: {
17153 if (elem_abi_size > 8) break :unpck;
17154 if (dst_abi_size > @as(u32, if (if (elem_abi_size >= 4)
17155 has_avx
17156 else
17157 self.hasFeature(.avx2)) 32 else 16)) break :unpck;
17158
17159 var sources = [1]?u1{null} ** 2;
17160 for (mask_elems, 0..) |maybe_mask_elem, elem_index| {
17161 const mask_elem = maybe_mask_elem orelse continue;
17162 const mask_elem_index =
17163 math.cast(u5, if (mask_elem < 0) ~mask_elem else mask_elem) orelse break :unpck;
17164 const elem_byte = (elem_index >> 1) * elem_abi_size;
17165 if (mask_elem_index * elem_abi_size != (elem_byte & 0b0111) | @as(u4, switch (variant) {
17166 .unpckl => 0b0000,
17167 .unpckh => 0b1000,
17168 else => unreachable,
17169 }) | (elem_byte << 1 & 0b10000)) break :unpck;
17170
17171 const source = @intFromBool(mask_elem < 0);
17172 if (sources[elem_index & 0b00001]) |prev_source| {
17173 if (source != prev_source) break :unpck;
17174 } else sources[elem_index & 0b00001] = source;
17175 }
17176 if (sources[0] orelse break :unpck == sources[1] orelse break :unpck) break :unpck;
17177
17178 const operands = [2]Air.Inst.Ref{ extra.a, extra.b };
17179 const operand_tys = [2]Type{ lhs_ty, rhs_ty };
17180 const lhs_mcv = try self.resolveInst(operands[sources[0].?]);
17181 const rhs_mcv = try self.resolveInst(operands[sources[1].?]);
17182
17183 const dst_mcv: MCValue = if (lhs_mcv.isRegister() and
17184 self.reuseOperand(inst, operands[sources[0].?], sources[0].?, lhs_mcv))
17185 lhs_mcv
17186 else if (has_avx and lhs_mcv.isRegister())
17187 .{ .register = try self.register_manager.allocReg(inst, abi.RegisterClass.sse) }
17188 else
17189 try self.copyToRegisterWithInstTracking(inst, operand_tys[sources[0].?], lhs_mcv);
17190 const dst_reg = dst_mcv.getReg().?;
17191 const dst_alias = registerAlias(dst_reg, max_abi_size);
17192
17193 const mir_tag: Mir.Inst.FixedTag = if ((elem_abi_size >= 4 and elem_ty.isRuntimeFloat()) or
17194 (dst_abi_size > 16 and !self.hasFeature(.avx2))) .{ switch (elem_abi_size) {
17195 4 => if (has_avx) .v_ps else ._ps,
17196 8 => if (has_avx) .v_pd else ._pd,
17197 else => unreachable,
17198 }, variant } else .{ if (has_avx) .vp_ else .p_, switch (variant) {
17199 .unpckl => switch (elem_abi_size) {
17200 1 => .unpcklbw,
17201 2 => .unpcklwd,
17202 4 => .unpckldq,
17203 8 => .unpcklqdq,
17204 else => unreachable,
17205 },
17206 .unpckh => switch (elem_abi_size) {
17207 1 => .unpckhbw,
17208 2 => .unpckhwd,
17209 4 => .unpckhdq,
17210 8 => .unpckhqdq,
17211 else => unreachable,
17212 },
17213 else => unreachable,
17214 } };
17215 if (has_avx) if (rhs_mcv.isMemory()) try self.asmRegisterRegisterMemory(
17216 mir_tag,
17217 dst_alias,
17218 registerAlias(lhs_mcv.getReg() orelse dst_reg, max_abi_size),
17219 try rhs_mcv.mem(self, Memory.Size.fromSize(max_abi_size)),
17220 ) else try self.asmRegisterRegisterRegister(
17221 mir_tag,
17222 dst_alias,
17223 registerAlias(lhs_mcv.getReg() orelse dst_reg, max_abi_size),
17224 registerAlias(if (rhs_mcv.isRegister())
17225 rhs_mcv.getReg().?
17226 else
17227 try self.copyToTmpRegister(operand_tys[sources[1].?], rhs_mcv), max_abi_size),
17228 ) else if (rhs_mcv.isMemory()) try self.asmRegisterMemory(
17229 mir_tag,
17230 dst_alias,
17231 try rhs_mcv.mem(self, Memory.Size.fromSize(max_abi_size)),
17232 ) else try self.asmRegisterRegister(
17233 mir_tag,
17234 dst_alias,
17235 registerAlias(if (rhs_mcv.isRegister())
17236 rhs_mcv.getReg().?
17237 else
17238 try self.copyToTmpRegister(operand_tys[sources[1].?], rhs_mcv), max_abi_size),
17239 );
17240 break :result dst_mcv;
17241 }
17242
17243 pshufd: {
17244 if (elem_abi_size != 4) break :pshufd;
17245 if (max_abi_size > @as(u32, if (has_avx) 32 else 16)) break :pshufd;
17246
17247 var control: u8 = 0b00_00_00_00;
17248 var sources = [1]?u1{null} ** 1;
17249 for (mask_elems, 0..) |maybe_mask_elem, elem_index| {
17250 const mask_elem = maybe_mask_elem orelse continue;
17251 const mask_elem_index: u3 = @intCast(if (mask_elem < 0) ~mask_elem else mask_elem);
17252 if (mask_elem_index & 0b100 != elem_index & 0b100) break :pshufd;
17253
17254 const source = @intFromBool(mask_elem < 0);
17255 if (sources[0]) |prev_source| {
17256 if (source != prev_source) break :pshufd;
17257 } else sources[(elem_index & 0b010) >> 1] = source;
17258
17259 const select_bit: u3 = @intCast((elem_index & 0b011) << 1);
17260 const select = @as(u8, @intCast(mask_elem_index & 0b011)) << select_bit;
17261 if (elem_index & 0b100 == 0)
17262 control |= select
17263 else if (control & @as(u8, 0b11) << select_bit != select) break :pshufd;
17264 }
17265
17266 const operands = [2]Air.Inst.Ref{ extra.a, extra.b };
17267 const operand_tys = [2]Type{ lhs_ty, rhs_ty };
17268 const src_mcv = try self.resolveInst(operands[sources[0] orelse break :pshufd]);
17269
17270 const dst_reg = if (src_mcv.isRegister() and
17271 self.reuseOperand(inst, operands[sources[0].?], sources[0].?, src_mcv))
17272 src_mcv.getReg().?
17273 else
17274 try self.register_manager.allocReg(inst, abi.RegisterClass.sse);
17275 const dst_alias = registerAlias(dst_reg, max_abi_size);
17276
17277 if (src_mcv.isMemory()) try self.asmRegisterMemoryImmediate(
17278 .{ if (has_avx) .vp_d else .p_d, .shuf },
17279 dst_alias,
17280 try src_mcv.mem(self, Memory.Size.fromSize(max_abi_size)),
17281 Immediate.u(control),
17282 ) else try self.asmRegisterRegisterImmediate(
17283 .{ if (has_avx) .vp_d else .p_d, .shuf },
17284 dst_alias,
17285 registerAlias(if (src_mcv.isRegister())
17286 src_mcv.getReg().?
17287 else
17288 try self.copyToTmpRegister(operand_tys[sources[0].?], src_mcv), max_abi_size),
17289 Immediate.u(control),
17290 );
17291 break :result .{ .register = dst_reg };
17292 }
17293
17294 shufps: {
17295 if (elem_abi_size != 4) break :shufps;
17296 if (max_abi_size > @as(u32, if (has_avx) 32 else 16)) break :shufps;
17297
17298 var control: u8 = 0b00_00_00_00;
17299 var sources = [1]?u1{null} ** 2;
17300 for (mask_elems, 0..) |maybe_mask_elem, elem_index| {
17301 const mask_elem = maybe_mask_elem orelse continue;
17302 const mask_elem_index: u3 = @intCast(if (mask_elem < 0) ~mask_elem else mask_elem);
17303 if (mask_elem_index & 0b100 != elem_index & 0b100) break :shufps;
17304
17305 const source = @intFromBool(mask_elem < 0);
17306 if (sources[(elem_index & 0b010) >> 1]) |prev_source| {
17307 if (source != prev_source) break :shufps;
17308 } else sources[(elem_index & 0b010) >> 1] = source;
17309
17310 const select_bit: u3 = @intCast((elem_index & 0b011) << 1);
17311 const select = @as(u8, @intCast(mask_elem_index & 0b011)) << select_bit;
17312 if (elem_index & 0b100 == 0)
17313 control |= select
17314 else if (control & @as(u8, 0b11) << select_bit != select) break :shufps;
17315 }
17316 if (sources[0] orelse break :shufps == sources[1] orelse break :shufps) break :shufps;
17317
17318 const operands = [2]Air.Inst.Ref{ extra.a, extra.b };
17319 const operand_tys = [2]Type{ lhs_ty, rhs_ty };
17320 const lhs_mcv = try self.resolveInst(operands[sources[0].?]);
17321 const rhs_mcv = try self.resolveInst(operands[sources[1].?]);
17322
17323 const dst_mcv: MCValue = if (lhs_mcv.isRegister() and
17324 self.reuseOperand(inst, operands[sources[0].?], sources[0].?, lhs_mcv))
17325 lhs_mcv
17326 else if (has_avx and lhs_mcv.isRegister())
17327 .{ .register = try self.register_manager.allocReg(inst, abi.RegisterClass.sse) }
17328 else
17329 try self.copyToRegisterWithInstTracking(inst, operand_tys[sources[0].?], lhs_mcv);
17330 const dst_reg = dst_mcv.getReg().?;
17331 const dst_alias = registerAlias(dst_reg, max_abi_size);
17332
17333 if (has_avx) if (rhs_mcv.isMemory()) try self.asmRegisterRegisterMemoryImmediate(
17334 .{ .v_ps, .shuf },
17335 dst_alias,
17336 registerAlias(lhs_mcv.getReg() orelse dst_reg, max_abi_size),
17337 try rhs_mcv.mem(self, Memory.Size.fromSize(max_abi_size)),
17338 Immediate.u(control),
17339 ) else try self.asmRegisterRegisterRegisterImmediate(
17340 .{ .v_ps, .shuf },
17341 dst_alias,
17342 registerAlias(lhs_mcv.getReg() orelse dst_reg, max_abi_size),
17343 registerAlias(if (rhs_mcv.isRegister())
17344 rhs_mcv.getReg().?
17345 else
17346 try self.copyToTmpRegister(operand_tys[sources[1].?], rhs_mcv), max_abi_size),
17347 Immediate.u(control),
17348 ) else if (rhs_mcv.isMemory()) try self.asmRegisterMemoryImmediate(
17349 .{ ._ps, .shuf },
17350 dst_alias,
17351 try rhs_mcv.mem(self, Memory.Size.fromSize(max_abi_size)),
17352 Immediate.u(control),
17353 ) else try self.asmRegisterRegisterImmediate(
17354 .{ ._ps, .shuf },
17355 dst_alias,
17356 registerAlias(if (rhs_mcv.isRegister())
17357 rhs_mcv.getReg().?
17358 else
17359 try self.copyToTmpRegister(operand_tys[sources[1].?], rhs_mcv), max_abi_size),
17360 Immediate.u(control),
17361 );
17362 break :result dst_mcv;
17363 }
17364
1714617365 shufpd: {
1714717366 if (elem_abi_size != 8) break :shufpd;
1714817367 if (max_abi_size > @as(u32, if (has_avx) 32 else 16)) break :shufpd;
......@@ -17163,8 +17382,8 @@ fn airShuffle(self: *Self, inst: Air.Inst.Index) !void {
1716317382 }
1716417383 if (sources[0] orelse break :shufpd == sources[1] orelse break :shufpd) break :shufpd;
1716517384
17166 const operands = [2]Air.Inst.Ref{ extra.a, extra.b };
17167 const operand_tys = [2]Type{ lhs_ty, rhs_ty };
17385 const operands: [2]Air.Inst.Ref = .{ extra.a, extra.b };
17386 const operand_tys: [2]Type = .{ lhs_ty, rhs_ty };
1716817387 const lhs_mcv = try self.resolveInst(operands[sources[0].?]);
1716917388 const rhs_mcv = try self.resolveInst(operands[sources[1].?]);
1717017389
......@@ -17210,6 +17429,355 @@ fn airShuffle(self: *Self, inst: Air.Inst.Index) !void {
1721017429 break :result dst_mcv;
1721117430 }
1721217431
17432 blend: {
17433 if (elem_abi_size < 2) break :blend;
17434 if (dst_abi_size > @as(u32, if (has_avx) 32 else 16)) break :blend;
17435 if (!self.hasFeature(.sse4_1)) break :blend;
17436
17437 var control: u8 = 0b0_0_0_0_0_0_0_0;
17438 for (mask_elems, 0..) |maybe_mask_elem, elem_index| {
17439 const mask_elem = maybe_mask_elem orelse continue;
17440 const mask_elem_index =
17441 math.cast(u4, if (mask_elem < 0) ~mask_elem else mask_elem) orelse break :blend;
17442 if (mask_elem_index != elem_index) break :blend;
17443
17444 const select = @as(u8, @intFromBool(mask_elem < 0)) << @truncate(elem_index);
17445 if (elem_index & 0b1000 == 0)
17446 control |= select
17447 else if (control & @as(u8, 0b1) << @truncate(elem_index) != select) break :blend;
17448 }
17449
17450 if (!elem_ty.isRuntimeFloat() and self.hasFeature(.avx2)) vpblendd: {
17451 const expanded_control = switch (elem_abi_size) {
17452 4 => control,
17453 8 => @as(u8, if (control & 0b0001 != 0) 0b00_00_00_11 else 0b00_00_00_00) |
17454 @as(u8, if (control & 0b0010 != 0) 0b00_00_11_00 else 0b00_00_00_00) |
17455 @as(u8, if (control & 0b0100 != 0) 0b00_11_00_00 else 0b00_00_00_00) |
17456 @as(u8, if (control & 0b1000 != 0) 0b11_00_00_00 else 0b00_00_00_00),
17457 else => break :vpblendd,
17458 };
17459
17460 const lhs_mcv = try self.resolveInst(extra.a);
17461 const lhs_reg = if (lhs_mcv.isRegister())
17462 lhs_mcv.getReg().?
17463 else
17464 try self.copyToTmpRegister(dst_ty, lhs_mcv);
17465 const lhs_lock = self.register_manager.lockReg(lhs_reg);
17466 defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock);
17467
17468 const rhs_mcv = try self.resolveInst(extra.b);
17469 const dst_reg = try self.register_manager.allocReg(inst, abi.RegisterClass.sse);
17470 if (rhs_mcv.isMemory()) try self.asmRegisterRegisterMemoryImmediate(
17471 .{ .vp_d, .blend },
17472 registerAlias(dst_reg, dst_abi_size),
17473 registerAlias(lhs_reg, dst_abi_size),
17474 try rhs_mcv.mem(self, Memory.Size.fromSize(dst_abi_size)),
17475 Immediate.u(expanded_control),
17476 ) else try self.asmRegisterRegisterRegisterImmediate(
17477 .{ .vp_d, .blend },
17478 registerAlias(dst_reg, dst_abi_size),
17479 registerAlias(lhs_reg, dst_abi_size),
17480 registerAlias(if (rhs_mcv.isRegister())
17481 rhs_mcv.getReg().?
17482 else
17483 try self.copyToTmpRegister(dst_ty, rhs_mcv), dst_abi_size),
17484 Immediate.u(expanded_control),
17485 );
17486 break :result .{ .register = dst_reg };
17487 }
17488
17489 if (!elem_ty.isRuntimeFloat() or elem_abi_size == 2) pblendw: {
17490 const expanded_control = switch (elem_abi_size) {
17491 2 => control,
17492 4 => if (dst_abi_size <= 16 or
17493 @as(u4, @intCast(control >> 4)) == @as(u4, @truncate(control >> 0)))
17494 @as(u8, if (control & 0b0001 != 0) 0b00_00_00_11 else 0b00_00_00_00) |
17495 @as(u8, if (control & 0b0010 != 0) 0b00_00_11_00 else 0b00_00_00_00) |
17496 @as(u8, if (control & 0b0100 != 0) 0b00_11_00_00 else 0b00_00_00_00) |
17497 @as(u8, if (control & 0b1000 != 0) 0b11_00_00_00 else 0b00_00_00_00)
17498 else
17499 break :pblendw,
17500 8 => if (dst_abi_size <= 16 or
17501 @as(u2, @intCast(control >> 2)) == @as(u2, @truncate(control >> 0)))
17502 @as(u8, if (control & 0b01 != 0) 0b0000_1111 else 0b0000_0000) |
17503 @as(u8, if (control & 0b10 != 0) 0b1111_0000 else 0b0000_0000)
17504 else
17505 break :pblendw,
17506 16 => break :pblendw,
17507 else => unreachable,
17508 };
17509
17510 const lhs_mcv = try self.resolveInst(extra.a);
17511 const rhs_mcv = try self.resolveInst(extra.b);
17512
17513 const dst_mcv: MCValue = if (lhs_mcv.isRegister() and
17514 self.reuseOperand(inst, extra.a, 0, lhs_mcv))
17515 lhs_mcv
17516 else if (has_avx and lhs_mcv.isRegister())
17517 .{ .register = try self.register_manager.allocReg(inst, abi.RegisterClass.sse) }
17518 else
17519 try self.copyToRegisterWithInstTracking(inst, dst_ty, lhs_mcv);
17520 const dst_reg = dst_mcv.getReg().?;
17521
17522 if (has_avx) if (rhs_mcv.isMemory()) try self.asmRegisterRegisterMemoryImmediate(
17523 .{ .vp_w, .blend },
17524 registerAlias(dst_reg, dst_abi_size),
17525 registerAlias(if (lhs_mcv.isRegister())
17526 lhs_mcv.getReg().?
17527 else
17528 dst_reg, dst_abi_size),
17529 try rhs_mcv.mem(self, Memory.Size.fromSize(dst_abi_size)),
17530 Immediate.u(expanded_control),
17531 ) else try self.asmRegisterRegisterRegisterImmediate(
17532 .{ .vp_w, .blend },
17533 registerAlias(dst_reg, dst_abi_size),
17534 registerAlias(if (lhs_mcv.isRegister())
17535 lhs_mcv.getReg().?
17536 else
17537 dst_reg, dst_abi_size),
17538 registerAlias(if (rhs_mcv.isRegister())
17539 rhs_mcv.getReg().?
17540 else
17541 try self.copyToTmpRegister(dst_ty, rhs_mcv), dst_abi_size),
17542 Immediate.u(expanded_control),
17543 ) else if (rhs_mcv.isMemory()) try self.asmRegisterMemoryImmediate(
17544 .{ .p_w, .blend },
17545 registerAlias(dst_reg, dst_abi_size),
17546 try rhs_mcv.mem(self, Memory.Size.fromSize(dst_abi_size)),
17547 Immediate.u(expanded_control),
17548 ) else try self.asmRegisterRegisterImmediate(
17549 .{ .p_w, .blend },
17550 registerAlias(dst_reg, dst_abi_size),
17551 registerAlias(if (rhs_mcv.isRegister())
17552 rhs_mcv.getReg().?
17553 else
17554 try self.copyToTmpRegister(dst_ty, rhs_mcv), dst_abi_size),
17555 Immediate.u(expanded_control),
17556 );
17557 break :result .{ .register = dst_reg };
17558 }
17559
17560 const expanded_control = switch (elem_abi_size) {
17561 4, 8 => control,
17562 16 => @as(u4, if (control & 0b01 != 0) 0b00_11 else 0b00_00) |
17563 @as(u4, if (control & 0b10 != 0) 0b11_00 else 0b00_00),
17564 else => unreachable,
17565 };
17566
17567 const lhs_mcv = try self.resolveInst(extra.a);
17568 const rhs_mcv = try self.resolveInst(extra.b);
17569
17570 const dst_mcv: MCValue = if (lhs_mcv.isRegister() and
17571 self.reuseOperand(inst, extra.a, 0, lhs_mcv))
17572 lhs_mcv
17573 else if (has_avx and lhs_mcv.isRegister())
17574 .{ .register = try self.register_manager.allocReg(inst, abi.RegisterClass.sse) }
17575 else
17576 try self.copyToRegisterWithInstTracking(inst, dst_ty, lhs_mcv);
17577 const dst_reg = dst_mcv.getReg().?;
17578
17579 if (has_avx) if (rhs_mcv.isMemory()) try self.asmRegisterRegisterMemoryImmediate(
17580 switch (elem_abi_size) {
17581 4 => .{ .v_ps, .blend },
17582 8, 16 => .{ .v_pd, .blend },
17583 else => unreachable,
17584 },
17585 registerAlias(dst_reg, dst_abi_size),
17586 registerAlias(if (lhs_mcv.isRegister())
17587 lhs_mcv.getReg().?
17588 else
17589 dst_reg, dst_abi_size),
17590 try rhs_mcv.mem(self, Memory.Size.fromSize(dst_abi_size)),
17591 Immediate.u(expanded_control),
17592 ) else try self.asmRegisterRegisterRegisterImmediate(
17593 switch (elem_abi_size) {
17594 4 => .{ .v_ps, .blend },
17595 8, 16 => .{ .v_pd, .blend },
17596 else => unreachable,
17597 },
17598 registerAlias(dst_reg, dst_abi_size),
17599 registerAlias(if (lhs_mcv.isRegister())
17600 lhs_mcv.getReg().?
17601 else
17602 dst_reg, dst_abi_size),
17603 registerAlias(if (rhs_mcv.isRegister())
17604 rhs_mcv.getReg().?
17605 else
17606 try self.copyToTmpRegister(dst_ty, rhs_mcv), dst_abi_size),
17607 Immediate.u(expanded_control),
17608 ) else if (rhs_mcv.isMemory()) try self.asmRegisterMemoryImmediate(
17609 switch (elem_abi_size) {
17610 4 => .{ ._ps, .blend },
17611 8, 16 => .{ ._pd, .blend },
17612 else => unreachable,
17613 },
17614 registerAlias(dst_reg, dst_abi_size),
17615 try rhs_mcv.mem(self, Memory.Size.fromSize(dst_abi_size)),
17616 Immediate.u(expanded_control),
17617 ) else try self.asmRegisterRegisterImmediate(
17618 switch (elem_abi_size) {
17619 4 => .{ ._ps, .blend },
17620 8, 16 => .{ ._pd, .blend },
17621 else => unreachable,
17622 },
17623 registerAlias(dst_reg, dst_abi_size),
17624 registerAlias(if (rhs_mcv.isRegister())
17625 rhs_mcv.getReg().?
17626 else
17627 try self.copyToTmpRegister(dst_ty, rhs_mcv), dst_abi_size),
17628 Immediate.u(expanded_control),
17629 );
17630 break :result .{ .register = dst_reg };
17631 }
17632
17633 blendv: {
17634 if (dst_abi_size > @as(u32, if (if (elem_abi_size >= 4)
17635 has_avx
17636 else
17637 self.hasFeature(.avx2)) 32 else 16)) break :blendv;
17638
17639 const select_mask_elem_ty = try mod.intType(.unsigned, elem_abi_size * 8);
17640 const select_mask_ty = try mod.vectorType(.{
17641 .len = @intCast(mask_elems.len),
17642 .child = select_mask_elem_ty.toIntern(),
17643 });
17644 var select_mask_elems: [32]InternPool.Index = undefined;
17645 for (
17646 select_mask_elems[0..mask_elems.len],
17647 mask_elems,
17648 0..,
17649 ) |*select_mask_elem, maybe_mask_elem, elem_index| {
17650 const mask_elem = maybe_mask_elem orelse continue;
17651 const mask_elem_index =
17652 math.cast(u5, if (mask_elem < 0) ~mask_elem else mask_elem) orelse break :blendv;
17653 if (mask_elem_index != elem_index) break :blendv;
17654
17655 select_mask_elem.* = (if (mask_elem < 0)
17656 try select_mask_elem_ty.maxIntScalar(mod, select_mask_elem_ty)
17657 else
17658 try select_mask_elem_ty.minIntScalar(mod, select_mask_elem_ty)).toIntern();
17659 }
17660 const select_mask_mcv = try self.genTypedValue(.{
17661 .ty = select_mask_ty,
17662 .val = Value.fromInterned(try mod.intern(.{ .aggregate = .{
17663 .ty = select_mask_ty.toIntern(),
17664 .storage = .{ .elems = select_mask_elems[0..mask_elems.len] },
17665 } })),
17666 });
17667
17668 if (self.hasFeature(.sse4_1)) {
17669 const mir_tag: Mir.Inst.FixedTag = .{
17670 if ((elem_abi_size >= 4 and elem_ty.isRuntimeFloat()) or
17671 (dst_abi_size > 16 and !self.hasFeature(.avx2))) switch (elem_abi_size) {
17672 4 => if (has_avx) .v_ps else ._ps,
17673 8 => if (has_avx) .v_pd else ._pd,
17674 else => unreachable,
17675 } else if (has_avx) .vp_b else .p_b,
17676 .blendv,
17677 };
17678
17679 const select_mask_reg = if (!has_avx) reg: {
17680 try self.register_manager.getKnownReg(.xmm0, null);
17681 try self.genSetReg(.xmm0, select_mask_elem_ty, select_mask_mcv, .{});
17682 break :reg .xmm0;
17683 } else try self.copyToTmpRegister(select_mask_ty, select_mask_mcv);
17684 const select_mask_alias = registerAlias(select_mask_reg, dst_abi_size);
17685 const select_mask_lock = self.register_manager.lockRegAssumeUnused(select_mask_reg);
17686 defer self.register_manager.unlockReg(select_mask_lock);
17687
17688 const lhs_mcv = try self.resolveInst(extra.a);
17689 const rhs_mcv = try self.resolveInst(extra.b);
17690
17691 const dst_mcv: MCValue = if (lhs_mcv.isRegister() and
17692 self.reuseOperand(inst, extra.a, 0, lhs_mcv))
17693 lhs_mcv
17694 else if (has_avx and lhs_mcv.isRegister())
17695 .{ .register = try self.register_manager.allocReg(inst, abi.RegisterClass.sse) }
17696 else
17697 try self.copyToRegisterWithInstTracking(inst, dst_ty, lhs_mcv);
17698 const dst_reg = dst_mcv.getReg().?;
17699 const dst_alias = registerAlias(dst_reg, dst_abi_size);
17700
17701 if (has_avx) if (rhs_mcv.isMemory()) try self.asmRegisterRegisterMemoryRegister(
17702 mir_tag,
17703 dst_alias,
17704 if (lhs_mcv.isRegister())
17705 registerAlias(lhs_mcv.getReg().?, dst_abi_size)
17706 else
17707 dst_alias,
17708 try rhs_mcv.mem(self, Memory.Size.fromSize(dst_abi_size)),
17709 select_mask_alias,
17710 ) else try self.asmRegisterRegisterRegisterRegister(
17711 mir_tag,
17712 dst_alias,
17713 if (lhs_mcv.isRegister())
17714 registerAlias(lhs_mcv.getReg().?, dst_abi_size)
17715 else
17716 dst_alias,
17717 registerAlias(if (rhs_mcv.isRegister())
17718 rhs_mcv.getReg().?
17719 else
17720 try self.copyToTmpRegister(dst_ty, rhs_mcv), dst_abi_size),
17721 select_mask_alias,
17722 ) else if (rhs_mcv.isMemory()) try self.asmRegisterMemoryRegister(
17723 mir_tag,
17724 dst_alias,
17725 try rhs_mcv.mem(self, Memory.Size.fromSize(dst_abi_size)),
17726 select_mask_alias,
17727 ) else try self.asmRegisterRegisterRegister(
17728 mir_tag,
17729 dst_alias,
17730 registerAlias(if (rhs_mcv.isRegister())
17731 rhs_mcv.getReg().?
17732 else
17733 try self.copyToTmpRegister(dst_ty, rhs_mcv), dst_abi_size),
17734 select_mask_alias,
17735 );
17736 break :result dst_mcv;
17737 }
17738
17739 const lhs_mcv = try self.resolveInst(extra.a);
17740 const rhs_mcv = try self.resolveInst(extra.b);
17741
17742 const dst_mcv: MCValue = if (rhs_mcv.isRegister() and
17743 self.reuseOperand(inst, extra.b, 1, rhs_mcv))
17744 rhs_mcv
17745 else
17746 try self.copyToRegisterWithInstTracking(inst, dst_ty, rhs_mcv);
17747 const dst_reg = dst_mcv.getReg().?;
17748 const dst_alias = registerAlias(dst_reg, dst_abi_size);
17749
17750 const mask_reg = try self.copyToTmpRegister(select_mask_ty, select_mask_mcv);
17751 const mask_alias = registerAlias(mask_reg, dst_abi_size);
17752 const mask_lock = self.register_manager.lockRegAssumeUnused(mask_reg);
17753 defer self.register_manager.unlockReg(mask_lock);
17754
17755 const mir_fixes: Mir.Inst.Fixes = if (elem_ty.isRuntimeFloat())
17756 switch (elem_ty.floatBits(self.target.*)) {
17757 16, 80, 128 => .p_,
17758 32 => ._ps,
17759 64 => ._pd,
17760 else => unreachable,
17761 }
17762 else
17763 .p_;
17764 try self.asmRegisterRegister(.{ mir_fixes, .@"and" }, dst_alias, mask_alias);
17765 if (lhs_mcv.isMemory()) try self.asmRegisterMemory(
17766 .{ mir_fixes, .andn },
17767 mask_alias,
17768 try lhs_mcv.mem(self, Memory.Size.fromSize(dst_abi_size)),
17769 ) else try self.asmRegisterRegister(
17770 .{ mir_fixes, .andn },
17771 mask_alias,
17772 if (lhs_mcv.isRegister())
17773 lhs_mcv.getReg().?
17774 else
17775 try self.copyToTmpRegister(dst_ty, lhs_mcv),
17776 );
17777 try self.asmRegisterRegister(.{ mir_fixes, .@"or" }, dst_alias, mask_alias);
17778 break :result dst_mcv;
17779 }
17780
1721317781 pshufb: {
1721417782 if (max_abi_size > 16) break :pshufb;
1721517783 if (!self.hasFeature(.ssse3)) break :pshufb;
......@@ -17330,38 +17898,13 @@ fn airShuffle(self: *Self, inst: Air.Inst.Index) !void {
1733017898 break :result .{ .register = temp_regs[0] };
1733117899 }
1733217900
17333 if (max_abi_size <= 16) {
17334 const lhs_mcv = try self.resolveInst(extra.a);
17335 const lhs_reg = if (lhs_mcv.isRegister())
17336 lhs_mcv.getReg().?
17337 else
17338 try self.copyToTmpRegister(lhs_ty, lhs_mcv);
17339 const lhs_lock = self.register_manager.lockRegAssumeUnused(lhs_reg);
17340 defer self.register_manager.unlockReg(lhs_lock);
17341
17342 const rhs_mcv = try self.resolveInst(extra.b);
17343 const rhs_reg = if (rhs_mcv.isRegister())
17344 rhs_mcv.getReg().?
17345 else
17346 try self.copyToTmpRegister(rhs_ty, rhs_mcv);
17347 const rhs_lock = self.register_manager.lockReg(rhs_reg);
17348 defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock);
17349
17350 //const dst_mcv = try self.register_manager.allocReg(inst, abi.RegisterClass.sse);
17351 switch (elem_ty.zigTypeTag(mod)) {
17352 .Float => switch (elem_ty.floatBits(self.target.*)) {
17353 16, 32 => {},
17354 64 => unreachable, // fully handled by shufpd
17355 80, 128 => unreachable, // all possible masks already handled
17356 else => unreachable,
17357 },
17358 else => {},
17359 }
17360 }
17361
1736217901 break :result null;
17363 }) orelse return self.fail("TODO implement airShuffle from {} and {} to {}", .{
17902 }) orelse return self.fail("TODO implement airShuffle from {} and {} to {} with {}", .{
1736417903 lhs_ty.fmt(mod), rhs_ty.fmt(mod), dst_ty.fmt(mod),
17904 Value.fromInterned(extra.mask).fmtValue(
17905 Type.fromInterned(mod.intern_pool.typeOf(extra.mask)),
17906 mod,
17907 ),
1736517908 });
1736617909 return self.finishAir(inst, result, .{ extra.a, extra.b, .none });
1736717910}
src/arch/x86_64/Encoding.zig+1-1
......@@ -416,7 +416,7 @@ pub const Mnemonic = enum {
416416 vfmadd132ss, vfmadd213ss, vfmadd231ss,
417417 // AVX2
418418 vbroadcasti128, vpbroadcastb, vpbroadcastd, vpbroadcastq, vpbroadcastw,
419 vextracti128, vinserti128,
419 vextracti128, vinserti128, vpblendd,
420420 // zig fmt: on
421421};
422422
src/arch/x86_64/encodings.zig+3
......@@ -1801,6 +1801,9 @@ pub const table = [_]Entry{
18011801
18021802 .{ .vpandn, .rvm, &.{ .ymm, .ymm, .ymm_m256 }, &.{ 0x66, 0x0f, 0xdf }, 0, .vex_256_wig, .avx2 },
18031803
1804 .{ .vpblendd, .rvmi, &.{ .xmm, .xmm, .xmm_m128, .imm8 }, &.{ 0x66, 0x0f, 0x3a, 0x02 }, 0, .vex_128_w0, .avx2 },
1805 .{ .vpblendd, .rvmi, &.{ .ymm, .ymm, .ymm_m256, .imm8 }, &.{ 0x66, 0x0f, 0x3a, 0x02 }, 0, .vex_256_w0, .avx2 },
1806
18041807 .{ .vpblendvb, .rvmr, &.{ .ymm, .ymm, .ymm_m256, .ymm }, &.{ 0x66, 0x0f, 0x3a, 0x4c }, 0, .vex_256_w0, .avx2 },
18051808
18061809 .{ .vpblendw, .rvmi, &.{ .ymm, .ymm, .ymm_m256, .imm8 }, &.{ 0x66, 0x0f, 0x3a, 0x0e }, 0, .vex_256_wig, .avx2 },