Commit 1f18b53589

Jacob Young <jacobly0@users.noreply.github.com>
2023-04-24 07:37:21
x86_64: fix some floating point encoding errors
1 parent 26d4f9b
src/arch/x86_64/CodeGen.zig
@@ -2596,14 +2596,7 @@ fn genIntMulDivOpMir(
         return self.fail("TODO implement genIntMulDivOpMir for ABI size larger than 8", .{});
     }
 
-    lhs: {
-        switch (lhs) {
-            .register => |reg| if (reg.to64() == .rax) break :lhs,
-            else => {},
-        }
-        try self.genSetReg(.rax, ty, lhs);
-    }
-
+    try self.genSetReg(.rax, ty, lhs);
     switch (tag) {
         else => unreachable,
         .mul, .imul => {},
@@ -2616,7 +2609,7 @@ fn genIntMulDivOpMir(
         else => .{ .register = try self.copyToTmpRegister(ty, rhs) },
     };
     switch (mat_rhs) {
-        .register => |reg| try self.asmRegister(tag, reg),
+        .register => |reg| try self.asmRegister(tag, registerAlias(reg, abi_size)),
         .indirect, .load_frame => try self.asmMemory(
             tag,
             Memory.sib(Memory.PtrSize.fromSize(abi_size), switch (mat_rhs) {
@@ -5086,11 +5079,11 @@ fn genBinOp(
         try self.genCopy(lhs_ty, dst_mcv, lhs);
         break :dst dst_mcv;
     };
-    const dst_mcv_lock: ?RegisterLock = switch (dst_mcv) {
+    const dst_lock: ?RegisterLock = switch (dst_mcv) {
         .register => |reg| self.register_manager.lockReg(reg),
         else => null,
     };
-    defer if (dst_mcv_lock) |lock| self.register_manager.unlockReg(lock);
+    defer if (dst_lock) |lock| self.register_manager.unlockReg(lock);
 
     const src_mcv = if (flipped) lhs else rhs;
     switch (tag) {
@@ -6951,11 +6944,15 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
             .qword
         else
             null;
-        const mnem = std.meta.stringToEnum(Mir.Inst.Tag, mnem_str) orelse
-            (if (mnem_size) |_|
-            std.meta.stringToEnum(Mir.Inst.Tag, mnem_str[0 .. mnem_str.len - 1])
-        else
-            null) orelse return self.fail("Invalid mnemonic: '{s}'", .{mnem_str});
+        const mnem = mnem: {
+            if (mnem_size) |_| {
+                if (std.meta.stringToEnum(Mir.Inst.Tag, mnem_str[0 .. mnem_str.len - 1])) |mnem| {
+                    break :mnem mnem;
+                }
+            }
+            break :mnem std.meta.stringToEnum(Mir.Inst.Tag, mnem_str) orelse
+                return self.fail("Invalid mnemonic: '{s}'", .{mnem_str});
+        };
 
         var op_it = mem.tokenize(u8, mnem_it.rest(), ",");
         var ops = [1]encoder.Instruction.Operand{.none} ** 4;
@@ -7204,10 +7201,19 @@ fn genSetReg(self: *Self, dst_reg: Register, ty: Type, src_mcv: MCValue) InnerEr
                 );
             }
         },
-        .register => |reg| if (dst_reg.id() != reg.id()) try self.asmRegisterRegister(
-            try self.movMirTag(ty),
+        .register => |src_reg| if (dst_reg.id() != src_reg.id()) try self.asmRegisterRegister(
+            if ((dst_reg.class() == .floating_point) == (src_reg.class() == .floating_point))
+                try self.movMirTag(ty)
+            else switch (abi_size) {
+                4 => .movd,
+                8 => .movq,
+                else => return self.fail(
+                    "unsupported register copy from {s} to {s}",
+                    .{ @tagName(src_reg), @tagName(dst_reg) },
+                ),
+            },
             registerAlias(dst_reg, abi_size),
-            registerAlias(reg, abi_size),
+            registerAlias(src_reg, abi_size),
         ),
         .register_offset, .indirect, .load_frame, .lea_frame => try self.asmRegisterMemory(
             switch (src_mcv) {
@@ -7503,9 +7509,14 @@ fn airPtrToInt(self: *Self, inst: Air.Inst.Index) !void {
 
 fn airBitCast(self: *Self, inst: Air.Inst.Index) !void {
     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
+    const dst_ty = self.air.typeOfIndex(inst);
+    const src_ty = self.air.typeOf(ty_op.operand);
+
     const result = result: {
+        const dst_rc = try self.regClassForType(dst_ty);
+        const src_rc = try self.regClassForType(src_ty);
         const operand = try self.resolveInst(ty_op.operand);
-        if (self.reuseOperand(inst, ty_op.operand, 0, operand)) break :result operand;
+        if (dst_rc.eql(src_rc) and self.reuseOperand(inst, ty_op.operand, 0, operand)) break :result operand;
 
         const operand_lock = switch (operand) {
             .register => |reg| self.register_manager.lockReg(reg),
@@ -7518,7 +7529,6 @@ fn airBitCast(self: *Self, inst: Air.Inst.Index) !void {
         try self.genCopy(self.air.typeOfIndex(inst), dest, operand);
         break :result dest;
     };
-    log.debug("airBitCast(%{d}): {}", .{ inst, result });
     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
 }
 
src/arch/x86_64/encoder.zig
@@ -322,7 +322,10 @@ pub const Instruction = struct {
 
         var rex = Rex{};
         rex.present = inst.encoding.data.mode == .rex;
-        rex.w = inst.encoding.data.mode == .long;
+        switch (inst.encoding.data.mode) {
+            .long, .sse2_long => rex.w = true,
+            else => {},
+        }
 
         switch (op_en) {
             .np, .i, .zi, .fd, .td, .d => {},
src/arch/x86_64/Encoding.zig
@@ -58,11 +58,11 @@ pub fn findByMnemonic(
     next: for (mnemonic_to_encodings_map[@enumToInt(mnemonic)]) |data| {
         switch (data.mode) {
             .rex => if (!rex_required) continue,
-            .long => {},
+            .long, .sse2_long => {},
             else => if (rex_required) continue,
         }
         for (input_ops, data.ops) |input_op, data_op|
-            if (!input_op.isSubset(data_op, data.mode)) continue :next;
+            if (!input_op.isSubset(data_op)) continue :next;
 
         const enc = Encoding{ .mnemonic = mnemonic, .data = data };
         if (shortest_enc) |previous_shortest_enc| {
@@ -89,8 +89,8 @@ pub fn findByOpcode(opc: []const u8, prefixes: struct {
         if (!std.mem.eql(u8, opc, enc.opcode())) continue;
         if (prefixes.rex.w) {
             switch (data.mode) {
-                .short, .fpu, .sse, .sse2, .sse4_1, .none => continue,
-                .long, .rex => {},
+                .short, .fpu, .sse, .sse2, .sse2_long, .sse4_1, .none => continue,
+                .long, .sse2_long, .rex => {},
             }
         } else if (prefixes.rex.present and !prefixes.rex.isSet()) {
             switch (data.mode) {
@@ -138,7 +138,7 @@ pub fn modRmExt(encoding: Encoding) u3 {
 pub fn operandBitSize(encoding: Encoding) u64 {
     switch (encoding.data.mode) {
         .short => return 16,
-        .long => return 64,
+        .long, .sse2_long => return 64,
         else => {},
     }
     const bit_size: u64 = switch (encoding.data.op_en) {
@@ -163,7 +163,7 @@ pub fn format(
     _ = options;
     _ = fmt;
     switch (encoding.data.mode) {
-        .long => try writer.writeAll("REX.W + "),
+        .long, .sse2_long => try writer.writeAll("REX.W + "),
         else => {},
     }
 
@@ -264,6 +264,8 @@ pub const Mnemonic = enum {
     @"test", tzcnt,
     ud2,
     xadd, xchg, xor,
+    // MMX
+    movd,
     // SSE
     addss,
     cmpss,
@@ -278,7 +280,7 @@ pub const Mnemonic = enum {
     //cmpsd,
     divsd,
     maxsd, minsd,
-    movq, //movsd,
+    movq, //movd, movsd,
     mulsd,
     subsd,
     ucomisd,
@@ -461,6 +463,17 @@ pub const Op = enum {
         };
     }
 
+    pub fn class(op: Op) bits.Register.Class {
+        return switch (op) {
+            else => unreachable,
+            .al, .ax, .eax, .rax, .cl => .general_purpose,
+            .r8, .r16, .r32, .r64 => .general_purpose,
+            .rm8, .rm16, .rm32, .rm64 => .general_purpose,
+            .sreg => .segment,
+            .xmm, .xmm_m32, .xmm_m64 => .floating_point,
+        };
+    }
+
     pub fn isFloatingPointRegister(op: Op) bool {
         return switch (op) {
             .xmm, .xmm_m32, .xmm_m64 => true,
@@ -469,7 +482,7 @@ pub const Op = enum {
     }
 
     /// Given an operand `op` checks if `target` is a subset for the purposes of the encoding.
-    pub fn isSubset(op: Op, target: Op, mode: Mode) bool {
+    pub fn isSubset(op: Op, target: Op) bool {
         switch (op) {
             .m, .o16, .o32, .o64 => unreachable,
             .moffs, .sreg => return op == target,
@@ -479,13 +492,13 @@ pub const Op = enum {
             },
             else => {
                 if (op.isRegister() and target.isRegister()) {
-                    switch (mode) {
-                        .sse, .sse2, .sse4_1 => return op.isFloatingPointRegister() and target.isFloatingPointRegister(),
-                        else => switch (target) {
-                            .cl, .al, .ax, .eax, .rax => return op == target,
-                            else => return op.bitSize() == target.bitSize(),
+                    return switch (target) {
+                        .cl, .al, .ax, .eax, .rax => op == target,
+                        else => op.class() == target.class() and switch (target.class()) {
+                            .floating_point => true,
+                            else => op.bitSize() == target.bitSize(),
                         },
-                    }
+                    };
                 }
                 if (op.isMemory() and target.isMemory()) {
                     switch (target) {
@@ -523,6 +536,7 @@ pub const Mode = enum {
     long,
     sse,
     sse2,
+    sse2_long,
     sse4_1,
 };
 
src/arch/x86_64/encodings.zig
@@ -860,6 +860,12 @@ pub const table = [_]Entry{
 
     .{ .minsd, .rm, &.{ .xmm, .xmm_m64 }, &.{ 0xf2, 0x0f, 0x5d }, 0, .sse2 },
 
+    .{ .movd, .rm, &.{ .xmm,  .rm32 }, &.{ 0x66, 0x0f, 0x6e }, 0, .sse2 },
+    .{ .movd, .mr, &.{ .rm32, .xmm  }, &.{ 0x66, 0x0f, 0x7e }, 0, .sse2 },
+
+    .{ .movq, .rm, &.{ .xmm,  .rm64 }, &.{ 0x66, 0x0f, 0x6e }, 0, .sse2_long },
+    .{ .movq, .mr, &.{ .rm64, .xmm  }, &.{ 0x66, 0x0f, 0x7e }, 0, .sse2_long },
+
     .{ .movq, .rm, &.{ .xmm,     .xmm_m64 }, &.{ 0xf3, 0x0f, 0x7e }, 0, .sse2 },
     .{ .movq, .mr, &.{ .xmm_m64, .xmm     }, &.{ 0x66, 0x0f, 0xd6 }, 0, .sse2 },
 
src/arch/x86_64/Lower.zig
@@ -60,6 +60,8 @@ pub fn lowerMir(lower: *Lower, inst: Mir.Inst) Error![]const Instruction {
         .mfence,
         .mov,
         .movbe,
+        .movd,
+        .movq,
         .movzx,
         .mul,
         .neg,
src/arch/x86_64/Mir.zig
@@ -99,6 +99,10 @@ pub const Inst = struct {
         mov,
         /// Move data after swapping bytes
         movbe,
+        /// Move doubleword
+        movd,
+        /// Move quadword
+        movq,
         /// Move with sign extension
         movsx,
         /// Move with zero extension