authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2023-05-07 10:04:56-04:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2023-05-08 07:36:20-04:00
logf8708e2c4d93eece5b3e131fd2d1b5b210806cd6
tree06385bc6d2841e74a90225f98ac89460430f38e7
parent057139fda575e0e6038b821256a45669cd70a073

x86_64: implement `@floor`, `@ceil`, and `@trunc` for float vectors


2 files changed, 93 insertions(+), 92 deletions(-)

src/arch/x86_64/CodeGen.zig+87-89
...@@ -1587,9 +1587,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -1587,9 +1587,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
1587 .round,1587 .round,
1588 => try self.airUnaryMath(inst),1588 => try self.airUnaryMath(inst),
15891589
1590 .floor => try self.airRound(inst, Immediate.u(0b1_0_01)),1590 .floor => try self.airRound(inst, 0b1_0_01),
1591 .ceil => try self.airRound(inst, Immediate.u(0b1_0_10)),1591 .ceil => try self.airRound(inst, 0b1_0_10),
1592 .trunc_float => try self.airRound(inst, Immediate.u(0b1_0_11)),1592 .trunc_float => try self.airRound(inst, 0b1_0_11),
1593 .sqrt => try self.airSqrt(inst),1593 .sqrt => try self.airSqrt(inst),
1594 .neg, .fabs => try self.airFloatSign(inst),1594 .neg, .fabs => try self.airFloatSign(inst),
15951595
...@@ -4509,49 +4509,91 @@ fn airFloatSign(self: *Self, inst: Air.Inst.Index) !void {...@@ -4509,49 +4509,91 @@ fn airFloatSign(self: *Self, inst: Air.Inst.Index) !void {
4509 return self.finishAir(inst, dst_mcv, .{ un_op, .none, .none });4509 return self.finishAir(inst, dst_mcv, .{ un_op, .none, .none });
4510}4510}
45114511
4512fn airRound(self: *Self, inst: Air.Inst.Index, mode: Immediate) !void {4512fn airRound(self: *Self, inst: Air.Inst.Index, mode: u4) !void {
4513 const un_op = self.air.instructions.items(.data)[inst].un_op;4513 const un_op = self.air.instructions.items(.data)[inst].un_op;
4514 const ty = self.air.typeOf(un_op);4514 const ty = self.air.typeOf(un_op);
45154515
4516 if (!self.hasFeature(.sse4_1))
4517 return self.fail("TODO implement airRound without sse4_1 feature", .{});
4518
4519 const src_mcv = try self.resolveInst(un_op);4516 const src_mcv = try self.resolveInst(un_op);
4520 const dst_mcv = if (src_mcv.isRegister() and self.reuseOperand(inst, un_op, 0, src_mcv))4517 const dst_mcv = if (src_mcv.isRegister() and self.reuseOperand(inst, un_op, 0, src_mcv))
4521 src_mcv4518 src_mcv
4522 else4519 else
4523 try self.copyToRegisterWithInstTracking(inst, ty, src_mcv);4520 try self.copyToRegisterWithInstTracking(inst, ty, src_mcv);
4521 const dst_reg = dst_mcv.getReg().?;
4522 const dst_lock = self.register_manager.lockReg(dst_reg);
4523 defer if (dst_lock) |lock| self.register_manager.unlockReg(lock);
4524 try self.genRound(ty, dst_reg, src_mcv, mode);
4525 return self.finishAir(inst, dst_mcv, .{ un_op, .none, .none });
4526}
45244527
4525 const mir_tag: Mir.Inst.Tag = switch (ty.zigTypeTag()) {4528fn genRound(self: *Self, ty: Type, dst_reg: Register, src_mcv: MCValue, mode: u4) !void {
4529 if (!self.hasFeature(.sse4_1))
4530 return self.fail("TODO implement genRound without sse4_1 feature", .{});
4531
4532 const mir_tag = if (@as(?Mir.Inst.Tag, switch (ty.zigTypeTag()) {
4526 .Float => switch (ty.floatBits(self.target.*)) {4533 .Float => switch (ty.floatBits(self.target.*)) {
4527 32 => .roundss,4534 32 => if (self.hasFeature(.avx)) .vroundss else .roundss,
4528 64 => .roundsd,4535 64 => if (self.hasFeature(.avx)) .vroundsd else .roundsd,
4529 else => return self.fail("TODO implement airRound for {}", .{4536 16, 80, 128 => null,
4530 ty.fmt(self.bin_file.options.module.?),4537 else => unreachable,
4531 }),
4532 },4538 },
4533 else => return self.fail("TODO implement airRound for {}", .{4539 .Vector => switch (ty.childType().zigTypeTag()) {
4534 ty.fmt(self.bin_file.options.module.?),4540 .Float => switch (ty.childType().floatBits(self.target.*)) {
4535 }),4541 32 => switch (ty.vectorLen()) {
4536 };4542 1 => if (self.hasFeature(.avx)) .vroundss else .roundss,
4537 assert(dst_mcv.isRegister());4543 2...4 => if (self.hasFeature(.avx)) .vroundps else .roundps,
4544 5...8 => if (self.hasFeature(.avx)) .vroundps else null,
4545 else => null,
4546 },
4547 64 => switch (ty.vectorLen()) {
4548 1 => if (self.hasFeature(.avx)) .vroundsd else .roundsd,
4549 2 => if (self.hasFeature(.avx)) .vroundpd else .roundpd,
4550 3...4 => if (self.hasFeature(.avx)) .vroundpd else null,
4551 else => null,
4552 },
4553 16, 80, 128 => null,
4554 else => unreachable,
4555 },
4556 else => null,
4557 },
4558 else => unreachable,
4559 })) |tag| tag else return self.fail("TODO implement genRound for {}", .{
4560 ty.fmt(self.bin_file.options.module.?),
4561 });
4562
4538 const abi_size = @intCast(u32, ty.abiSize(self.target.*));4563 const abi_size = @intCast(u32, ty.abiSize(self.target.*));
4539 const dst_reg = registerAlias(dst_mcv.getReg().?, abi_size);4564 const dst_alias = registerAlias(dst_reg, abi_size);
4540 if (src_mcv.isRegister())4565 switch (mir_tag) {
4541 try self.asmRegisterRegisterImmediate(4566 .vroundss, .vroundsd => if (src_mcv.isMemory()) try self.asmRegisterRegisterMemoryImmediate(
4542 mir_tag,4567 mir_tag,
4543 dst_reg,4568 dst_alias,
4544 registerAlias(src_mcv.getReg().?, abi_size),4569 dst_alias,
4545 mode,4570 src_mcv.mem(Memory.PtrSize.fromSize(abi_size)),
4546 )4571 Immediate.u(mode),
4547 else4572 ) else try self.asmRegisterRegisterRegisterImmediate(
4548 try self.asmRegisterMemoryImmediate(
4549 mir_tag,4573 mir_tag,
4550 dst_reg,4574 dst_alias,
4551 src_mcv.mem(Memory.PtrSize.fromSize(@intCast(u32, ty.abiSize(self.target.*)))),4575 dst_alias,
4552 mode,4576 registerAlias(if (src_mcv.isRegister())
4553 );4577 src_mcv.getReg().?
4554 return self.finishAir(inst, dst_mcv, .{ un_op, .none, .none });4578 else
4579 try self.copyToTmpRegister(ty, src_mcv), abi_size),
4580 Immediate.u(mode),
4581 ),
4582 else => if (src_mcv.isMemory()) try self.asmRegisterMemoryImmediate(
4583 mir_tag,
4584 dst_alias,
4585 src_mcv.mem(Memory.PtrSize.fromSize(abi_size)),
4586 Immediate.u(mode),
4587 ) else try self.asmRegisterRegisterImmediate(
4588 mir_tag,
4589 dst_alias,
4590 registerAlias(if (src_mcv.isRegister())
4591 src_mcv.getReg().?
4592 else
4593 try self.copyToTmpRegister(ty, src_mcv), abi_size),
4594 Immediate.u(mode),
4595 ),
4596 }
4555}4597}
45564598
4557fn airSqrt(self: *Self, inst: Air.Inst.Index) !void {4599fn airSqrt(self: *Self, inst: Air.Inst.Index) !void {
...@@ -6188,18 +6230,18 @@ fn genBinOp(...@@ -6188,18 +6230,18 @@ fn genBinOp(
6188 })) |tag| tag else return self.fail("TODO implement genBinOp for {s} {}", .{6230 })) |tag| tag else return self.fail("TODO implement genBinOp for {s} {}", .{
6189 @tagName(air_tag), lhs_ty.fmt(self.bin_file.options.module.?),6231 @tagName(air_tag), lhs_ty.fmt(self.bin_file.options.module.?),
6190 });6232 });
6191 const dst_alias = registerAlias(dst_mcv.getReg().?, abi_size);6233 const dst_reg = registerAlias(dst_mcv.getReg().?, abi_size);
6192 if (self.hasFeature(.avx)) {6234 if (self.hasFeature(.avx)) {
6193 const src1_alias =6235 const src1_alias =
6194 if (copied_to_dst) dst_alias else registerAlias(lhs_mcv.getReg().?, abi_size);6236 if (copied_to_dst) dst_reg else registerAlias(lhs_mcv.getReg().?, abi_size);
6195 if (src_mcv.isMemory()) try self.asmRegisterRegisterMemory(6237 if (src_mcv.isMemory()) try self.asmRegisterRegisterMemory(
6196 mir_tag,6238 mir_tag,
6197 dst_alias,6239 dst_reg,
6198 src1_alias,6240 src1_alias,
6199 src_mcv.mem(Memory.PtrSize.fromSize(abi_size)),6241 src_mcv.mem(Memory.PtrSize.fromSize(abi_size)),
6200 ) else try self.asmRegisterRegisterRegister(6242 ) else try self.asmRegisterRegisterRegister(
6201 mir_tag,6243 mir_tag,
6202 dst_alias,6244 dst_reg,
6203 src1_alias,6245 src1_alias,
6204 registerAlias(if (src_mcv.isRegister())6246 registerAlias(if (src_mcv.isRegister())
6205 src_mcv.getReg().?6247 src_mcv.getReg().?
...@@ -6210,11 +6252,11 @@ fn genBinOp(...@@ -6210,11 +6252,11 @@ fn genBinOp(
6210 assert(copied_to_dst);6252 assert(copied_to_dst);
6211 if (src_mcv.isMemory()) try self.asmRegisterMemory(6253 if (src_mcv.isMemory()) try self.asmRegisterMemory(
6212 mir_tag,6254 mir_tag,
6213 dst_alias,6255 dst_reg,
6214 src_mcv.mem(Memory.PtrSize.fromSize(abi_size)),6256 src_mcv.mem(Memory.PtrSize.fromSize(abi_size)),
6215 ) else try self.asmRegisterRegister(6257 ) else try self.asmRegisterRegister(
6216 mir_tag,6258 mir_tag,
6217 dst_alias,6259 dst_reg,
6218 registerAlias(if (src_mcv.isRegister())6260 registerAlias(if (src_mcv.isRegister())
6219 src_mcv.getReg().?6261 src_mcv.getReg().?
6220 else6262 else
...@@ -6223,60 +6265,16 @@ fn genBinOp(...@@ -6223,60 +6265,16 @@ fn genBinOp(
6223 }6265 }
6224 switch (air_tag) {6266 switch (air_tag) {
6225 .add, .sub, .mul, .div_float, .div_exact => {},6267 .add, .sub, .mul, .div_float, .div_exact => {},
6226 .div_trunc, .div_floor => if (self.hasFeature(.sse4_1)) {6268 .div_trunc, .div_floor => try self.genRound(
6227 const round_tag = if (@as(?Mir.Inst.Tag, switch (lhs_ty.zigTypeTag()) {6269 lhs_ty,
6228 .Float => switch (lhs_ty.floatBits(self.target.*)) {6270 dst_reg,
6229 32 => if (self.hasFeature(.avx)) .vroundss else .roundss,6271 .{ .register = dst_reg },
6230 64 => if (self.hasFeature(.avx)) .vroundsd else .roundsd,6272 switch (air_tag) {
6231 16, 80, 128 => null,
6232 else => unreachable,
6233 },
6234 .Vector => switch (lhs_ty.childType().zigTypeTag()) {
6235 .Float => switch (lhs_ty.childType().floatBits(self.target.*)) {
6236 32 => switch (lhs_ty.vectorLen()) {
6237 1 => if (self.hasFeature(.avx)) .vroundss else .roundss,
6238 2...4 => if (self.hasFeature(.avx)) .vroundps else .roundps,
6239 5...8 => if (self.hasFeature(.avx)) .vroundps else null,
6240 else => null,
6241 },
6242 64 => switch (lhs_ty.vectorLen()) {
6243 1 => if (self.hasFeature(.avx)) .vroundsd else .roundsd,
6244 2 => if (self.hasFeature(.avx)) .vroundpd else .roundpd,
6245 3...4 => if (self.hasFeature(.avx)) .vroundpd else null,
6246 else => null,
6247 },
6248 16, 80, 128 => null,
6249 else => unreachable,
6250 },
6251 else => null,
6252 },
6253 else => unreachable,
6254 })) |tag| tag else return self.fail("TODO implement genBinOp for {s} {}", .{
6255 @tagName(air_tag), lhs_ty.fmt(self.bin_file.options.module.?),
6256 });
6257 const round_mode = Immediate.u(switch (air_tag) {
6258 .div_trunc => 0b1_0_11,6273 .div_trunc => 0b1_0_11,
6259 .div_floor => 0b1_0_01,6274 .div_floor => 0b1_0_01,
6260 else => unreachable,6275 else => unreachable,
6261 });6276 },
6262 switch (round_tag) {6277 ),
6263 .vroundss, .vroundsd => try self.asmRegisterRegisterRegisterImmediate(
6264 round_tag,
6265 dst_alias,
6266 dst_alias,
6267 dst_alias,
6268 round_mode,
6269 ),
6270 else => try self.asmRegisterRegisterImmediate(
6271 round_tag,
6272 dst_alias,
6273 dst_alias,
6274 round_mode,
6275 ),
6276 }
6277 } else return self.fail("TODO implement genBinOp for {s} {} without sse4_1", .{
6278 @tagName(air_tag), lhs_ty.fmt(self.bin_file.options.module.?),
6279 }),
6280 .max, .min => {}, // TODO: unordered select6278 .max, .min => {}, // TODO: unordered select
6281 else => unreachable,6279 else => unreachable,
6282 }6280 }
test/behavior/floatop.zig+6-3
...@@ -617,7 +617,8 @@ fn testFloor() !void {...@@ -617,7 +617,8 @@ fn testFloor() !void {
617617
618test "@floor with vectors" {618test "@floor with vectors" {
619 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO619 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
620 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO620 if (builtin.zig_backend == .stage2_x86_64 and
621 !comptime std.Target.x86.featureSetHas(builtin.cpu.features, .sse4_1)) return error.SkipZigTest; // TODO
621 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO622 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
622 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO623 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
623624
...@@ -707,7 +708,8 @@ fn testCeil() !void {...@@ -707,7 +708,8 @@ fn testCeil() !void {
707708
708test "@ceil with vectors" {709test "@ceil with vectors" {
709 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO710 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
710 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO711 if (builtin.zig_backend == .stage2_x86_64 and
712 !comptime std.Target.x86.featureSetHas(builtin.cpu.features, .sse4_1)) return error.SkipZigTest; // TODO
711 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO713 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
712 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO714 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
713715
...@@ -797,7 +799,8 @@ fn testTrunc() !void {...@@ -797,7 +799,8 @@ fn testTrunc() !void {
797799
798test "@trunc with vectors" {800test "@trunc with vectors" {
799 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO801 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
800 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO802 if (builtin.zig_backend == .stage2_x86_64 and
803 !comptime std.Target.x86.featureSetHas(builtin.cpu.features, .sse4_1)) return error.SkipZigTest; // TODO
801 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO804 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
802 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO805 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
803806