authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2023-05-15 20:17:06-04:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2023-05-18 20:42:38-04:00
log403c2d91bed456085eb685a9f89996c4635ce4b9
tree0c3f60b5c7152dff3367f29f970845b88c210c03
parentb9d2e0e308794463db1b1acf04d76778c470a070

x86_64: fix float min/max behavior


7 files changed, 393 insertions(+), 40 deletions(-)

src/arch/x86_64/CodeGen.zig+292-10
......@@ -1271,6 +1271,27 @@ fn asmRegisterRegisterRegister(
12711271 });
12721272}
12731273
1274fn asmRegisterRegisterRegisterRegister(
1275 self: *Self,
1276 tag: Mir.Inst.FixedTag,
1277 reg1: Register,
1278 reg2: Register,
1279 reg3: Register,
1280 reg4: Register,
1281) !void {
1282 _ = try self.addInst(.{
1283 .tag = tag[1],
1284 .ops = .rrrr,
1285 .data = .{ .rrrr = .{
1286 .fixes = tag[0],
1287 .r1 = reg1,
1288 .r2 = reg2,
1289 .r3 = reg3,
1290 .r4 = reg4,
1291 } },
1292 });
1293}
1294
12741295fn asmRegisterRegisterRegisterImmediate(
12751296 self: *Self,
12761297 tag: Mir.Inst.FixedTag,
......@@ -6224,12 +6245,26 @@ fn genBinOp(
62246245 lhs_air: Air.Inst.Ref,
62256246 rhs_air: Air.Inst.Ref,
62266247) !MCValue {
6227 const lhs_mcv = try self.resolveInst(lhs_air);
6228 const rhs_mcv = try self.resolveInst(rhs_air);
62296248 const lhs_ty = self.air.typeOf(lhs_air);
62306249 const rhs_ty = self.air.typeOf(rhs_air);
62316250 const abi_size = @intCast(u32, lhs_ty.abiSize(self.target.*));
62326251
6252 const maybe_mask_reg = switch (air_tag) {
6253 else => null,
6254 .max, .min => if (lhs_ty.scalarType().isRuntimeFloat()) registerAlias(
6255 if (!self.hasFeature(.avx) and self.hasFeature(.sse4_1)) mask: {
6256 try self.register_manager.getReg(.xmm0, null);
6257 break :mask .xmm0;
6258 } else try self.register_manager.allocReg(null, sse),
6259 abi_size,
6260 ) else null,
6261 };
6262 const mask_lock =
6263 if (maybe_mask_reg) |mask_reg| self.register_manager.lockRegAssumeUnused(mask_reg) else null;
6264 defer if (mask_lock) |lock| self.register_manager.unlockReg(lock);
6265
6266 const lhs_mcv = try self.resolveInst(lhs_air);
6267 const rhs_mcv = try self.resolveInst(rhs_air);
62336268 switch (lhs_mcv) {
62346269 .immediate => |imm| switch (imm) {
62356270 0 => switch (air_tag) {
......@@ -6300,7 +6335,16 @@ fn genBinOp(
63006335 };
63016336 defer if (dst_lock) |lock| self.register_manager.unlockReg(lock);
63026337
6303 const src_mcv = if (flipped) lhs_mcv else rhs_mcv;
6338 const unmat_src_mcv = if (flipped) lhs_mcv else rhs_mcv;
6339 const src_mcv: MCValue = if (maybe_mask_reg) |mask_reg|
6340 if (self.hasFeature(.avx) and unmat_src_mcv.isRegister() and maybe_inst != null and
6341 self.liveness.operandDies(maybe_inst.?, if (flipped) 0 else 1)) unmat_src_mcv else src: {
6342 try self.genSetReg(mask_reg, rhs_ty, unmat_src_mcv);
6343 break :src .{ .register = mask_reg };
6344 }
6345 else
6346 unmat_src_mcv;
6347
63046348 if (!vec_op) {
63056349 switch (air_tag) {
63066350 .add,
......@@ -7009,18 +7053,26 @@ fn genBinOp(
70097053 })) |tag| tag else return self.fail("TODO implement genBinOp for {s} {}", .{
70107054 @tagName(air_tag), lhs_ty.fmt(self.bin_file.options.module.?),
70117055 });
7056
7057 const lhs_copy_reg = if (maybe_mask_reg) |_| registerAlias(
7058 if (copied_to_dst) try self.copyToTmpRegister(lhs_ty, dst_mcv) else lhs_mcv.getReg().?,
7059 abi_size,
7060 ) else null;
7061 const lhs_copy_lock = if (lhs_copy_reg) |reg| self.register_manager.lockReg(reg) else null;
7062 defer if (lhs_copy_lock) |lock| self.register_manager.unlockReg(lock);
7063
70127064 if (self.hasFeature(.avx)) {
7013 const src1_alias =
7065 const lhs_reg =
70147066 if (copied_to_dst) dst_reg else registerAlias(lhs_mcv.getReg().?, abi_size);
70157067 if (src_mcv.isMemory()) try self.asmRegisterRegisterMemory(
70167068 mir_tag,
70177069 dst_reg,
7018 src1_alias,
7070 lhs_reg,
70197071 src_mcv.mem(Memory.PtrSize.fromSize(abi_size)),
70207072 ) else try self.asmRegisterRegisterRegister(
70217073 mir_tag,
70227074 dst_reg,
7023 src1_alias,
7075 lhs_reg,
70247076 registerAlias(if (src_mcv.isRegister())
70257077 src_mcv.getReg().?
70267078 else
......@@ -7041,9 +7093,10 @@ fn genBinOp(
70417093 try self.copyToTmpRegister(rhs_ty, src_mcv), abi_size),
70427094 );
70437095 }
7096
70447097 switch (air_tag) {
70457098 .add, .addwrap, .sub, .subwrap, .mul, .mulwrap, .div_float, .div_exact => {},
7046 .div_trunc, .div_floor => try self.genRound(
7099 .div_trunc, .div_floor => if (self.hasFeature(.sse4_1)) try self.genRound(
70477100 lhs_ty,
70487101 dst_reg,
70497102 .{ .register = dst_reg },
......@@ -7052,11 +7105,240 @@ fn genBinOp(
70527105 .div_floor => 0b1_0_01,
70537106 else => unreachable,
70547107 },
7055 ),
7108 ) else return self.fail("TODO implement genBinOp for {s} {} without sse4_1 feature", .{
7109 @tagName(air_tag), lhs_ty.fmt(self.bin_file.options.module.?),
7110 }),
70567111 .bit_and, .bit_or, .xor => {},
7057 .max, .min => {}, // TODO: unordered select
7112 .max, .min => if (maybe_mask_reg) |mask_reg| if (self.hasFeature(.avx)) {
7113 const rhs_copy_reg = registerAlias(src_mcv.getReg().?, abi_size);
7114
7115 try self.asmRegisterRegisterRegisterImmediate(
7116 if (@as(?Mir.Inst.FixedTag, switch (lhs_ty.zigTypeTag()) {
7117 .Float => switch (lhs_ty.floatBits(self.target.*)) {
7118 32 => .{ .v_ss, .cmp },
7119 64 => .{ .v_sd, .cmp },
7120 16, 80, 128 => null,
7121 else => unreachable,
7122 },
7123 .Vector => switch (lhs_ty.childType().zigTypeTag()) {
7124 .Float => switch (lhs_ty.childType().floatBits(self.target.*)) {
7125 32 => switch (lhs_ty.vectorLen()) {
7126 1 => .{ .v_ss, .cmp },
7127 2...8 => .{ .v_ps, .cmp },
7128 else => null,
7129 },
7130 64 => switch (lhs_ty.vectorLen()) {
7131 1 => .{ .v_sd, .cmp },
7132 2...4 => .{ .v_pd, .cmp },
7133 else => null,
7134 },
7135 16, 80, 128 => null,
7136 else => unreachable,
7137 },
7138 else => unreachable,
7139 },
7140 else => unreachable,
7141 })) |tag| tag else return self.fail("TODO implement genBinOp for {s} {}", .{
7142 @tagName(air_tag), lhs_ty.fmt(self.bin_file.options.module.?),
7143 }),
7144 mask_reg,
7145 rhs_copy_reg,
7146 rhs_copy_reg,
7147 Immediate.u(3), // unord
7148 );
7149 try self.asmRegisterRegisterRegisterRegister(
7150 if (@as(?Mir.Inst.FixedTag, switch (lhs_ty.zigTypeTag()) {
7151 .Float => switch (lhs_ty.floatBits(self.target.*)) {
7152 32 => .{ .v_ps, .blendv },
7153 64 => .{ .v_pd, .blendv },
7154 16, 80, 128 => null,
7155 else => unreachable,
7156 },
7157 .Vector => switch (lhs_ty.childType().zigTypeTag()) {
7158 .Float => switch (lhs_ty.childType().floatBits(self.target.*)) {
7159 32 => switch (lhs_ty.vectorLen()) {
7160 1...8 => .{ .v_ps, .blendv },
7161 else => null,
7162 },
7163 64 => switch (lhs_ty.vectorLen()) {
7164 1...4 => .{ .v_pd, .blendv },
7165 else => null,
7166 },
7167 16, 80, 128 => null,
7168 else => unreachable,
7169 },
7170 else => unreachable,
7171 },
7172 else => unreachable,
7173 })) |tag| tag else return self.fail("TODO implement genBinOp for {s} {}", .{
7174 @tagName(air_tag), lhs_ty.fmt(self.bin_file.options.module.?),
7175 }),
7176 dst_reg,
7177 dst_reg,
7178 lhs_copy_reg.?,
7179 mask_reg,
7180 );
7181 } else {
7182 const has_blend = self.hasFeature(.sse4_1);
7183 try self.asmRegisterRegisterImmediate(
7184 if (@as(?Mir.Inst.FixedTag, switch (lhs_ty.zigTypeTag()) {
7185 .Float => switch (lhs_ty.floatBits(self.target.*)) {
7186 32 => .{ ._ss, .cmp },
7187 64 => .{ ._sd, .cmp },
7188 16, 80, 128 => null,
7189 else => unreachable,
7190 },
7191 .Vector => switch (lhs_ty.childType().zigTypeTag()) {
7192 .Float => switch (lhs_ty.childType().floatBits(self.target.*)) {
7193 32 => switch (lhs_ty.vectorLen()) {
7194 1 => .{ ._ss, .cmp },
7195 2...4 => .{ ._ps, .cmp },
7196 else => null,
7197 },
7198 64 => switch (lhs_ty.vectorLen()) {
7199 1 => .{ ._sd, .cmp },
7200 2 => .{ ._pd, .cmp },
7201 else => null,
7202 },
7203 16, 80, 128 => null,
7204 else => unreachable,
7205 },
7206 else => unreachable,
7207 },
7208 else => unreachable,
7209 })) |tag| tag else return self.fail("TODO implement genBinOp for {s} {}", .{
7210 @tagName(air_tag), lhs_ty.fmt(self.bin_file.options.module.?),
7211 }),
7212 mask_reg,
7213 mask_reg,
7214 Immediate.u(if (has_blend) 3 else 7), // unord, ord
7215 );
7216 if (has_blend) try self.asmRegisterRegisterRegister(
7217 if (@as(?Mir.Inst.FixedTag, switch (lhs_ty.zigTypeTag()) {
7218 .Float => switch (lhs_ty.floatBits(self.target.*)) {
7219 32 => .{ ._ps, .blendv },
7220 64 => .{ ._pd, .blendv },
7221 16, 80, 128 => null,
7222 else => unreachable,
7223 },
7224 .Vector => switch (lhs_ty.childType().zigTypeTag()) {
7225 .Float => switch (lhs_ty.childType().floatBits(self.target.*)) {
7226 32 => switch (lhs_ty.vectorLen()) {
7227 1...4 => .{ ._ps, .blendv },
7228 else => null,
7229 },
7230 64 => switch (lhs_ty.vectorLen()) {
7231 1...2 => .{ ._pd, .blendv },
7232 else => null,
7233 },
7234 16, 80, 128 => null,
7235 else => unreachable,
7236 },
7237 else => unreachable,
7238 },
7239 else => unreachable,
7240 })) |tag| tag else return self.fail("TODO implement genBinOp for {s} {}", .{
7241 @tagName(air_tag), lhs_ty.fmt(self.bin_file.options.module.?),
7242 }),
7243 dst_reg,
7244 lhs_copy_reg.?,
7245 mask_reg,
7246 ) else {
7247 try self.asmRegisterRegister(
7248 if (@as(?Mir.Inst.FixedTag, switch (lhs_ty.zigTypeTag()) {
7249 .Float => switch (lhs_ty.floatBits(self.target.*)) {
7250 32 => .{ ._ps, .@"and" },
7251 64 => .{ ._pd, .@"and" },
7252 16, 80, 128 => null,
7253 else => unreachable,
7254 },
7255 .Vector => switch (lhs_ty.childType().zigTypeTag()) {
7256 .Float => switch (lhs_ty.childType().floatBits(self.target.*)) {
7257 32 => switch (lhs_ty.vectorLen()) {
7258 1...4 => .{ ._ps, .@"and" },
7259 else => null,
7260 },
7261 64 => switch (lhs_ty.vectorLen()) {
7262 1...2 => .{ ._pd, .@"and" },
7263 else => null,
7264 },
7265 16, 80, 128 => null,
7266 else => unreachable,
7267 },
7268 else => unreachable,
7269 },
7270 else => unreachable,
7271 })) |tag| tag else return self.fail("TODO implement genBinOp for {s} {}", .{
7272 @tagName(air_tag), lhs_ty.fmt(self.bin_file.options.module.?),
7273 }),
7274 dst_reg,
7275 mask_reg,
7276 );
7277 try self.asmRegisterRegister(
7278 if (@as(?Mir.Inst.FixedTag, switch (lhs_ty.zigTypeTag()) {
7279 .Float => switch (lhs_ty.floatBits(self.target.*)) {
7280 32 => .{ ._ps, .andn },
7281 64 => .{ ._pd, .andn },
7282 16, 80, 128 => null,
7283 else => unreachable,
7284 },
7285 .Vector => switch (lhs_ty.childType().zigTypeTag()) {
7286 .Float => switch (lhs_ty.childType().floatBits(self.target.*)) {
7287 32 => switch (lhs_ty.vectorLen()) {
7288 1...4 => .{ ._ps, .andn },
7289 else => null,
7290 },
7291 64 => switch (lhs_ty.vectorLen()) {
7292 1...2 => .{ ._pd, .andn },
7293 else => null,
7294 },
7295 16, 80, 128 => null,
7296 else => unreachable,
7297 },
7298 else => unreachable,
7299 },
7300 else => unreachable,
7301 })) |tag| tag else return self.fail("TODO implement genBinOp for {s} {}", .{
7302 @tagName(air_tag), lhs_ty.fmt(self.bin_file.options.module.?),
7303 }),
7304 mask_reg,
7305 lhs_copy_reg.?,
7306 );
7307 try self.asmRegisterRegister(
7308 if (@as(?Mir.Inst.FixedTag, switch (lhs_ty.zigTypeTag()) {
7309 .Float => switch (lhs_ty.floatBits(self.target.*)) {
7310 32 => .{ ._ps, .@"or" },
7311 64 => .{ ._pd, .@"or" },
7312 16, 80, 128 => null,
7313 else => unreachable,
7314 },
7315 .Vector => switch (lhs_ty.childType().zigTypeTag()) {
7316 .Float => switch (lhs_ty.childType().floatBits(self.target.*)) {
7317 32 => switch (lhs_ty.vectorLen()) {
7318 1...4 => .{ ._ps, .@"or" },
7319 else => null,
7320 },
7321 64 => switch (lhs_ty.vectorLen()) {
7322 1...2 => .{ ._pd, .@"or" },
7323 else => null,
7324 },
7325 16, 80, 128 => null,
7326 else => unreachable,
7327 },
7328 else => unreachable,
7329 },
7330 else => unreachable,
7331 })) |tag| tag else return self.fail("TODO implement genBinOp for {s} {}", .{
7332 @tagName(air_tag), lhs_ty.fmt(self.bin_file.options.module.?),
7333 }),
7334 dst_reg,
7335 mask_reg,
7336 );
7337 }
7338 },
70587339 else => unreachable,
70597340 }
7341
70607342 return dst_mcv;
70617343}
70627344
......@@ -9282,7 +9564,7 @@ fn genSetReg(self: *Self, dst_reg: Register, ty: Type, src_mcv: MCValue) InnerEr
92829564 17...32 => if (self.hasFeature(.avx)) .{ .v_, .movdqa } else null,
92839565 else => null,
92849566 },
9285 .Float => switch (ty.floatBits(self.target.*)) {
9567 .Float => switch (ty.scalarType().floatBits(self.target.*)) {
92869568 16, 128 => switch (abi_size) {
92879569 2...4 => if (self.hasFeature(.avx)) .{ .v_d, .mov } else .{ ._d, .mov },
92889570 5...8 => if (self.hasFeature(.avx)) .{ .v_q, .mov } else .{ ._q, .mov },
src/arch/x86_64/Encoding.zig+20-13
......@@ -178,7 +178,7 @@ pub fn format(
178178 try writer.print("+{s} ", .{tag});
179179 },
180180 .m, .mi, .m1, .mc, .vmi => try writer.print("/{d} ", .{encoding.modRmExt()}),
181 .mr, .rm, .rmi, .mri, .mrc, .rvm, .rvmi, .mvr => try writer.writeAll("/r "),
181 .mr, .rm, .rmi, .mri, .mrc, .rm0, .rvm, .rvmr, .rvmi, .mvr => try writer.writeAll("/r "),
182182 }
183183
184184 switch (encoding.data.op_en) {
......@@ -202,7 +202,8 @@ pub fn format(
202202 };
203203 try writer.print("{s} ", .{tag});
204204 },
205 .np, .fd, .td, .o, .m, .m1, .mc, .mr, .rm, .mrc, .rvm, .mvr => {},
205 .rvmr => try writer.writeAll("/is4 "),
206 .np, .fd, .td, .o, .m, .m1, .mc, .mr, .rm, .mrc, .rm0, .rvm, .mvr => {},
206207 }
207208
208209 try writer.print("{s} ", .{@tagName(encoding.mnemonic)});
......@@ -270,7 +271,7 @@ pub const Mnemonic = enum {
270271 addps, addss,
271272 andps,
272273 andnps,
273 cmpss,
274 cmpps, cmpss,
274275 cvtpi2ps, cvtps2pi, cvtsi2ss, cvtss2si, cvttps2pi, cvttss2si,
275276 divps, divss,
276277 maxps, maxss,
......@@ -290,7 +291,7 @@ pub const Mnemonic = enum {
290291 addpd, addsd,
291292 andpd,
292293 andnpd,
293 //cmpsd,
294 cmppd, //cmpsd,
294295 cvtdq2pd, cvtdq2ps, cvtpd2dq, cvtpd2pi, cvtpd2ps, cvtpi2pd,
295296 cvtps2dq, cvtps2pd, cvtsd2si, cvtsd2ss, cvtsi2sd, cvtss2sd,
296297 cvttpd2dq, cvttpd2pi, cvttps2dq, cvttsd2si,
......@@ -315,6 +316,7 @@ pub const Mnemonic = enum {
315316 // SSE3
316317 movddup, movshdup, movsldup,
317318 // SSE4.1
319 blendpd, blendps, blendvpd, blendvps,
318320 extractps,
319321 insertps,
320322 pextrb, pextrd, pextrq,
......@@ -325,7 +327,9 @@ pub const Mnemonic = enum {
325327 // AVX
326328 vaddpd, vaddps, vaddsd, vaddss,
327329 vandnpd, vandnps, vandpd, vandps,
330 vblendpd, vblendps, vblendvpd, vblendvps,
328331 vbroadcastf128, vbroadcastsd, vbroadcastss,
332 vcmppd, vcmpps, vcmpsd, vcmpss,
329333 vcvtdq2pd, vcvtdq2ps, vcvtpd2dq, vcvtpd2ps,
330334 vcvtps2dq, vcvtps2pd, vcvtsd2si, vcvtsd2ss,
331335 vcvtsi2sd, vcvtsi2ss, vcvtss2sd, vcvtss2si,
......@@ -385,7 +389,7 @@ pub const OpEn = enum {
385389 fd, td,
386390 m1, mc, mi, mr, rm,
387391 rmi, mri, mrc,
388 vmi, rvm, rvmi, mvr,
392 rm0, vmi, rvm, rvmr, rvmi, mvr,
389393 // zig fmt: on
390394};
391395
......@@ -407,7 +411,7 @@ pub const Op = enum {
407411 moffs,
408412 sreg,
409413 st, mm, mm_m64,
410 xmm, xmm_m32, xmm_m64, xmm_m128,
414 xmm0, xmm, xmm_m32, xmm_m64, xmm_m128,
411415 ymm, ymm_m256,
412416 // zig fmt: on
413417
......@@ -436,7 +440,9 @@ pub const Op = enum {
436440 .segment => .sreg,
437441 .x87 => .st,
438442 .mmx => .mm,
439 .sse => switch (reg.bitSize()) {
443 .sse => if (reg == .xmm0)
444 .xmm0
445 else switch (reg.bitSize()) {
440446 128 => .xmm,
441447 256 => .ymm,
442448 else => unreachable,
......@@ -494,7 +500,7 @@ pub const Op = enum {
494500 .eax, .r32, .rm32, .r32_m16 => unreachable,
495501 .rax, .r64, .rm64, .r64_m16 => unreachable,
496502 .st, .mm, .mm_m64 => unreachable,
497 .xmm, .xmm_m32, .xmm_m64, .xmm_m128 => unreachable,
503 .xmm0, .xmm, .xmm_m32, .xmm_m64, .xmm_m128 => unreachable,
498504 .ymm, .ymm_m256 => unreachable,
499505 .m8, .m16, .m32, .m64, .m80, .m128, .m256 => unreachable,
500506 .unity => 1,
......@@ -516,7 +522,7 @@ pub const Op = enum {
516522 .eax, .r32, .rm32, .r32_m8, .r32_m16 => 32,
517523 .rax, .r64, .rm64, .r64_m16, .mm, .mm_m64 => 64,
518524 .st => 80,
519 .xmm, .xmm_m32, .xmm_m64, .xmm_m128 => 128,
525 .xmm0, .xmm, .xmm_m32, .xmm_m64, .xmm_m128 => 128,
520526 .ymm, .ymm_m256 => 256,
521527 };
522528 }
......@@ -526,7 +532,8 @@ pub const Op = enum {
526532 .none, .o16, .o32, .o64, .moffs, .m, .sreg => unreachable,
527533 .unity, .imm8, .imm8s, .imm16, .imm16s, .imm32, .imm32s, .imm64 => unreachable,
528534 .rel8, .rel16, .rel32 => unreachable,
529 .al, .cl, .r8, .ax, .r16, .eax, .r32, .rax, .r64, .st, .mm, .xmm, .ymm => unreachable,
535 .al, .cl, .r8, .ax, .r16, .eax, .r32, .rax, .r64 => unreachable,
536 .st, .mm, .xmm0, .xmm, .ymm => unreachable,
530537 .m8, .rm8, .r32_m8 => 8,
531538 .m16, .rm16, .r32_m16, .r64_m16 => 16,
532539 .m32, .rm32, .xmm_m32 => 32,
......@@ -558,7 +565,7 @@ pub const Op = enum {
558565 .rm8, .rm16, .rm32, .rm64,
559566 .r32_m8, .r32_m16, .r64_m16,
560567 .st, .mm, .mm_m64,
561 .xmm, .xmm_m32, .xmm_m64, .xmm_m128,
568 .xmm0, .xmm, .xmm_m32, .xmm_m64, .xmm_m128,
562569 .ymm, .ymm_m256,
563570 => true,
564571 else => false,
......@@ -612,7 +619,7 @@ pub const Op = enum {
612619 .sreg => .segment,
613620 .st => .x87,
614621 .mm, .mm_m64 => .mmx,
615 .xmm, .xmm_m32, .xmm_m64, .xmm_m128 => .sse,
622 .xmm0, .xmm, .xmm_m32, .xmm_m64, .xmm_m128 => .sse,
616623 .ymm, .ymm_m256 => .sse,
617624 };
618625 }
......@@ -629,7 +636,7 @@ pub const Op = enum {
629636 else => {
630637 if (op.isRegister() and target.isRegister()) {
631638 return switch (target) {
632 .cl, .al, .ax, .eax, .rax => op == target,
639 .cl, .al, .ax, .eax, .rax, .xmm0 => op == target,
633640 else => op.class() == target.class() and op.regBitSize() == target.regBitSize(),
634641 };
635642 }
src/arch/x86_64/Lower.zig+7
......@@ -377,6 +377,7 @@ fn generic(lower: *Lower, inst: Mir.Inst) Error!void {
377377 .r => inst.data.r.fixes,
378378 .rr => inst.data.rr.fixes,
379379 .rrr => inst.data.rrr.fixes,
380 .rrrr => inst.data.rrrr.fixes,
380381 .rrri => inst.data.rrri.fixes,
381382 .rri_s, .rri_u => inst.data.rri.fixes,
382383 .ri_s, .ri_u => inst.data.ri.fixes,
......@@ -430,6 +431,12 @@ fn generic(lower: *Lower, inst: Mir.Inst) Error!void {
430431 .{ .reg = inst.data.rrr.r2 },
431432 .{ .reg = inst.data.rrr.r3 },
432433 },
434 .rrrr => &.{
435 .{ .reg = inst.data.rrrr.r1 },
436 .{ .reg = inst.data.rrrr.r2 },
437 .{ .reg = inst.data.rrrr.r3 },
438 .{ .reg = inst.data.rrrr.r4 },
439 },
433440 .rrri => &.{
434441 .{ .reg = inst.data.rrri.r1 },
435442 .{ .reg = inst.data.rrri.r2 },
src/arch/x86_64/Mir.zig+20
......@@ -596,6 +596,16 @@ pub const Inst = struct {
596596 /// Replicate single floating-point values
597597 movsldup,
598598
599 /// Blend packed single-precision floating-point values
600 /// Blend scalar single-precision floating-point values
601 /// Blend packed double-precision floating-point values
602 /// Blend scalar double-precision floating-point values
603 blend,
604 /// Variable blend packed single-precision floating-point values
605 /// Variable blend scalar single-precision floating-point values
606 /// Variable blend packed double-precision floating-point values
607 /// Variable blend scalar double-precision floating-point values
608 blendv,
599609 /// Extract packed floating-point values
600610 extract,
601611 /// Insert scalar single-precision floating-point value
......@@ -651,6 +661,9 @@ pub const Inst = struct {
651661 /// Register, register, register operands.
652662 /// Uses `rrr` payload.
653663 rrr,
664 /// Register, register, register, register operands.
665 /// Uses `rrrr` payload.
666 rrrr,
654667 /// Register, register, register, immediate (byte) operands.
655668 /// Uses `rrri` payload.
656669 rrri,
......@@ -870,6 +883,13 @@ pub const Inst = struct {
870883 r2: Register,
871884 r3: Register,
872885 },
886 rrrr: struct {
887 fixes: Fixes = ._,
888 r1: Register,
889 r2: Register,
890 r3: Register,
891 r4: Register,
892 },
873893 rrri: struct {
874894 fixes: Fixes = ._,
875895 r1: Register,
src/arch/x86_64/encoder.zig+16-15
......@@ -226,8 +226,8 @@ pub const Instruction = struct {
226226 else => {
227227 const mem_op = switch (data.op_en) {
228228 .m, .mi, .m1, .mc, .mr, .mri, .mrc, .mvr => inst.ops[0],
229 .rm, .rmi, .vmi => inst.ops[1],
230 .rvm, .rvmi => inst.ops[2],
229 .rm, .rmi, .rm0, .vmi => inst.ops[1],
230 .rvm, .rvmr, .rvmi => inst.ops[2],
231231 else => unreachable,
232232 };
233233 switch (mem_op) {
......@@ -235,7 +235,7 @@ pub const Instruction = struct {
235235 const rm = switch (data.op_en) {
236236 .m, .mi, .m1, .mc, .vmi => enc.modRmExt(),
237237 .mr, .mri, .mrc => inst.ops[1].reg.lowEnc(),
238 .rm, .rmi, .rvm, .rvmi => inst.ops[0].reg.lowEnc(),
238 .rm, .rmi, .rm0, .rvm, .rvmr, .rvmi => inst.ops[0].reg.lowEnc(),
239239 .mvr => inst.ops[2].reg.lowEnc(),
240240 else => unreachable,
241241 };
......@@ -245,7 +245,7 @@ pub const Instruction = struct {
245245 const op = switch (data.op_en) {
246246 .m, .mi, .m1, .mc, .vmi => .none,
247247 .mr, .mri, .mrc => inst.ops[1],
248 .rm, .rmi, .rvm, .rvmi => inst.ops[0],
248 .rm, .rmi, .rm0, .rvm, .rvmr, .rvmi => inst.ops[0],
249249 .mvr => inst.ops[2],
250250 else => unreachable,
251251 };
......@@ -257,6 +257,7 @@ pub const Instruction = struct {
257257 switch (data.op_en) {
258258 .mi => try encodeImm(inst.ops[1].imm, data.ops[1], encoder),
259259 .rmi, .mri, .vmi => try encodeImm(inst.ops[2].imm, data.ops[2], encoder),
260 .rvmr => try encoder.imm8(@as(u8, inst.ops[3].reg.enc()) << 4),
260261 .rvmi => try encodeImm(inst.ops[3].imm, data.ops[3], encoder),
261262 else => {},
262263 }
......@@ -298,7 +299,7 @@ pub const Instruction = struct {
298299 .i, .zi, .o, .oi, .d, .np => null,
299300 .fd => inst.ops[1].mem.base().reg,
300301 .td => inst.ops[0].mem.base().reg,
301 .rm, .rmi => if (inst.ops[1].isSegmentRegister())
302 .rm, .rmi, .rm0 => if (inst.ops[1].isSegmentRegister())
302303 switch (inst.ops[1]) {
303304 .reg => |reg| reg,
304305 .mem => |mem| mem.base().reg,
......@@ -314,7 +315,7 @@ pub const Instruction = struct {
314315 }
315316 else
316317 null,
317 .vmi, .rvm, .rvmi, .mvr => unreachable,
318 .vmi, .rvm, .rvmr, .rvmi, .mvr => unreachable,
318319 };
319320 if (segment_override) |seg| {
320321 legacy.setSegmentOverride(seg);
......@@ -333,23 +334,23 @@ pub const Instruction = struct {
333334 switch (op_en) {
334335 .np, .i, .zi, .fd, .td, .d => {},
335336 .o, .oi => rex.b = inst.ops[0].reg.isExtended(),
336 .m, .mi, .m1, .mc, .mr, .rm, .rmi, .mri, .mrc => {
337 .m, .mi, .m1, .mc, .mr, .rm, .rmi, .mri, .mrc, .rm0 => {
337338 const r_op = switch (op_en) {
338 .rm, .rmi => inst.ops[0],
339 .rm, .rmi, .rm0 => inst.ops[0],
339340 .mr, .mri, .mrc => inst.ops[1],
340341 else => .none,
341342 };
342343 rex.r = r_op.isBaseExtended();
343344
344345 const b_x_op = switch (op_en) {
345 .rm, .rmi => inst.ops[1],
346 .rm, .rmi, .rm0 => inst.ops[1],
346347 .m, .mi, .m1, .mc, .mr, .mri, .mrc => inst.ops[0],
347348 else => unreachable,
348349 };
349350 rex.b = b_x_op.isBaseExtended();
350351 rex.x = b_x_op.isIndexExtended();
351352 },
352 .vmi, .rvm, .rvmi, .mvr => unreachable,
353 .vmi, .rvm, .rvmr, .rvmi, .mvr => unreachable,
353354 }
354355
355356 try encoder.rex(rex);
......@@ -367,9 +368,9 @@ pub const Instruction = struct {
367368 switch (op_en) {
368369 .np, .i, .zi, .fd, .td, .d => {},
369370 .o, .oi => vex.b = inst.ops[0].reg.isExtended(),
370 .m, .mi, .m1, .mc, .mr, .rm, .rmi, .mri, .mrc, .vmi, .rvm, .rvmi, .mvr => {
371 .m, .mi, .m1, .mc, .mr, .rm, .rmi, .mri, .mrc, .rm0, .vmi, .rvm, .rvmr, .rvmi, .mvr => {
371372 const r_op = switch (op_en) {
372 .rm, .rmi, .rvm, .rvmi => inst.ops[0],
373 .rm, .rmi, .rm0, .rvm, .rvmr, .rvmi => inst.ops[0],
373374 .mr, .mri, .mrc => inst.ops[1],
374375 .mvr => inst.ops[2],
375376 .m, .mi, .m1, .mc, .vmi => .none,
......@@ -378,9 +379,9 @@ pub const Instruction = struct {
378379 vex.r = r_op.isBaseExtended();
379380
380381 const b_x_op = switch (op_en) {
381 .rm, .rmi, .vmi => inst.ops[1],
382 .rm, .rmi, .rm0, .vmi => inst.ops[1],
382383 .m, .mi, .m1, .mc, .mr, .mri, .mrc, .mvr => inst.ops[0],
383 .rvm, .rvmi => inst.ops[2],
384 .rvm, .rvmr, .rvmi => inst.ops[2],
384385 else => unreachable,
385386 };
386387 vex.b = b_x_op.isBaseExtended();
......@@ -408,7 +409,7 @@ pub const Instruction = struct {
408409 switch (op_en) {
409410 else => {},
410411 .vmi => vex.v = inst.ops[0].reg,
411 .rvm, .rvmi => vex.v = inst.ops[1].reg,
412 .rvm, .rvmr, .rvmi => vex.v = inst.ops[1].reg,
412413 }
413414
414415 try encoder.vex(vex);
src/arch/x86_64/encodings.zig+34
......@@ -846,6 +846,8 @@ pub const table = [_]Entry{
846846
847847 .{ .andps, .rm, &.{ .xmm, .xmm_m128 }, &.{ 0x0f, 0x54 }, 0, .none, .sse },
848848
849 .{ .cmpps, .rmi, &.{ .xmm, .xmm_m128, .imm8 }, &.{ 0x0f, 0xc2 }, 0, .none, .sse },
850
849851 .{ .cmpss, .rmi, &.{ .xmm, .xmm_m32, .imm8 }, &.{ 0xf3, 0x0f, 0xc2 }, 0, .none, .sse },
850852
851853 .{ .cvtpi2ps, .rm, &.{ .xmm, .mm_m64 }, &.{ 0x0f, 0x2a }, 0, .none, .sse },
......@@ -917,6 +919,8 @@ pub const table = [_]Entry{
917919
918920 .{ .andpd, .rm, &.{ .xmm, .xmm_m128 }, &.{ 0x66, 0x0f, 0x54 }, 0, .none, .sse2 },
919921
922 .{ .cmppd, .rmi, &.{ .xmm, .xmm_m128, .imm8 }, &.{ 0x66, 0x0f, 0xc2 }, 0, .none, .sse2 },
923
920924 .{ .cmpsd, .rmi, &.{ .xmm, .xmm_m64, .imm8 }, &.{ 0xf2, 0x0f, 0xc2 }, 0, .none, .sse2 },
921925
922926 .{ .cvtdq2pd, .rm, &.{ .xmm, .xmm_m64 }, &.{ 0xf3, 0x0f, 0xe6 }, 0, .none, .sse2 },
......@@ -1085,6 +1089,14 @@ pub const table = [_]Entry{
10851089 .{ .movsldup, .rm, &.{ .xmm, .xmm_m128 }, &.{ 0xf3, 0x0f, 0x12 }, 0, .none, .sse3 },
10861090
10871091 // SSE4.1
1092 .{ .blendpd, .rmi, &.{ .xmm, .xmm_m128, .imm8 }, &.{ 0x66, 0x0f, 0x3a, 0x0d }, 0, .none, .sse4_1 },
1093
1094 .{ .blendps, .rmi, &.{ .xmm, .xmm_m128, .imm8 }, &.{ 0x66, 0x0f, 0x3a, 0x0c }, 0, .none, .sse4_1 },
1095
1096 .{ .blendvpd, .rm0, &.{ .xmm, .xmm_m128, .xmm0 }, &.{ 0x66, 0x0f, 0x38, 0x15 }, 0, .none, .sse4_1 },
1097
1098 .{ .blendvps, .rm0, &.{ .xmm, .xmm_m128, .xmm0 }, &.{ 0x66, 0x0f, 0x38, 0x14 }, 0, .none, .sse4_1 },
1099
10881100 .{ .extractps, .mri, &.{ .rm32, .xmm, .imm8 }, &.{ 0x66, 0x0f, 0x3a, 0x17 }, 0, .none, .sse4_1 },
10891101
10901102 .{ .insertps, .rmi, &.{ .xmm, .xmm_m32, .imm8 }, &.{ 0x66, 0x0f, 0x3a, 0x21 }, 0, .none, .sse4_1 },
......@@ -1146,11 +1158,33 @@ pub const table = [_]Entry{
11461158 .{ .vandps, .rvm, &.{ .xmm, .xmm, .xmm_m128 }, &.{ 0x0f, 0x54 }, 0, .vex_128_wig, .avx },
11471159 .{ .vandps, .rvm, &.{ .ymm, .ymm, .ymm_m256 }, &.{ 0x0f, 0x54 }, 0, .vex_256_wig, .avx },
11481160
1161 .{ .vblendpd, .rvmi, &.{ .xmm, .xmm, .xmm_m128, .imm8 }, &.{ 0x66, 0x0f, 0x3a, 0x0d }, 0, .vex_128_wig, .avx },
1162 .{ .vblendpd, .rvmi, &.{ .ymm, .ymm, .ymm_m256, .imm8 }, &.{ 0x66, 0x0f, 0x3a, 0x0d }, 0, .vex_256_wig, .avx },
1163
1164 .{ .vblendps, .rvmi, &.{ .xmm, .xmm, .xmm_m128, .imm8 }, &.{ 0x66, 0x0f, 0x3a, 0x0c }, 0, .vex_128_wig, .avx },
1165 .{ .vblendps, .rvmi, &.{ .ymm, .ymm, .ymm_m256, .imm8 }, &.{ 0x66, 0x0f, 0x3a, 0x0c }, 0, .vex_256_wig, .avx },
1166
1167 .{ .vblendvpd, .rvmr, &.{ .xmm, .xmm, .xmm_m128, .xmm }, &.{ 0x66, 0x0f, 0x3a, 0x4b }, 0, .vex_128_w0, .avx },
1168 .{ .vblendvpd, .rvmr, &.{ .ymm, .ymm, .ymm_m256, .ymm }, &.{ 0x66, 0x0f, 0x3a, 0x4b }, 0, .vex_256_w0, .avx },
1169
1170 .{ .vblendvps, .rvmr, &.{ .xmm, .xmm, .xmm_m128, .xmm }, &.{ 0x66, 0x0f, 0x3a, 0x4a }, 0, .vex_128_w0, .avx },
1171 .{ .vblendvps, .rvmr, &.{ .ymm, .ymm, .ymm_m256, .ymm }, &.{ 0x66, 0x0f, 0x3a, 0x4a }, 0, .vex_256_w0, .avx },
1172
11491173 .{ .vbroadcastss, .rm, &.{ .xmm, .m32 }, &.{ 0x66, 0x0f, 0x38, 0x18 }, 0, .vex_128_w0, .avx },
11501174 .{ .vbroadcastss, .rm, &.{ .ymm, .m32 }, &.{ 0x66, 0x0f, 0x38, 0x18 }, 0, .vex_256_w0, .avx },
11511175 .{ .vbroadcastsd, .rm, &.{ .ymm, .m64 }, &.{ 0x66, 0x0f, 0x38, 0x19 }, 0, .vex_256_w0, .avx },
11521176 .{ .vbroadcastf128, .rm, &.{ .ymm, .m128 }, &.{ 0x66, 0x0f, 0x38, 0x1a }, 0, .vex_256_w0, .avx },
11531177
1178 .{ .vcmppd, .rvmi, &.{ .xmm, .xmm, .xmm_m128, .imm8 }, &.{ 0x66, 0x0f, 0xc2 }, 0, .vex_128_wig, .avx },
1179 .{ .vcmppd, .rvmi, &.{ .ymm, .ymm, .ymm_m256, .imm8 }, &.{ 0x66, 0x0f, 0xc2 }, 0, .vex_256_wig, .avx },
1180
1181 .{ .vcmpps, .rvmi, &.{ .xmm, .xmm, .xmm_m128, .imm8 }, &.{ 0x0f, 0xc2 }, 0, .vex_128_wig, .avx },
1182 .{ .vcmpps, .rvmi, &.{ .ymm, .ymm, .ymm_m256, .imm8 }, &.{ 0x0f, 0xc2 }, 0, .vex_256_wig, .avx },
1183
1184 .{ .vcmpsd, .rvmi, &.{ .xmm, .xmm, .xmm_m64, .imm8 }, &.{ 0xf2, 0x0f, 0xc2 }, 0, .vex_lig_wig, .avx },
1185
1186 .{ .vcmpss, .rvmi, &.{ .xmm, .xmm, .xmm_m32, .imm8 }, &.{ 0xf3, 0x0f, 0xc2 }, 0, .vex_lig_wig, .avx },
1187
11541188 .{ .vcvtdq2pd, .rm, &.{ .xmm, .xmm_m64 }, &.{ 0xf3, 0x0f, 0xe6 }, 0, .vex_128_wig, .avx },
11551189 .{ .vcvtdq2pd, .rm, &.{ .ymm, .xmm_m128 }, &.{ 0xf3, 0x0f, 0xe6 }, 0, .vex_256_wig, .avx },
11561190
test/behavior/maximum_minimum.zig+4-2
......@@ -24,7 +24,8 @@ test "@max" {
2424
2525test "@max on vectors" {
2626 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
27 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
27 if (builtin.zig_backend == .stage2_x86_64 and
28 !comptime std.Target.x86.featureSetHas(builtin.cpu.features, .sse4_1)) return error.SkipZigTest; // TODO
2829 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
2930 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
3031 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -72,7 +73,8 @@ test "@min" {
7273
7374test "@min for vectors" {
7475 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
75 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
76 if (builtin.zig_backend == .stage2_x86_64 and
77 !comptime std.Target.x86.featureSetHas(builtin.cpu.features, .sse4_1)) return error.SkipZigTest; // TODO
7678 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
7779 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
7880 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO