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 {
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)) {
return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none });
}
const tag = self.air.instructions.items(.tag)[inst];
const ty = self.air.typeOfIndex(inst);
const tag = self.air.instructions.items(.tag)[inst];
const ty = self.air.typeOfIndex(inst);
if (ty.zigTypeTag() == .Float) {
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 rhs = try self.resolveInst(bin_op.rhs);
const result = try self.genMulDivBinOp(tag, inst, ty, lhs, rhs);
const lhs = try self.resolveInst(bin_op.lhs);
const rhs = try self.resolveInst(bin_op.rhs);
break :result try self.genMulDivBinOp(tag, inst, ty, lhs, rhs);
};
return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
}
@ -3288,10 +3290,10 @@ fn genMulDivBinOp(
rhs: MCValue,
) !MCValue {
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) {
return self.fail("TODO implement genBinOp for {}", .{ty.fmtDebug()});
return self.fail("TODO implement genMulDivBinOp for {}", .{ty.fmtDebug()});
}
if (tag == .div_float) {
return self.fail("TODO implement genMulDivBinOp for div_float", .{});
@ -3516,11 +3518,31 @@ fn genBinOp(
switch (tag) {
.add,
.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,
.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_sub,
@ -3547,54 +3569,66 @@ fn genBinOp(
.min,
.max,
=> {
if (!lhs_ty.isAbiInt() or !rhs_ty.isAbiInt()) {
return self.fail("TODO implement genBinOp for {s} {}", .{ @tagName(tag), lhs_ty.fmt(self.bin_file.options.module.?) });
}
=> switch (lhs_ty.zigTypeTag()) {
.Int => {
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) {
.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);
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 cc: Condition = switch (int_info.signedness) {
.unsigned => switch (tag) {
.min => .a,
.max => .b,
const abi_size = @intCast(u32, lhs_ty.abiSize(self.target.*));
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,
}
},
.Float => try self.genBinOpMir(switch (lhs_ty.tag()) {
.f32 => switch (tag) {
.min => .minss,
.max => .maxss,
else => unreachable,
},
.signed => switch (tag) {
.min => .g,
.max => .l,
.f64 => switch (tag) {
.min => .minsd,
.max => .maxsd,
else => unreachable,
},
};
const abi_size = @intCast(u32, lhs_ty.abiSize(self.target.*));
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 => return self.fail("TODO implement genBinOp for {s} {}", .{ @tagName(tag), lhs_ty.fmt(self.bin_file.options.module.?) }),
}, lhs_ty, dst_mcv, src_mcv),
else => return self.fail("TODO implement genBinOp for {s} {}", .{ @tagName(tag), lhs_ty.fmt(self.bin_file.options.module.?) }),
},
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()) {
.Float => {
if (intrinsicsAllowed(self.target.*, dst_ty)) {
const actual_tag: Mir.Inst.Tag = switch (dst_ty.tag()) {
.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.asmRegisterRegister(mir_tag, dst_reg.to128(), src_reg.to128());
}
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);
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) {
.signed => MCValue{ .eflags = Condition.fromCompareOperatorSigned(op) },

View file

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

View file

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

View file

@ -109,20 +109,40 @@ pub const Inst = struct {
/// Logical exclusive-or
xor,
/// Add single precision floating point
/// Add single precision floating point values
addss,
/// Compare scalar single-precision floating-point values
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
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
ucomiss,
/// Add double precision floating point
/// Add double precision floating point values
addsd,
/// Compare scalar double-precision floating-point values
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
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
ucomisd,

View file

@ -599,9 +599,19 @@ pub const table = &[_]Entry{
.{ .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, .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 },
// SSE2
@ -609,9 +619,19 @@ pub const table = &[_]Entry{
.{ .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, .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, .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;
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_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
@ -52,7 +51,6 @@ test "@max on vectors" {
}
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_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO