Commit 30e1daa746

Jacob Young <jacobly0@users.noreply.github.com>
2023-03-18 03:07:32
x86_64: implement basic float ops
1 parent 29e6aed
Changed files (6)
src/arch/x86_64/CodeGen.zig
@@ -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);
 
-    try self.spillRegisters(2, .{ .rax, .rdx });
+        if (ty.zigTypeTag() == .Float) {
+            break :result try self.genBinOp(inst, tag, bin_op.lhs, bin_op.rhs);
+        }
 
-    const lhs = try self.resolveInst(bin_op.lhs);
-    const rhs = try self.resolveInst(bin_op.rhs);
+        try self.spillRegisters(2, .{ .rax, .rdx });
 
-    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,
-                },
-                .signed => switch (tag) {
-                    .min => .g,
-                    .max => .l,
+                }
+            },
+            .Float => try self.genBinOpMir(switch (lhs_ty.tag()) {
+                .f32 => switch (tag) {
+                    .min => .minss,
+                    .max => .maxss,
                     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,
-                    ),
+                .f64 => switch (tag) {
+                    .min => .minsd,
+                    .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),
+            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) },
src/arch/x86_64/Emit.zig
@@ -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),
 
src/arch/x86_64/Encoding.zig
@@ -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
 };
src/arch/x86_64/encodings.zig
@@ -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 },
 
src/arch/x86_64/Mir.zig
@@ -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,
 
test/behavior/maximum_minimum.zig
@@ -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