x86_64: implement basic float ops

This commit is contained in:
Jacob Young 2023-03-17 22:07:32 -04:00 committed by Jakub Konka
parent 29e6aedc95
commit 30e1daa746
6 changed files with 155 additions and 83 deletions

View file

@ -1530,21 +1530,23 @@ fn airPtrArithmetic(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void
fn airMulDivBinOp(self: *Self, inst: Air.Inst.Index) !void { fn airMulDivBinOp(self: *Self, inst: Air.Inst.Index) !void {
const bin_op = self.air.instructions.items(.data)[inst].bin_op; const bin_op = self.air.instructions.items(.data)[inst].bin_op;
const result = result: {
if (self.liveness.isUnused(inst)) break :result .dead;
if (self.liveness.isUnused(inst)) { const tag = self.air.instructions.items(.tag)[inst];
return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); const ty = self.air.typeOfIndex(inst);
}
const tag = self.air.instructions.items(.tag)[inst]; if (ty.zigTypeTag() == .Float) {
const ty = self.air.typeOfIndex(inst); break :result try self.genBinOp(inst, tag, bin_op.lhs, bin_op.rhs);
}
try self.spillRegisters(2, .{ .rax, .rdx }); try self.spillRegisters(2, .{ .rax, .rdx });
const lhs = try self.resolveInst(bin_op.lhs); const lhs = try self.resolveInst(bin_op.lhs);
const rhs = try self.resolveInst(bin_op.rhs); const rhs = try self.resolveInst(bin_op.rhs);
const result = try self.genMulDivBinOp(tag, inst, ty, lhs, rhs);
break :result try self.genMulDivBinOp(tag, inst, ty, lhs, rhs);
};
return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
} }
@ -3288,10 +3290,10 @@ fn genMulDivBinOp(
rhs: MCValue, rhs: MCValue,
) !MCValue { ) !MCValue {
if (ty.zigTypeTag() == .Vector or ty.zigTypeTag() == .Float) { if (ty.zigTypeTag() == .Vector or ty.zigTypeTag() == .Float) {
return self.fail("TODO implement genBinOp for {}", .{ty.fmtDebug()}); return self.fail("TODO implement genMulDivBinOp for {}", .{ty.fmtDebug()});
} }
if (ty.abiSize(self.target.*) > 8) { if (ty.abiSize(self.target.*) > 8) {
return self.fail("TODO implement genBinOp for {}", .{ty.fmtDebug()}); return self.fail("TODO implement genMulDivBinOp for {}", .{ty.fmtDebug()});
} }
if (tag == .div_float) { if (tag == .div_float) {
return self.fail("TODO implement genMulDivBinOp for div_float", .{}); return self.fail("TODO implement genMulDivBinOp for div_float", .{});
@ -3516,11 +3518,31 @@ fn genBinOp(
switch (tag) { switch (tag) {
.add, .add,
.addwrap, .addwrap,
=> try self.genBinOpMir(.add, lhs_ty, dst_mcv, src_mcv), => try self.genBinOpMir(switch (lhs_ty.tag()) {
else => .add,
.f32 => .addss,
.f64 => .addsd,
}, lhs_ty, dst_mcv, src_mcv),
.sub, .sub,
.subwrap, .subwrap,
=> try self.genBinOpMir(.sub, lhs_ty, dst_mcv, src_mcv), => try self.genBinOpMir(switch (lhs_ty.tag()) {
else => .sub,
.f32 => .subss,
.f64 => .subsd,
}, lhs_ty, dst_mcv, src_mcv),
.mul => try self.genBinOpMir(switch (lhs_ty.tag()) {
.f32 => .mulss,
.f64 => .mulsd,
else => return self.fail("TODO implement genBinOp for {s} {}", .{ @tagName(tag), lhs_ty.fmt(self.bin_file.options.module.?) }),
}, lhs_ty, dst_mcv, src_mcv),
.div_float => try self.genBinOpMir(switch (lhs_ty.tag()) {
.f32 => .divss,
.f64 => .divsd,
else => return self.fail("TODO implement genBinOp for {s} {}", .{ @tagName(tag), lhs_ty.fmt(self.bin_file.options.module.?) }),
}, lhs_ty, dst_mcv, src_mcv),
.ptr_add, .ptr_add,
.ptr_sub, .ptr_sub,
@ -3547,54 +3569,66 @@ fn genBinOp(
.min, .min,
.max, .max,
=> { => switch (lhs_ty.zigTypeTag()) {
if (!lhs_ty.isAbiInt() or !rhs_ty.isAbiInt()) { .Int => {
return self.fail("TODO implement genBinOp for {s} {}", .{ @tagName(tag), lhs_ty.fmt(self.bin_file.options.module.?) }); const mat_src_mcv = switch (src_mcv) {
} .immediate => MCValue{ .register = try self.copyToTmpRegister(rhs_ty, src_mcv) },
else => src_mcv,
};
const mat_mcv_lock = switch (mat_src_mcv) {
.register => |reg| self.register_manager.lockReg(reg),
else => null,
};
defer if (mat_mcv_lock) |lock| self.register_manager.unlockReg(lock);
const mat_src_mcv = switch (src_mcv) { try self.genBinOpMir(.cmp, lhs_ty, dst_mcv, mat_src_mcv);
.immediate => MCValue{ .register = try self.copyToTmpRegister(rhs_ty, src_mcv) },
else => src_mcv,
};
const mat_mcv_lock = switch (mat_src_mcv) {
.register => |reg| self.register_manager.lockReg(reg),
else => null,
};
defer if (mat_mcv_lock) |lock| self.register_manager.unlockReg(lock);
try self.genBinOpMir(.cmp, lhs_ty, dst_mcv, mat_src_mcv); const int_info = lhs_ty.intInfo(self.target.*);
const cc: Condition = switch (int_info.signedness) {
.unsigned => switch (tag) {
.min => .a,
.max => .b,
else => unreachable,
},
.signed => switch (tag) {
.min => .g,
.max => .l,
else => unreachable,
},
};
const int_info = lhs_ty.intInfo(self.target.*); const abi_size = @intCast(u32, lhs_ty.abiSize(self.target.*));
const cc: Condition = switch (int_info.signedness) { switch (dst_mcv) {
.unsigned => switch (tag) { .register => |dst_reg| switch (mat_src_mcv) {
.min => .a, .register => |src_reg| try self.asmCmovccRegisterRegister(
.max => .b, registerAlias(dst_reg, abi_size),
registerAlias(src_reg, abi_size),
cc,
),
.stack_offset => |off| try self.asmCmovccRegisterMemory(
registerAlias(dst_reg, abi_size),
Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ .base = .rbp, .disp = -off }),
cc,
),
else => unreachable,
},
else => unreachable,
}
},
.Float => try self.genBinOpMir(switch (lhs_ty.tag()) {
.f32 => switch (tag) {
.min => .minss,
.max => .maxss,
else => unreachable, else => unreachable,
}, },
.signed => switch (tag) { .f64 => switch (tag) {
.min => .g, .min => .minsd,
.max => .l, .max => .maxsd,
else => unreachable, else => unreachable,
}, },
}; else => return self.fail("TODO implement genBinOp for {s} {}", .{ @tagName(tag), lhs_ty.fmt(self.bin_file.options.module.?) }),
}, lhs_ty, dst_mcv, src_mcv),
const abi_size = @intCast(u32, lhs_ty.abiSize(self.target.*)); else => return self.fail("TODO implement genBinOp for {s} {}", .{ @tagName(tag), lhs_ty.fmt(self.bin_file.options.module.?) }),
switch (dst_mcv) {
.register => |dst_reg| switch (mat_src_mcv) {
.register => |src_reg| try self.asmCmovccRegisterRegister(
registerAlias(dst_reg, abi_size),
registerAlias(src_reg, abi_size),
cc,
),
.stack_offset => |off| try self.asmCmovccRegisterMemory(
registerAlias(dst_reg, abi_size),
Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ .base = .rbp, .disp = -off }),
cc,
),
else => unreachable,
},
else => unreachable,
}
}, },
else => unreachable, else => unreachable,
@ -3626,29 +3660,7 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu
.register => |src_reg| switch (dst_ty.zigTypeTag()) { .register => |src_reg| switch (dst_ty.zigTypeTag()) {
.Float => { .Float => {
if (intrinsicsAllowed(self.target.*, dst_ty)) { if (intrinsicsAllowed(self.target.*, dst_ty)) {
const actual_tag: Mir.Inst.Tag = switch (dst_ty.tag()) { return self.asmRegisterRegister(mir_tag, dst_reg.to128(), src_reg.to128());
.f32 => switch (mir_tag) {
.add => .addss,
.cmp => .ucomiss,
else => return self.fail(
"TODO genBinOpMir for f32 register-register with MIR tag {}",
.{mir_tag},
),
},
.f64 => switch (mir_tag) {
.add => .addsd,
.cmp => .ucomisd,
else => return self.fail(
"TODO genBinOpMir for f64 register-register with MIR tag {}",
.{mir_tag},
),
},
else => return self.fail(
"TODO genBinOpMir for float register-register and type {}",
.{dst_ty.fmtDebug()},
),
};
return self.asmRegisterRegister(actual_tag, dst_reg.to128(), src_reg.to128());
} }
return self.fail("TODO genBinOpMir for float register-register and no intrinsics", .{}); return self.fail("TODO genBinOpMir for float register-register and no intrinsics", .{});
@ -4307,7 +4319,11 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
}; };
defer if (src_lock) |lock| self.register_manager.unlockReg(lock); defer if (src_lock) |lock| self.register_manager.unlockReg(lock);
try self.genBinOpMir(.cmp, ty, dst_mcv, src_mcv); try self.genBinOpMir(switch (ty.tag()) {
else => .cmp,
.f32 => .ucomiss,
.f64 => .ucomisd,
}, ty, dst_mcv, src_mcv);
break :result switch (signedness) { break :result switch (signedness) {
.signed => MCValue{ .eflags = Condition.fromCompareOperatorSigned(op) }, .signed => MCValue{ .eflags = Condition.fromCompareOperatorSigned(op) },

View file

@ -110,11 +110,21 @@ pub fn lowerMir(emit: *Emit) InnerError!void {
.addss, .addss,
.cmpss, .cmpss,
.divss,
.maxss,
.minss,
.movss, .movss,
.mulss,
.subss,
.ucomiss, .ucomiss,
.addsd, .addsd,
.cmpsd, .cmpsd,
.divsd,
.maxsd,
.minsd,
.movsd, .movsd,
.mulsd,
.subsd,
.ucomisd, .ucomisd,
=> try emit.mirEncodeGeneric(tag, inst), => try emit.mirEncodeGeneric(tag, inst),

View file

@ -342,12 +342,20 @@ pub const Mnemonic = enum {
// SSE // SSE
addss, addss,
cmpss, cmpss,
divss,
maxss, minss,
movss, movss,
mulss,
subss,
ucomiss, ucomiss,
// SSE2 // SSE2
addsd, addsd,
cmpsd, cmpsd,
divsd,
maxsd, minsd,
movq, movsd, movq, movsd,
mulsd,
subsd,
ucomisd, ucomisd,
// zig fmt: on // zig fmt: on
}; };

View file

@ -109,20 +109,40 @@ pub const Inst = struct {
/// Logical exclusive-or /// Logical exclusive-or
xor, xor,
/// Add single precision floating point /// Add single precision floating point values
addss, addss,
/// Compare scalar single-precision floating-point values /// Compare scalar single-precision floating-point values
cmpss, cmpss,
/// Divide scalar single-precision floating-point values
divss,
/// Return maximum single-precision floating-point value
maxss,
/// Return minimum single-precision floating-point value
minss,
/// Move scalar single-precision floating-point value /// Move scalar single-precision floating-point value
movss, movss,
/// Multiply scalar single-precision floating-point values
mulss,
/// Subtract scalar single-precision floating-point values
subss,
/// Unordered compare scalar single-precision floating-point values /// Unordered compare scalar single-precision floating-point values
ucomiss, ucomiss,
/// Add double precision floating point /// Add double precision floating point values
addsd, addsd,
/// Compare scalar double-precision floating-point values /// Compare scalar double-precision floating-point values
cmpsd, cmpsd,
/// Divide scalar double-precision floating-point values
divsd,
/// Return maximum double-precision floating-point value
maxsd,
/// Return minimum double-precision floating-point value
minsd,
/// Move scalar double-precision floating-point value /// Move scalar double-precision floating-point value
movsd, movsd,
/// Multiply scalar double-precision floating-point values
mulsd,
/// Subtract scalar double-precision floating-point values
subsd,
/// Unordered compare scalar double-precision floating-point values /// Unordered compare scalar double-precision floating-point values
ucomisd, ucomisd,

View file

@ -599,9 +599,19 @@ pub const table = &[_]Entry{
.{ .cmpss, .rmi, .xmm, .xmm_m32, .imm8, .none, 3, 0xf3, 0x0f, 0xc2, 0, .sse }, .{ .cmpss, .rmi, .xmm, .xmm_m32, .imm8, .none, 3, 0xf3, 0x0f, 0xc2, 0, .sse },
.{ .divss, .rm, .xmm, .xmm_m32, .none, .none, 3, 0xf3, 0x0f, 0x5e, 0, .sse },
.{ .maxss, .rm, .xmm, .xmm_m32, .none, .none, 3, 0xf3, 0x0f, 0x5f, 0, .sse },
.{ .minss, .rm, .xmm, .xmm_m32, .none, .none, 3, 0xf3, 0x0f, 0x5d, 0, .sse },
.{ .movss, .rm, .xmm, .xmm_m32, .none, .none, 3, 0xf3, 0x0f, 0x10, 0, .sse }, .{ .movss, .rm, .xmm, .xmm_m32, .none, .none, 3, 0xf3, 0x0f, 0x10, 0, .sse },
.{ .movss, .mr, .xmm_m32, .xmm, .none, .none, 3, 0xf3, 0x0f, 0x11, 0, .sse }, .{ .movss, .mr, .xmm_m32, .xmm, .none, .none, 3, 0xf3, 0x0f, 0x11, 0, .sse },
.{ .mulss, .rm, .xmm, .xmm_m32, .none, .none, 3, 0xf3, 0x0f, 0x59, 0, .sse },
.{ .subss, .rm, .xmm, .xmm_m32, .none, .none, 3, 0xf3, 0x0f, 0x5c, 0, .sse },
.{ .ucomiss, .rm, .xmm, .xmm_m32, .none, .none, 2, 0x0f, 0x2e, 0x00, 0, .sse }, .{ .ucomiss, .rm, .xmm, .xmm_m32, .none, .none, 2, 0x0f, 0x2e, 0x00, 0, .sse },
// SSE2 // SSE2
@ -609,9 +619,19 @@ pub const table = &[_]Entry{
.{ .cmpsd, .rmi, .xmm, .xmm_m64, .imm8, .none, 3, 0xf2, 0x0f, 0xc2, 0, .sse2 }, .{ .cmpsd, .rmi, .xmm, .xmm_m64, .imm8, .none, 3, 0xf2, 0x0f, 0xc2, 0, .sse2 },
.{ .divsd, .rm, .xmm, .xmm_m64, .none, .none, 3, 0xf2, 0x0f, 0x5e, 0, .sse2 },
.{ .maxsd, .rm, .xmm, .xmm_m32, .none, .none, 3, 0xf2, 0x0f, 0x5f, 0, .sse2 },
.{ .minsd, .rm, .xmm, .xmm_m32, .none, .none, 3, 0xf2, 0x0f, 0x5d, 0, .sse2 },
.{ .movq, .rm, .xmm, .xmm_m64, .none, .none, 3, 0xf3, 0x0f, 0x7e, 0, .sse2 }, .{ .movq, .rm, .xmm, .xmm_m64, .none, .none, 3, 0xf3, 0x0f, 0x7e, 0, .sse2 },
.{ .movq, .mr, .xmm_m64, .xmm, .none, .none, 3, 0x66, 0x0f, 0xd6, 0, .sse2 }, .{ .movq, .mr, .xmm_m64, .xmm, .none, .none, 3, 0x66, 0x0f, 0xd6, 0, .sse2 },
.{ .mulsd, .rm, .xmm, .xmm_m32, .none, .none, 3, 0xf2, 0x0f, 0x59, 0, .sse2 },
.{ .subsd, .rm, .xmm, .xmm_m32, .none, .none, 3, 0xf2, 0x0f, 0x5c, 0, .sse2 },
.{ .movsd, .rm, .xmm, .xmm_m64, .none, .none, 3, 0xf2, 0x0f, 0x10, 0, .sse2 }, .{ .movsd, .rm, .xmm, .xmm_m64, .none, .none, 3, 0xf2, 0x0f, 0x10, 0, .sse2 },
.{ .movsd, .mr, .xmm_m64, .xmm, .none, .none, 3, 0xf2, 0x0f, 0x11, 0, .sse2 }, .{ .movsd, .mr, .xmm_m64, .xmm, .none, .none, 3, 0xf2, 0x0f, 0x11, 0, .sse2 },

View file

@ -5,7 +5,6 @@ const expect = std.testing.expect;
const expectEqual = std.testing.expectEqual; const expectEqual = std.testing.expectEqual;
test "@max" { test "@max" {
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
@ -52,7 +51,6 @@ test "@max on vectors" {
} }
test "@min" { test "@min" {
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO